src/go/types/issues_test.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/go/types/typexpr.go | 33 ++++++++++++++++++++++++++++++--- diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go index 8560bb9b7dc8d2a0ec2df8d3700210c227975d94..f8810b6734bb89e1ab66ab8ab5c3757b9a8b150f 100644 --- a/src/go/types/issues_test.go +++ b/src/go/types/issues_test.go @@ -355,3 +355,70 @@ return true }) } } + +func TestIssue28005(t *testing.T) { + // method names must match defining interface name for this test + // (see last comment in this function) + sources := [...]string{ + "package p; type A interface{ A() }", + "package p; type B interface{ B() }", + "package p; type X interface{ A; B }", + } + + // compute original file ASTs + var orig [len(sources)]*ast.File + for i, src := range sources { + f, err := parser.ParseFile(fset, "", src, 0) + if err != nil { + t.Fatal(err) + } + orig[i] = f + } + + // run the test for all order permutations of the incoming files + for _, perm := range [][len(sources)]int{ + {0, 1, 2}, + {0, 2, 1}, + {1, 0, 2}, + {1, 2, 0}, + {2, 0, 1}, + {2, 1, 0}, + } { + // create file order permutation + files := make([]*ast.File, len(sources)) + for i := range perm { + files[i] = orig[perm[i]] + } + + // type-check package with given file order permutation + var conf Config + info := &Info{Defs: make(map[*ast.Ident]Object)} + _, err := conf.Check("", fset, files, info) + if err != nil { + t.Fatal(err) + } + + // look for interface object X + var obj Object + for name, def := range info.Defs { + if name.Name == "X" { + obj = def + break + } + } + if obj == nil { + t.Fatal("interface not found") + } + iface := obj.Type().Underlying().(*Interface) // I must be an interface + + // Each iface method m is embedded; and m's receiver base type name + // must match the method's name per the choice in the source file. + for i := 0; i < iface.NumMethods(); i++ { + m := iface.Method(i) + recvName := m.Type().(*Signature).Recv().Type().(*Named).Obj().Name() + if recvName != m.Name() { + t.Errorf("perm %v: got recv %s; want %s", perm, recvName, m.Name()) + } + } + } +} diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 45ada5874bc193adbcd3ae37c69ca7a0bfad154e..9a0318475854b4d4aa987e02ce1ab1dec85e9e5d 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -571,6 +571,15 @@ if def != nil { recvTyp = def } + // Correct receiver type for all methods explicitly declared + // by this interface after we're done with type-checking at + // this level. See comment below for details. + check.later(func() { + for _, m := range ityp.methods { + m.typ.(*Signature).recv.typ = recvTyp + } + }) + // collect methods var sigfix []*methodInfo for i, minfo := range info.methods { @@ -580,9 +589,27 @@ name := minfo.src.Names[0] pos := name.Pos() // Don't type-check signature yet - use an // empty signature now and update it later. - // Since we know the receiver, set it up now - // (required to avoid crash in ptrRecv; see - // e.g. test case for issue 6638). + // But set up receiver since we know it and + // its position, and because interface method + // signatures don't get a receiver via regular + // type-checking (there isn't a receiver in the + // method's AST). Setting the receiver type is + // also important for ptrRecv() (see methodset.go). + // + // Note: For embedded methods, the receiver type + // should be the type of the interface that declared + // the methods in the first place. Since we get the + // methods here via methodInfo, which may be computed + // before we have all relevant interface types, we use + // the current interface's type (recvType). This may be + // the type of the interface embedding the interface that + // declared the methods. This doesn't matter for type- + // checking (we only care about the receiver type for + // the ptrRecv predicate, and it's never a pointer recv + // for interfaces), but it matters for go/types clients + // and for printing. We correct the receiver after type- + // checking. + // // TODO(gri) Consider marking methods signatures // as incomplete, for better error messages. See // also the T4 and T5 tests in testdata/cycles2.src.