Skip to content

Commit 68ad1d6

Browse files
griesemerbradfitz
authored andcommitted
[release-branch.go1.11] 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 #28249 Updates #28005 Change-Id: I96c120fb0e517d7f8a14b8530f0273674569d5ea Reviewed-on: https://go-review.googlesource.com/c/141358 Reviewed-by: Alan Donovan <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/146660 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 369c188 commit 68ad1d6

File tree

2 files changed

+97
-3
lines changed

2 files changed

+97
-3
lines changed

src/go/types/issues_test.go

+67
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

+30-3
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,15 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
571571
recvTyp = def
572572
}
573573

574+
// Correct receiver type for all methods explicitly declared
575+
// by this interface after we're done with type-checking at
576+
// this level. See comment below for details.
577+
check.later(func() {
578+
for _, m := range ityp.methods {
579+
m.typ.(*Signature).recv.typ = recvTyp
580+
}
581+
})
582+
574583
// collect methods
575584
var sigfix []*methodInfo
576585
for i, minfo := range info.methods {
@@ -580,9 +589,27 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
580589
pos := name.Pos()
581590
// Don't type-check signature yet - use an
582591
// empty signature now and update it later.
583-
// Since we know the receiver, set it up now
584-
// (required to avoid crash in ptrRecv; see
585-
// e.g. test case for issue 6638).
592+
// But set up receiver since we know it and
593+
// its position, and because interface method
594+
// signatures don't get a receiver via regular
595+
// type-checking (there isn't a receiver in the
596+
// method's AST). Setting the receiver type is
597+
// also important for ptrRecv() (see methodset.go).
598+
//
599+
// Note: For embedded methods, the receiver type
600+
// should be the type of the interface that declared
601+
// the methods in the first place. Since we get the
602+
// methods here via methodInfo, which may be computed
603+
// before we have all relevant interface types, we use
604+
// the current interface's type (recvType). This may be
605+
// the type of the interface embedding the interface that
606+
// declared the methods. This doesn't matter for type-
607+
// checking (we only care about the receiver type for
608+
// the ptrRecv predicate, and it's never a pointer recv
609+
// for interfaces), but it matters for go/types clients
610+
// and for printing. We correct the receiver after type-
611+
// checking.
612+
//
586613
// TODO(gri) Consider marking methods signatures
587614
// as incomplete, for better error messages. See
588615
// also the T4 and T5 tests in testdata/cycles2.src.

0 commit comments

Comments
 (0)