1
0
mirror of https://github.com/golang/go synced 2024-09-29 11:34:32 -06:00

go/types: postpone interface method type comparison to the end

Introduce a new list of final actions that is executed at the
end of type checking and use it to collect method type comparisons
and also map key checks.

Fixes #33656.

Change-Id: Ia77a35a45a9d7eaa7fc3e9e19f41f32dcd6ef9d9
Reviewed-on: https://go-review.googlesource.com/c/go/+/191418
Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
Robert Griesemer 2019-08-22 17:03:30 -07:00
parent 82d6e2eab8
commit 29d3c569e0
4 changed files with 51 additions and 19 deletions

View File

@ -89,7 +89,8 @@ type Checker struct {
firstErr error // first error encountered
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
untyped map[ast.Expr]exprInfo // map of expressions without final type
delayed []func() // stack of delayed actions
delayed []func() // stack of delayed action segments; segments are processed in FIFO order
finals []func() // list of final actions; processed at the end of type-checking the current set of files
objPath []Object // path of object dependencies during type inference (for cycle reporting)
// context within which the current object is type-checked
@ -145,6 +146,14 @@ func (check *Checker) later(f func()) {
check.delayed = append(check.delayed, f)
}
// atEnd adds f to the list of actions processed at the end
// of type-checking, before initialization order computation.
// Actions added by atEnd are processed after any actions
// added by later.
func (check *Checker) atEnd(f func()) {
check.finals = append(check.finals, f)
}
// push pushes obj onto the object path and returns its index in the path.
func (check *Checker) push(obj Object) int {
check.objPath = append(check.objPath, obj)
@ -195,6 +204,7 @@ func (check *Checker) initFiles(files []*ast.File) {
check.methods = nil
check.untyped = nil
check.delayed = nil
check.finals = nil
// determine package name and collect valid files
pkg := check.pkg
@ -245,6 +255,7 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
check.packageObjects()
check.processDelayed(0) // incl. all functions
check.processFinals()
check.initOrder()
@ -258,6 +269,16 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
return
}
func (check *Checker) processFinals() {
n := len(check.finals)
for _, f := range check.finals {
f() // must not append to check.finals
}
if len(check.finals) != n {
panic("internal error: final action list grew")
}
}
func (check *Checker) recordUntyped() {
if !debug && check.Types == nil {
return // nothing to do

View File

@ -45,13 +45,11 @@ type B interface {
type AB interface {
a() interface {
A
// TODO(gri) there shouldn't be an error here. See issue #33656.
B // ERROR duplicate method a
B
}
b() interface {
A
// TODO(gri) there shouldn't be an error here. See issue #33656.
B // ERROR duplicate method a
B
}
}

View File

@ -351,16 +351,18 @@ func (t *Interface) Complete() *Interface {
t.allMethods = markComplete // avoid infinite recursion
var todo []*Func
var methods []*Func
var seen objset
addMethod := func(m *Func, explicit bool) {
switch alt := seen.insert(m); {
case alt == nil:
switch other := seen.insert(m); {
case other == nil:
methods = append(methods, m)
case explicit || !Identical(m.Type(), alt.Type()):
case explicit:
panic("duplicate method " + m.name)
default:
// silently drop method m
// check method signatures after all locally embedded interfaces are computed
todo = append(todo, m, other.(*Func))
}
}
@ -376,6 +378,14 @@ func (t *Interface) Complete() *Interface {
}
}
for i := 0; i < len(todo); i += 2 {
m := todo[i]
other := todo[i+1]
if !Identical(m.typ, other.typ) {
panic("duplicate method " + m.name)
}
}
if methods != nil {
sort.Sort(byUniqueMethodName(methods))
t.allMethods = methods

View File

@ -314,7 +314,7 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
//
// Delay this check because it requires fully setup types;
// it is safe to continue in any case (was issue 6667).
check.later(func() {
check.atEnd(func() {
if !Comparable(typ.key) {
check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key)
}
@ -560,17 +560,20 @@ func (check *Checker) completeInterface(ityp *Interface) {
var methods []*Func
var seen objset
addMethod := func(m *Func, explicit bool) {
switch alt := seen.insert(m); {
case alt == nil:
switch other := seen.insert(m); {
case other == nil:
methods = append(methods, m)
case explicit || !Identical(m.Type(), alt.Type()):
case explicit:
check.errorf(m.pos, "duplicate method %s", m.name)
// We use "other" rather than "previous" here because
// the first declaration seen may not be textually
// earlier in the source.
check.errorf(alt.Pos(), "\tother declaration of %s", m) // secondary error, \t indented
check.reportAltDecl(other)
default:
// silently drop method m
// check method signatures after all types are computed (issue #33656)
check.atEnd(func() {
if !Identical(m.typ, other.Type()) {
check.errorf(m.pos, "duplicate method %s", m.name)
check.reportAltDecl(other)
}
})
}
}
@ -581,7 +584,7 @@ func (check *Checker) completeInterface(ityp *Interface) {
posList := check.posMap[ityp]
for i, typ := range ityp.embeddeds {
pos := posList[i] // embedding position
typ := typ.Underlying().(*Interface)
typ := underlying(typ).(*Interface)
check.completeInterface(typ)
for _, m := range typ.allMethods {
copy := *m