Skip to content

Commit 99843e2

Browse files
committed
go/types: type-check embedded methods in correct scope (regression)
Change https://go-review.googlesource.com/79575 fixed the computation of recursive method sets by separating the method set computation from type computation. However, it didn't track an embedded method's scope and as a result, some methods' signatures were typed in the wrong context. This change tracks embedded methods together with their scope and uses that scope for the correct context setup when typing those method signatures. Fixes #23914. Change-Id: If3677dceddb43e9db2f9fb3c7a4a87d2531fbc2a Reviewed-on: https://go-review.googlesource.com/96376 Reviewed-by: Alan Donovan <[email protected]>
1 parent 85caeaf commit 99843e2

File tree

4 files changed

+36
-13
lines changed

4 files changed

+36
-13
lines changed

src/go/types/interfaces.go

+12-9
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,14 @@ func (info *ifaceInfo) String() string {
5353

5454
// methodInfo represents an interface method.
5555
// At least one of src or fun must be non-nil.
56-
// (Methods declared in the current package have a non-nil src,
57-
// and eventually a non-nil fun field; imported and predeclared
58-
// methods have a nil src, and only a non-nil fun field.)
56+
// (Methods declared in the current package have a non-nil scope
57+
// and src, and eventually a non-nil fun field; imported and pre-
58+
// declared methods have a nil scope and src, and only a non-nil
59+
// fun field.)
5960
type methodInfo struct {
60-
src *ast.Field // syntax tree representation of interface method; or nil
61-
fun *Func // corresponding fully type-checked method type; or nil
61+
scope *Scope // scope of interface method; or nil
62+
src *ast.Field // syntax tree representation of interface method; or nil
63+
fun *Func // corresponding fully type-checked method type; or nil
6264
}
6365

6466
func (info *methodInfo) String() string {
@@ -124,15 +126,16 @@ func (check *Checker) reportAltMethod(m *methodInfo) {
124126
}
125127
}
126128

127-
// infoFromTypeLit computes the method set for the given interface iface.
129+
// infoFromTypeLit computes the method set for the given interface iface
130+
// declared in scope.
128131
// If a corresponding type name exists (tname != nil), it is used for
129132
// cycle detection and to cache the method set.
130133
// The result is the method set, or nil if there is a cycle via embedded
131134
// interfaces. A non-nil result doesn't mean that there were no errors,
132135
// but they were either reported (e.g., blank methods), or will be found
133136
// (again) when computing the interface's type.
134137
// If tname is not nil it must be the last element in path.
135-
func (check *Checker) infoFromTypeLit(iface *ast.InterfaceType, tname *TypeName, path []*TypeName) (info *ifaceInfo) {
138+
func (check *Checker) infoFromTypeLit(scope *Scope, iface *ast.InterfaceType, tname *TypeName, path []*TypeName) (info *ifaceInfo) {
136139
assert(iface != nil)
137140

138141
// lazy-allocate interfaces map
@@ -207,7 +210,7 @@ func (check *Checker) infoFromTypeLit(iface *ast.InterfaceType, tname *TypeName,
207210
continue // ignore
208211
}
209212

210-
m := &methodInfo{src: f}
213+
m := &methodInfo{scope: scope, src: f}
211214
if check.declareInMethodSet(&mset, f.Pos(), m) {
212215
info.methods = append(info.methods, m)
213216
}
@@ -333,7 +336,7 @@ typenameLoop:
333336
return check.infoFromQualifiedTypeName(typ)
334337
case *ast.InterfaceType:
335338
// type tname interface{...}
336-
return check.infoFromTypeLit(typ, tname, path)
339+
return check.infoFromTypeLit(decl.file, typ, tname, path)
337340
}
338341
// type tname X // and X is not an interface type
339342
return nil

src/go/types/testdata/importdecl1a.src

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@
66

77
package importdecl1
88

9+
import "go/ast"
910
import . "unsafe"
1011

1112
var _ Pointer // use dot-imported package unsafe
13+
14+
// Test cases for issue 23914.
15+
16+
type A interface {
17+
// Methods m1, m2 must be type-checked in this file scope
18+
// even when embedded in an interface in a different
19+
// file of the same package.
20+
m1() ast.Node
21+
m2() Pointer
22+
}

src/go/types/testdata/importdecl1b.src

+4
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@
55
package importdecl1
66

77
import . /* ERROR "imported but not used" */ "unsafe"
8+
9+
type B interface {
10+
A
11+
}

src/go/types/typexpr.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -481,9 +481,9 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
481481
// collect embedded interfaces
482482
// Only needed for printing and API. Delay collection
483483
// to end of type-checking when all types are complete.
484-
interfaceScope := check.scope // capture for use in closure below
484+
interfaceContext := check.context // capture for use in closure below
485485
check.later(func() {
486-
check.scope = interfaceScope
486+
check.context = interfaceContext
487487
if trace {
488488
check.trace(iface.Pos(), "-- delayed checking embedded interfaces of %s", iface)
489489
check.indent++
@@ -495,7 +495,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
495495
if len(f.Names) == 0 {
496496
typ := check.typ(f.Type)
497497
// typ should be a named type denoting an interface
498-
// (the parser will make sure it's a name type but
498+
// (the parser will make sure it's a named type but
499499
// constructed ASTs may be wrong).
500500
if typ == Typ[Invalid] {
501501
continue // error reported before
@@ -531,7 +531,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
531531
if def != nil {
532532
tname = def.obj
533533
}
534-
info := check.infoFromTypeLit(iface, tname, path)
534+
info := check.infoFromTypeLit(check.scope, iface, tname, path)
535535
if info == nil || info == &emptyIfaceInfo {
536536
// error or empty interface - exit early
537537
ityp.allMethods = markComplete
@@ -574,7 +574,11 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
574574
}
575575

576576
// fix signatures now that we have collected all methods
577+
savedContext := check.context
577578
for _, minfo := range sigfix {
579+
// (possibly embedded) methods must be type-checked within their scope and
580+
// type-checking them must not affect the current context (was issue #23914)
581+
check.context = context{scope: minfo.scope}
578582
typ := check.typ(minfo.src.Type)
579583
sig, _ := typ.(*Signature)
580584
if sig == nil {
@@ -588,6 +592,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
588592
sig.recv = old.recv
589593
*old = *sig // update signature (don't replace pointer!)
590594
}
595+
check.context = savedContext
591596

592597
// sort to match NewInterface
593598
// TODO(gri) we may be able to switch to source order

0 commit comments

Comments
 (0)