[dev.typeparams] go/types: re-use existing code for Interface.Complete

This is a port of CL 321751 to go/types, adjusted to use token.Pos, and
to exclude a missing position from a panic message (an unresolved
comment on the original CL).

Change-Id: I5814067aecb67aca9d73f2093fb6004b769924f3
Reviewed-on: https://go-review.googlesource.com/c/go/+/324756
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Rob Findley 2021-06-03 11:49:52 -04:00 committed by Robert Findley
parent 1395952075
commit 2f26adc232
2 changed files with 44 additions and 64 deletions

View file

@ -5,6 +5,7 @@
package types package types
import ( import (
"fmt"
"go/ast" "go/ast"
"go/internal/typeparams" "go/internal/typeparams"
"go/token" "go/token"
@ -142,8 +143,13 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
if check == nil { if check == nil {
panic("internal error: incomplete interface") panic("internal error: incomplete interface")
} }
completeInterface(check, pos, ityp)
}
if trace { func completeInterface(check *Checker, pos token.Pos, ityp *Interface) {
assert(ityp.allMethods == nil)
if check != nil && trace {
// Types don't generally have position information. // Types don't generally have position information.
// If we don't have a valid pos provided, try to use // If we don't have a valid pos provided, try to use
// one close enough. // one close enough.
@ -179,6 +185,7 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
// we can get rid of the mpos map below and simply use the cloned method's // we can get rid of the mpos map below and simply use the cloned method's
// position. // position.
var todo []*Func
var seen objset var seen objset
var methods []*Func var methods []*Func
mpos := make(map[*Func]token.Pos) // method specification or method embedding position, for good error messages mpos := make(map[*Func]token.Pos) // method specification or method embedding position, for good error messages
@ -188,6 +195,9 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
methods = append(methods, m) methods = append(methods, m)
mpos[m] = pos mpos[m] = pos
case explicit: case explicit:
if check == nil {
panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name))
}
check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name) check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
default: default:
@ -196,6 +206,11 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
// If we're pre-go1.14 (overlapping embeddings are not permitted), report that // If we're pre-go1.14 (overlapping embeddings are not permitted), report that
// error here as well (even though we could do it eagerly) because it's the same // error here as well (even though we could do it eagerly) because it's the same
// error message. // error message.
if check == nil {
// check method signatures after all locally embedded interfaces are computed
todo = append(todo, m, other.(*Func))
break
}
check.later(func() { check.later(func() {
if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) { if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) {
check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name) check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
@ -212,9 +227,15 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
// collect types // collect types
allTypes := ityp.types allTypes := ityp.types
posList := check.posMap[ityp] var posList []token.Pos
if check != nil {
posList = check.posMap[ityp]
}
for i, typ := range ityp.embeddeds { for i, typ := range ityp.embeddeds {
pos := posList[i] // embedding position var pos token.Pos // embedding position
if posList != nil {
pos = posList[i]
}
utyp := under(typ) utyp := under(typ)
etyp := asInterface(utyp) etyp := asInterface(utyp)
if etyp == nil { if etyp == nil {
@ -225,18 +246,33 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
} else { } else {
format = "%s is not an interface" format = "%s is not an interface"
} }
// TODO: correct error code. if check != nil {
check.errorf(atPos(pos), _InvalidIfaceEmbed, format, typ) // TODO: correct error code.
check.errorf(atPos(pos), _InvalidIfaceEmbed, format, typ)
} else {
panic(fmt.Sprintf(format, typ))
}
} }
continue continue
} }
check.completeInterface(pos, etyp) if etyp.allMethods == nil {
completeInterface(check, pos, etyp)
}
for _, m := range etyp.allMethods { for _, m := range etyp.allMethods {
addMethod(pos, m, false) // use embedding position pos rather than m.pos addMethod(pos, m, false) // use embedding position pos rather than m.pos
} }
allTypes = intersect(allTypes, etyp.allTypes) allTypes = intersect(allTypes, etyp.allTypes)
} }
// process todo's (this only happens if check == nil)
for i := 0; i < len(todo); i += 2 {
m := todo[i]
other := todo[i+1]
if !Identical(m.typ, other.typ) {
panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name))
}
}
if methods != nil { if methods != nil {
sort.Sort(byUniqueMethodName(methods)) sort.Sort(byUniqueMethodName(methods))
ityp.allMethods = methods ityp.allMethods = methods

View file

@ -5,7 +5,6 @@
package types package types
import ( import (
"fmt"
"go/token" "go/token"
"sync/atomic" "sync/atomic"
) )
@ -538,64 +537,9 @@ func (t *Interface) isSatisfiedBy(typ Type) bool {
// form other types. The interface must not contain duplicate methods or a // form other types. The interface must not contain duplicate methods or a
// panic occurs. Complete returns the receiver. // panic occurs. Complete returns the receiver.
func (t *Interface) Complete() *Interface { func (t *Interface) Complete() *Interface {
// TODO(gri) consolidate this method with Checker.completeInterface if t.allMethods == nil {
if t.allMethods != nil { completeInterface(nil, token.NoPos, t)
return t
} }
t.allMethods = markComplete // avoid infinite recursion
var todo []*Func
var methods []*Func
var seen objset
addMethod := func(m *Func, explicit bool) {
switch other := seen.insert(m); {
case other == nil:
methods = append(methods, m)
case explicit:
panic("duplicate method " + m.name)
default:
// check method signatures after all locally embedded interfaces are computed
todo = append(todo, m, other.(*Func))
}
}
for _, m := range t.methods {
addMethod(m, true)
}
allTypes := t.types
for _, typ := range t.embeddeds {
utyp := under(typ)
etyp := asInterface(utyp)
if etyp == nil {
if utyp != Typ[Invalid] {
panic(fmt.Sprintf("%s is not an interface", typ))
}
continue
}
etyp.Complete()
for _, m := range etyp.allMethods {
addMethod(m, false)
}
allTypes = intersect(allTypes, etyp.allTypes)
}
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 {
sortMethods(methods)
t.allMethods = methods
}
t.allTypes = allTypes
return t return t
} }