Skip to content

Commit a80c5f0

Browse files
committed
go/types: allow embedding overlapping interfaces
Quietly drop duplicate methods from embedded interfaces if they have an identical signature to existing methods. Instead of adjusting the prior syntax-based only method set computation where methods don't have signature information (and thus where de-duplication according to the new rules would have been somewhat tricky to get right), this change completely rewrites interface method set computation, taking a page from the cmd/compiler's implementation. In a first pass, when type-checking interfaces, explicit methods and embedded interfaces are collected, but the interfaces are not "expanded", that is the final method set computation is done lazily, either when needed for method lookup, or at the end of type-checking. While this is a substantial rewrite, it allows us to get rid of the separate (duplicate and delicate) syntactical method set computation and generally simplifies checking of interface types significantly. A few (esoteric) test cases now have slightly different error messages but all tests that are accepted by cmd/compile are also accepted by go/types. (This is a replacement for golang.org/cl/190258.) Updates #6977. Change-Id: Ic8b9321374ab4f617498d97c12871b69d1119735 Reviewed-on: https://go-review.googlesource.com/c/go/+/191257 Reviewed-by: Matthew Dempsky <[email protected]>
1 parent 89f02eb commit a80c5f0

15 files changed

+287
-682
lines changed

src/go/types/builtins.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
559559

560560
base := derefStructPtr(x.typ)
561561
sel := selx.Sel.Name
562-
obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)
562+
obj, index, indirect := check.LookupFieldOrMethod(base, false, check.pkg, sel)
563563
switch obj.(type) {
564564
case nil:
565565
check.invalidArg(x.pos(), "%s has no single field %s", base, sel)

src/go/types/call.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
370370
goto Error
371371
}
372372

373-
obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
373+
obj, index, indirect = check.LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
374374
if obj == nil {
375375
switch {
376376
case index != nil:
@@ -437,6 +437,10 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
437437

438438
if debug {
439439
// Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
440+
// TODO(gri) This only works because we call LookupFieldOrMethod
441+
// _before_ calling NewMethodSet: LookupFieldOrMethod completes
442+
// any incomplete interfaces so they are avaible to NewMethodSet
443+
// (which assumes that interfaces have been completed already).
440444
typ := x.typ
441445
if x.mode == variable {
442446
// If typ is not an (unnamed) pointer or an interface,

src/go/types/check.go

+8-17
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ type Checker struct {
7676
fset *token.FileSet
7777
pkg *Package
7878
*Info
79-
objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
80-
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
79+
objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
80+
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
81+
posMap map[*Interface][]token.Pos // maps interface types to lists of embedded interface positions
8182

8283
// information collected during type-checking of a set of package files
8384
// (initialized by Files, valid only for the duration of check.Files;
@@ -86,12 +87,10 @@ type Checker struct {
8687
unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope
8788

8889
firstErr error // first error encountered
89-
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank, non-interface methods
90-
// TODO(gri) move interfaces up to the group of fields persistent across check.Files invocations (see also comment in Checker.initFiles)
91-
interfaces map[*TypeName]*ifaceInfo // maps interface type names to corresponding interface infos
92-
untyped map[ast.Expr]exprInfo // map of expressions without final type
93-
delayed []func() // stack of delayed actions
94-
objPath []Object // path of object dependencies during type inference (for cycle reporting)
90+
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
91+
untyped map[ast.Expr]exprInfo // map of expressions without final type
92+
delayed []func() // stack of delayed actions
93+
objPath []Object // path of object dependencies during type inference (for cycle reporting)
9594

9695
// context within which the current object is type-checked
9796
// (valid only for the duration of type-checking a specific object)
@@ -181,6 +180,7 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
181180
Info: info,
182181
objMap: make(map[Object]*declInfo),
183182
impMap: make(map[importKey]*Package),
183+
posMap: make(map[*Interface][]token.Pos),
184184
}
185185
}
186186

@@ -193,15 +193,6 @@ func (check *Checker) initFiles(files []*ast.File) {
193193

194194
check.firstErr = nil
195195
check.methods = nil
196-
// Don't clear the interfaces cache! It's important that we don't recompute
197-
// ifaceInfos repeatedly (due to multiple check.Files calls) because when
198-
// they are recomputed, they are not used in the context of their original
199-
// declaration (because those types are already type-checked, typically) and
200-
// then they will get the wrong receiver types, which matters for go/types
201-
// clients. It is also safe to not reset the interfaces cache because files
202-
// added to a package cannot change (add methods to) existing interface types;
203-
// they can only add new interfaces. See also the respective comment in
204-
// checker.infoFromTypeName (interfaces.go). Was bug - see issue #29029.
205196
check.untyped = nil
206197
check.delayed = nil
207198

src/go/types/check_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ var tests = [][]string{
9797
{"testdata/issue23203a.src"},
9898
{"testdata/issue23203b.src"},
9999
{"testdata/issue28251.src"},
100+
{"testdata/issue6977.src"},
100101
}
101102

102103
var fset = token.NewFileSet()

src/go/types/errors.go

+4
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ func (check *Checker) err(pos token.Pos, msg string, soft bool) {
8282
check.firstErr = err
8383
}
8484

85+
if trace {
86+
check.trace(pos, "ERROR: %s", msg)
87+
}
88+
8589
f := check.conf.Error
8690
if f == nil {
8791
panic(bailout{}) // report only first error

0 commit comments

Comments
 (0)