diff --git a/compiler/compiler.go b/compiler/compiler.go index 8707e1bc93..7826c00e1f 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -92,6 +92,7 @@ type compilerContext struct { pkg *types.Package packageDir string // directory for this package runtimePkg *types.Package + scopeIdx map[*types.Scope]int } // newCompilerContext returns a new compiler context ready for use, most @@ -106,6 +107,7 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C targetData: machine.CreateTargetData(), functionInfos: map[*ssa.Function]functionInfo{}, astComments: map[string]*ast.CommentGroup{}, + scopeIdx: map[*types.Scope]int{}, } c.ctx = llvm.NewContext() diff --git a/compiler/interface.go b/compiler/interface.go index e63666834f..f2d48ebd72 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -158,7 +158,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { } } - typeCodeName, isLocal := getTypeCodeName(typ) + typeCodeName, isLocal := c.getTypeCodeName(typ) globalName := "reflect/types.type:" + typeCodeName var global llvm.Value if isLocal { @@ -511,23 +511,57 @@ var basicTypeNames = [...]string{ types.UnsafePointer: "unsafe.Pointer", } +// return an integer representing this scope in a package. +func (c *compilerContext) getScopeID(pkg *types.Scope, scope *types.Scope) string { + var ids []int + + for scope != pkg { + if idx, ok := c.scopeIdx[scope]; ok { + ids = append(ids, idx) + } else { + // flesh out scope idx for all children of this parent + parent := scope.Parent() + for i := range parent.NumChildren() { + child := parent.Child(i) + if child == scope { + c.scopeIdx[scope] = i + break + } + } + ids = append(ids, c.scopeIdx[scope]) + } + // move up a level + scope = scope.Parent() + } + + var buf []byte + for _, v := range ids { + buf = strconv.AppendInt(buf, int64(v), 10) + buf = append(buf, ':') + } + + id := string(buf) + return id +} + // getTypeCodeName returns a name for this type that can be used in the // interface lowering pass to assign type codes as expected by the reflect // package. See getTypeCodeNum. -func getTypeCodeName(t types.Type) (string, bool) { +func (c *compilerContext) getTypeCodeName(t types.Type) (string, bool) { switch t := types.Unalias(t).(type) { case *types.Named: - if t.Obj().Parent() != t.Obj().Pkg().Scope() { - return "named:" + t.String() + "$local", true + parent, pkg := t.Obj().Parent(), t.Obj().Pkg().Scope() + if parent != pkg { + return fmt.Sprintf("named:%s$local:%s", t.String(), c.getScopeID(pkg, parent)), true } return "named:" + t.String(), false case *types.Array: - s, isLocal := getTypeCodeName(t.Elem()) + s, isLocal := c.getTypeCodeName(t.Elem()) return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + s, isLocal case *types.Basic: return "basic:" + basicTypeNames[t.Kind()], false case *types.Chan: - s, isLocal := getTypeCodeName(t.Elem()) + s, isLocal := c.getTypeCodeName(t.Elem()) var dir string switch t.Dir() { case types.SendOnly: @@ -547,7 +581,7 @@ func getTypeCodeName(t types.Type) (string, bool) { if !token.IsExported(name) { name = t.Method(i).Pkg().Path() + "." + name } - s, local := getTypeCodeName(t.Method(i).Type()) + s, local := c.getTypeCodeName(t.Method(i).Type()) if local { isLocal = true } @@ -555,17 +589,17 @@ func getTypeCodeName(t types.Type) (string, bool) { } return "interface:" + "{" + strings.Join(methods, ",") + "}", isLocal case *types.Map: - keyType, keyLocal := getTypeCodeName(t.Key()) - elemType, elemLocal := getTypeCodeName(t.Elem()) + keyType, keyLocal := c.getTypeCodeName(t.Key()) + elemType, elemLocal := c.getTypeCodeName(t.Elem()) return "map:" + "{" + keyType + "," + elemType + "}", keyLocal || elemLocal case *types.Pointer: - s, isLocal := getTypeCodeName(t.Elem()) + s, isLocal := c.getTypeCodeName(t.Elem()) return "pointer:" + s, isLocal case *types.Signature: isLocal := false params := make([]string, t.Params().Len()) for i := 0; i < t.Params().Len(); i++ { - s, local := getTypeCodeName(t.Params().At(i).Type()) + s, local := c.getTypeCodeName(t.Params().At(i).Type()) if local { isLocal = true } @@ -573,7 +607,7 @@ func getTypeCodeName(t types.Type) (string, bool) { } results := make([]string, t.Results().Len()) for i := 0; i < t.Results().Len(); i++ { - s, local := getTypeCodeName(t.Results().At(i).Type()) + s, local := c.getTypeCodeName(t.Results().At(i).Type()) if local { isLocal = true } @@ -581,7 +615,7 @@ func getTypeCodeName(t types.Type) (string, bool) { } return "func:" + "{" + strings.Join(params, ",") + "}{" + strings.Join(results, ",") + "}", isLocal case *types.Slice: - s, isLocal := getTypeCodeName(t.Elem()) + s, isLocal := c.getTypeCodeName(t.Elem()) return "slice:" + s, isLocal case *types.Struct: elems := make([]string, t.NumFields()) @@ -591,7 +625,7 @@ func getTypeCodeName(t types.Type) (string, bool) { if t.Field(i).Embedded() { embedded = "#" } - s, local := getTypeCodeName(t.Field(i).Type()) + s, local := c.getTypeCodeName(t.Field(i).Type()) if local { isLocal = true } @@ -709,7 +743,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value { commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "") } } else { - name, _ := getTypeCodeName(expr.AssertedType) + name, _ := b.getTypeCodeName(expr.AssertedType) globalName := "reflect/types.typeid:" + name assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName) if assertedTypeCodeGlobal.IsNil() { @@ -786,7 +820,7 @@ func (c *compilerContext) getMethodsString(itf *types.Interface) string { // getInterfaceImplementsFunc returns a declared function that works as a type // switch. The interface lowering pass will define this function. func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) llvm.Value { - s, _ := getTypeCodeName(assertedType.Underlying()) + s, _ := c.getTypeCodeName(assertedType.Underlying()) fnName := s + ".$typeassert" llvmFn := c.mod.NamedFunction(fnName) if llvmFn.IsNil() { @@ -803,7 +837,7 @@ func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) ll // thunk is declared, not defined: it will be defined by the interface lowering // pass. func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value { - s, _ := getTypeCodeName(instr.Value.Type().Underlying()) + s, _ := c.getTypeCodeName(instr.Value.Type().Underlying()) fnName := s + "." + instr.Method.Name() + "$invoke" llvmFn := c.mod.NamedFunction(fnName) if llvmFn.IsNil() { diff --git a/compiler/testdata/interface.go b/compiler/testdata/interface.go index 2833ff7717..a39f94fada 100644 --- a/compiler/testdata/interface.go +++ b/compiler/testdata/interface.go @@ -61,3 +61,37 @@ func callFooMethod(itf fooInterface) uint8 { func callErrorMethod(itf error) string { return itf.Error() } + +func namedFoo() { + type Foo struct { + A int + } + f1 := &Foo{} + fcopy := copyOf(f1) + f2 := fcopy.(*Foo) + println(f2.A) +} + +func namedFoo2Nested() { + type Foo struct { + A *int + } + f1 := &Foo{} + fcopy := copyOf(f1) + f2 := fcopy.(*Foo) + println(f2.A == nil) + + if f2.A == nil { + type Foo struct { + A *byte + } + nestedf1 := &Foo{} + fcopy := copyOf(nestedf1) + nestedf2 := fcopy.(*Foo) + println(nestedf2.A == nil) + } +} + +func copyOf(src interface{}) (dst interface{}) { + return src +} diff --git a/compiler/testdata/interface.ll b/compiler/testdata/interface.ll index fcde0158d4..b5fff47482 100644 --- a/compiler/testdata/interface.ll +++ b/compiler/testdata/interface.ll @@ -3,6 +3,7 @@ source_filename = "interface.go" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20" target triple = "wasm32-unknown-wasi" +%runtime.structField = type { ptr, ptr } %runtime._interface = type { ptr, ptr } %runtime._string = type { ptr, i32 } @@ -16,6 +17,27 @@ target triple = "wasm32-unknown-wasi" @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4 @"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4 @"reflect/types.typeid:basic:int" = external constant i8 +@"reflect/types.type:pointer:named:main.Foo$local:11:0:" = internal constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:main.Foo$local:11:0:" }, align 4 +@"reflect/types.type:named:main.Foo$local:11:0:" = internal constant { i8, i16, ptr, ptr, ptr, [9 x i8] } { i8 -6, i16 0, ptr @"reflect/types.type:pointer:named:main.Foo$local:11:0:", ptr @"reflect/types.type:struct:{A:basic:int}", ptr @"reflect/types.type.pkgpath:main", [9 x i8] c"main.Foo\00" }, align 4 +@"reflect/types.type.pkgpath:main" = linkonce_odr unnamed_addr constant [5 x i8] c"main\00", align 1 +@"reflect/types.type:struct:{A:basic:int}" = linkonce_odr constant { i8, i16, ptr, ptr, i32, i16, [1 x %runtime.structField] } { i8 -38, i16 0, ptr @"reflect/types.type:pointer:struct:{A:basic:int}", ptr @"reflect/types.type.pkgpath:main", i32 4, i16 1, [1 x %runtime.structField] [%runtime.structField { ptr @"reflect/types.type:basic:int", ptr @"reflect/types.type:struct:{A:basic:int}.A" }] }, align 4 +@"reflect/types.type:pointer:struct:{A:basic:int}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:struct:{A:basic:int}" }, align 4 +@"reflect/types.type:struct:{A:basic:int}.A" = internal unnamed_addr constant [4 x i8] c"\04\00A\00", align 1 +@"reflect/types.typeid:pointer:named:main.Foo$local:11:0:" = external constant i8 +@"reflect/types.type:pointer:named:main.Foo$local:12:0:" = internal constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:main.Foo$local:12:0:" }, align 4 +@"reflect/types.type:named:main.Foo$local:12:0:" = internal constant { i8, i16, ptr, ptr, ptr, [9 x i8] } { i8 -6, i16 0, ptr @"reflect/types.type:pointer:named:main.Foo$local:12:0:", ptr @"reflect/types.type:struct:{A:pointer:basic:int}", ptr @"reflect/types.type.pkgpath:main", [9 x i8] c"main.Foo\00" }, align 4 +@"reflect/types.type:struct:{A:pointer:basic:int}" = linkonce_odr constant { i8, i16, ptr, ptr, i32, i16, [1 x %runtime.structField] } { i8 -38, i16 0, ptr @"reflect/types.type:pointer:struct:{A:pointer:basic:int}", ptr @"reflect/types.type.pkgpath:main", i32 4, i16 1, [1 x %runtime.structField] [%runtime.structField { ptr @"reflect/types.type:pointer:basic:int", ptr @"reflect/types.type:struct:{A:pointer:basic:int}.A" }] }, align 4 +@"reflect/types.type:pointer:struct:{A:pointer:basic:int}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:struct:{A:pointer:basic:int}" }, align 4 +@"reflect/types.type:struct:{A:pointer:basic:int}.A" = internal unnamed_addr constant [4 x i8] c"\04\00A\00", align 1 +@"reflect/types.typeid:pointer:named:main.Foo$local:12:0:" = external constant i8 +@"reflect/types.type:pointer:named:main.Foo$local:0:0:12:0:" = internal constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:main.Foo$local:0:0:12:0:" }, align 4 +@"reflect/types.type:named:main.Foo$local:0:0:12:0:" = internal constant { i8, i16, ptr, ptr, ptr, [9 x i8] } { i8 -6, i16 0, ptr @"reflect/types.type:pointer:named:main.Foo$local:0:0:12:0:", ptr @"reflect/types.type:struct:{A:pointer:basic:uint8}", ptr @"reflect/types.type.pkgpath:main", [9 x i8] c"main.Foo\00" }, align 4 +@"reflect/types.type:struct:{A:pointer:basic:uint8}" = linkonce_odr constant { i8, i16, ptr, ptr, i32, i16, [1 x %runtime.structField] } { i8 -38, i16 0, ptr @"reflect/types.type:pointer:struct:{A:pointer:basic:uint8}", ptr @"reflect/types.type.pkgpath:main", i32 4, i16 1, [1 x %runtime.structField] [%runtime.structField { ptr @"reflect/types.type:pointer:basic:uint8", ptr @"reflect/types.type:struct:{A:pointer:basic:uint8}.A" }] }, align 4 +@"reflect/types.type:pointer:struct:{A:pointer:basic:uint8}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:struct:{A:pointer:basic:uint8}" }, align 4 +@"reflect/types.type:struct:{A:pointer:basic:uint8}.A" = internal unnamed_addr constant [4 x i8] c"\04\00A\00", align 1 +@"reflect/types.type:pointer:basic:uint8" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:uint8" }, align 4 +@"reflect/types.type:basic:uint8" = linkonce_odr constant { i8, ptr } { i8 -56, ptr @"reflect/types.type:pointer:basic:uint8" }, align 4 +@"reflect/types.typeid:pointer:named:main.Foo$local:0:0:12:0:" = external constant i8 ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 @@ -130,6 +152,184 @@ entry: declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6 +; Function Attrs: nounwind +define hidden void @main.namedFoo(ptr %context) unnamed_addr #2 { +entry: + %stackalloc = alloca i8, align 1 + %complit = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:main.Foo$local:11:0:", ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #7 + %0 = call %runtime._interface @main.copyOf(ptr nonnull @"reflect/types.type:pointer:named:main.Foo$local:11:0:", ptr nonnull %complit, ptr undef) + %1 = extractvalue %runtime._interface %0, 0 + call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7 + %2 = extractvalue %runtime._interface %0, 1 + call void @runtime.trackPointer(ptr %2, ptr nonnull %stackalloc, ptr undef) #7 + %interface.type = extractvalue %runtime._interface %0, 0 + %typecode = call i1 @runtime.typeAssert(ptr %interface.type, ptr nonnull @"reflect/types.typeid:pointer:named:main.Foo$local:11:0:", ptr undef) #7 + br i1 %typecode, label %typeassert.ok, label %typeassert.next + +typeassert.next: ; preds = %typeassert.ok, %entry + %typeassert.value = phi ptr [ null, %entry ], [ %typeassert.value.ptr, %typeassert.ok ] + call void @runtime.interfaceTypeAssert(i1 %typecode, ptr undef) #7 + %3 = icmp eq ptr %typeassert.value, null + br i1 %3, label %gep.throw, label %gep.next + +gep.next: ; preds = %typeassert.next + br i1 false, label %deref.throw, label %deref.next + +deref.next: ; preds = %gep.next + %4 = load i32, ptr %typeassert.value, align 4 + call void @runtime.printlock(ptr undef) #7 + call void @runtime.printint32(i32 %4, ptr undef) #7 + call void @runtime.printnl(ptr undef) #7 + call void @runtime.printunlock(ptr undef) #7 + ret void + +typeassert.ok: ; preds = %entry + %typeassert.value.ptr = extractvalue %runtime._interface %0, 1 + br label %typeassert.next + +gep.throw: ; preds = %typeassert.next + call void @runtime.nilPanic(ptr undef) #7 + unreachable + +deref.throw: ; preds = %gep.next + unreachable +} + +; Function Attrs: nounwind +define hidden %runtime._interface @main.copyOf(ptr %src.typecode, ptr %src.value, ptr %context) unnamed_addr #2 { +entry: + %0 = insertvalue %runtime._interface zeroinitializer, ptr %src.typecode, 0 + %1 = insertvalue %runtime._interface %0, ptr %src.value, 1 + ret %runtime._interface %1 +} + +declare void @runtime.interfaceTypeAssert(i1, ptr) #1 + +declare void @runtime.nilPanic(ptr) #1 + +declare void @runtime.printlock(ptr) #1 + +declare void @runtime.printint32(i32, ptr) #1 + +declare void @runtime.printnl(ptr) #1 + +declare void @runtime.printunlock(ptr) #1 + +; Function Attrs: nounwind +define hidden void @main.namedFoo2Nested(ptr %context) unnamed_addr #2 { +entry: + %stackalloc = alloca i8, align 1 + %complit = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:main.Foo$local:12:0:", ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #7 + %0 = call %runtime._interface @main.copyOf(ptr nonnull @"reflect/types.type:pointer:named:main.Foo$local:12:0:", ptr nonnull %complit, ptr undef) + %1 = extractvalue %runtime._interface %0, 0 + call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7 + %2 = extractvalue %runtime._interface %0, 1 + call void @runtime.trackPointer(ptr %2, ptr nonnull %stackalloc, ptr undef) #7 + %interface.type = extractvalue %runtime._interface %0, 0 + %typecode = call i1 @runtime.typeAssert(ptr %interface.type, ptr nonnull @"reflect/types.typeid:pointer:named:main.Foo$local:12:0:", ptr undef) #7 + br i1 %typecode, label %typeassert.ok, label %typeassert.next + +typeassert.next: ; preds = %typeassert.ok, %entry + %typeassert.value = phi ptr [ null, %entry ], [ %typeassert.value.ptr, %typeassert.ok ] + call void @runtime.interfaceTypeAssert(i1 %typecode, ptr undef) #7 + %3 = icmp eq ptr %typeassert.value, null + br i1 %3, label %gep.throw, label %gep.next + +gep.next: ; preds = %typeassert.next + br i1 false, label %deref.throw, label %deref.next + +deref.next: ; preds = %gep.next + %4 = load ptr, ptr %typeassert.value, align 4 + call void @runtime.trackPointer(ptr %4, ptr nonnull %stackalloc, ptr undef) #7 + %5 = icmp eq ptr %4, null + call void @runtime.printlock(ptr undef) #7 + call void @runtime.printbool(i1 %5, ptr undef) #7 + call void @runtime.printnl(ptr undef) #7 + call void @runtime.printunlock(ptr undef) #7 + br i1 false, label %gep.throw1, label %gep.next2 + +gep.next2: ; preds = %deref.next + br i1 false, label %deref.throw3, label %deref.next4 + +deref.next4: ; preds = %gep.next2 + %6 = load ptr, ptr %typeassert.value, align 4 + call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #7 + %7 = icmp eq ptr %6, null + br i1 %7, label %if.then, label %if.done + +typeassert.ok: ; preds = %entry + %typeassert.value.ptr = extractvalue %runtime._interface %0, 1 + br label %typeassert.next + +if.then: ; preds = %deref.next4 + %complit5 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull %complit5, ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:main.Foo$local:0:0:12:0:", ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull %complit5, ptr nonnull %stackalloc, ptr undef) #7 + %8 = call %runtime._interface @main.copyOf(ptr nonnull @"reflect/types.type:pointer:named:main.Foo$local:0:0:12:0:", ptr nonnull %complit5, ptr undef) + %9 = extractvalue %runtime._interface %8, 0 + call void @runtime.trackPointer(ptr %9, ptr nonnull %stackalloc, ptr undef) #7 + %10 = extractvalue %runtime._interface %8, 1 + call void @runtime.trackPointer(ptr %10, ptr nonnull %stackalloc, ptr undef) #7 + %interface.type6 = extractvalue %runtime._interface %8, 0 + %typecode7 = call i1 @runtime.typeAssert(ptr %interface.type6, ptr nonnull @"reflect/types.typeid:pointer:named:main.Foo$local:0:0:12:0:", ptr undef) #7 + br i1 %typecode7, label %typeassert.ok8, label %typeassert.next9 + +typeassert.next9: ; preds = %typeassert.ok8, %if.then + %typeassert.value11 = phi ptr [ null, %if.then ], [ %typeassert.value.ptr10, %typeassert.ok8 ] + call void @runtime.interfaceTypeAssert(i1 %typecode7, ptr undef) #7 + %11 = icmp eq ptr %typeassert.value11, null + br i1 %11, label %gep.throw12, label %gep.next13 + +gep.next13: ; preds = %typeassert.next9 + br i1 false, label %deref.throw14, label %deref.next15 + +deref.next15: ; preds = %gep.next13 + %12 = load ptr, ptr %typeassert.value11, align 4 + call void @runtime.trackPointer(ptr %12, ptr nonnull %stackalloc, ptr undef) #7 + %13 = icmp eq ptr %12, null + call void @runtime.printlock(ptr undef) #7 + call void @runtime.printbool(i1 %13, ptr undef) #7 + call void @runtime.printnl(ptr undef) #7 + call void @runtime.printunlock(ptr undef) #7 + br label %if.done + +typeassert.ok8: ; preds = %if.then + %typeassert.value.ptr10 = extractvalue %runtime._interface %8, 1 + br label %typeassert.next9 + +if.done: ; preds = %deref.next15, %deref.next4 + ret void + +gep.throw: ; preds = %typeassert.next + call void @runtime.nilPanic(ptr undef) #7 + unreachable + +deref.throw: ; preds = %gep.next + unreachable + +gep.throw1: ; preds = %deref.next + unreachable + +deref.throw3: ; preds = %gep.next2 + unreachable + +gep.throw12: ; preds = %typeassert.next9 + call void @runtime.nilPanic(ptr undef) #7 + unreachable + +deref.throw14: ; preds = %gep.next13 + unreachable +} + +declare void @runtime.printbool(i1, ptr) #1 + attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } diff --git a/testdata/interface.go b/testdata/interface.go index 1a3a838288..674ca04f2c 100644 --- a/testdata/interface.go +++ b/testdata/interface.go @@ -119,6 +119,10 @@ func main() { // check that type asserts to interfaces with no methods work emptyintfcrash() + + // These are part of a test that checks that `main.Foo` can refer to 2+ different entities without getting them confused. + namedFoo() + namedFoo2Nested() } func printItf(val interface{}) { @@ -343,3 +347,37 @@ func emptyintfcrash() { println("x is", x.(int)) } } + +func namedFoo() { + type Foo struct { + A int + } + f1 := &Foo{} + fcopy := copyOf(f1) + f2 := fcopy.(*Foo) + println(f2.A) +} + +func namedFoo2Nested() { + type Foo struct { + A *int + } + f1 := &Foo{} + fcopy := copyOf(f1) + f2 := fcopy.(*Foo) + println(f2.A == nil) + + if f2.A == nil { + type Foo struct { + A *byte + } + nestedf1 := &Foo{} + fcopy := copyOf(nestedf1) + nestedf2 := fcopy.(*Foo) + println(nestedf2.A == nil) + } +} + +func copyOf(src interface{}) (dst interface{}) { + return src +} diff --git a/testdata/interface.txt b/testdata/interface.txt index 55f7ae1def..ced833d1fe 100644 --- a/testdata/interface.txt +++ b/testdata/interface.txt @@ -28,3 +28,6 @@ type is int type is *int type is **int x is 5 +0 +true +true