Skip to content

Commit 76f5784

Browse files
committed
go/types: use correct receiver types for embedded interface methods
Interface methods don't declare a receiver (it's implicit), but after type-checking the respective *types.Func objects are marked as methods by having a receiver. For interface methods, the receiver base type used to be the interface that declared the method in the first place, even if the method also appeared in other interfaces via embedding. A change in the computation of method sets for interfaces for Go1.10 changed that inadvertently, with the consequence that sometimes a method's receiver type ended up being an interface into which the method was embedded. The exact behavior also depended on file type-checking order, and because files are sometimes sorted by name, the behavior depended on file names. This didn't matter for type-checking (the typechecker doesn't need the receiver), but it matters for clients, and for printing of methods. This change fixes interface method receivers at the end of type-checking when we have all relevant information. Fixes #28005. Change-Id: I96c120fb0e517d7f8a14b8530f0273674569d5ea Reviewed-on: https://go-review.googlesource.com/c/141358 Reviewed-by: Alan Donovan <[email protected]>
1 parent 872a547 commit 76f5784

File tree

2 files changed

+92
-2
lines changed

2 files changed

+92
-2
lines changed

src/go/types/issues_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,70 @@ func TestIssue25627(t *testing.T) {
355355
})
356356
}
357357
}
358+
359+
func TestIssue28005(t *testing.T) {
360+
// method names must match defining interface name for this test
361+
// (see last comment in this function)
362+
sources := [...]string{
363+
"package p; type A interface{ A() }",
364+
"package p; type B interface{ B() }",
365+
"package p; type X interface{ A; B }",
366+
}
367+
368+
// compute original file ASTs
369+
var orig [len(sources)]*ast.File
370+
for i, src := range sources {
371+
f, err := parser.ParseFile(fset, "", src, 0)
372+
if err != nil {
373+
t.Fatal(err)
374+
}
375+
orig[i] = f
376+
}
377+
378+
// run the test for all order permutations of the incoming files
379+
for _, perm := range [][len(sources)]int{
380+
{0, 1, 2},
381+
{0, 2, 1},
382+
{1, 0, 2},
383+
{1, 2, 0},
384+
{2, 0, 1},
385+
{2, 1, 0},
386+
} {
387+
// create file order permutation
388+
files := make([]*ast.File, len(sources))
389+
for i := range perm {
390+
files[i] = orig[perm[i]]
391+
}
392+
393+
// type-check package with given file order permutation
394+
var conf Config
395+
info := &Info{Defs: make(map[*ast.Ident]Object)}
396+
_, err := conf.Check("", fset, files, info)
397+
if err != nil {
398+
t.Fatal(err)
399+
}
400+
401+
// look for interface object X
402+
var obj Object
403+
for name, def := range info.Defs {
404+
if name.Name == "X" {
405+
obj = def
406+
break
407+
}
408+
}
409+
if obj == nil {
410+
t.Fatal("interface not found")
411+
}
412+
iface := obj.Type().Underlying().(*Interface) // I must be an interface
413+
414+
// Each iface method m is embedded; and m's receiver base type name
415+
// must match the method's name per the choice in the source file.
416+
for i := 0; i < iface.NumMethods(); i++ {
417+
m := iface.Method(i)
418+
recvName := m.Type().(*Signature).Recv().Type().(*Named).Obj().Name()
419+
if recvName != m.Name() {
420+
t.Errorf("perm %v: got recv %s; want %s", perm, recvName, m.Name())
421+
}
422+
}
423+
}
424+
}

src/go/types/typexpr.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,15 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
549549
recvTyp = def
550550
}
551551

552+
// Correct receiver type for all methods explicitly declared
553+
// by this interface after we're done with type-checking at
554+
// this level. See comment below for details.
555+
check.later(func() {
556+
for _, m := range ityp.methods {
557+
m.typ.(*Signature).recv.typ = recvTyp
558+
}
559+
})
560+
552561
// collect methods
553562
var sigfix []*methodInfo
554563
for i, minfo := range info.methods {
@@ -562,8 +571,22 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
562571
// its position, and because interface method
563572
// signatures don't get a receiver via regular
564573
// type-checking (there isn't a receiver in the
565-
// method's AST). Setting the correct receiver
566-
// type is also important for ptrRecv() (see methodset.go).
574+
// method's AST). Setting the receiver type is
575+
// also important for ptrRecv() (see methodset.go).
576+
//
577+
// Note: For embedded methods, the receiver type
578+
// should be the type of the interface that declared
579+
// the methods in the first place. Since we get the
580+
// methods here via methodInfo, which may be computed
581+
// before we have all relevant interface types, we use
582+
// the current interface's type (recvType). This may be
583+
// the type of the interface embedding the interface that
584+
// declared the methods. This doesn't matter for type-
585+
// checking (we only care about the receiver type for
586+
// the ptrRecv predicate, and it's never a pointer recv
587+
// for interfaces), but it matters for go/types clients
588+
// and for printing. We correct the receiver after type-
589+
// checking.
567590
//
568591
// TODO(gri) Consider marking methods signatures
569592
// as incomplete, for better error messages. See

0 commit comments

Comments
 (0)