1
0
mirror of https://github.com/golang/go synced 2024-11-14 09:00:21 -07:00

[dev.typeparams] go/types: import lookup logic from dev.go2go

Changes from dev.go2go:
 + Remove support for pointer designation.
 + Remove support for method type parameters in missingMethod. We could
   leave this logic in, but it looked sufficiently shaky that I'd rather
   not bring in the additional complexity.
 + Remove the strictness flag parameter to assertableTo, since it isn't
   used.

Change-Id: I812b8d1c49f3b714b166f061fbb7f2e683a0ce86
Reviewed-on: https://go-review.googlesource.com/c/go/+/278333
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Rob Findley 2020-12-15 11:37:06 -05:00 committed by Robert Findley
parent 1306435103
commit a4d4c10340
2 changed files with 75 additions and 21 deletions

View File

@ -55,7 +55,7 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package
// Thus, if we have a named pointer type, proceed with the underlying
// pointer type but discard the result if it is a method since we would
// not have found it for T (see also issue 8590).
if t, _ := T.(*Named); t != nil {
if t := asNamed(T); t != nil {
if p, _ := t.underlying.(*Pointer); p != nil {
obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name)
if _, ok := obj.(*Func); ok {
@ -85,7 +85,8 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
typ, isPtr := deref(T)
// *typ where typ is an interface has no methods.
if isPtr && IsInterface(typ) {
// Be cautious: typ may be nil (issue 39634, crash #3).
if typ == nil || isPtr && IsInterface(typ) {
return
}
@ -106,12 +107,13 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
var next []embeddedType // embedded types found at current depth
// look for (pkg, name) in all types at current depth
var tpar *TypeParam // set if obj receiver is a type parameter
for _, e := range current {
typ := e.typ
// If we have a named type, we may have associated methods.
// Look for those first.
if named, _ := typ.(*Named); named != nil {
if named := asNamed(typ); named != nil {
if seen[named] {
// We have seen this type before, at a more shallow depth
// (note that multiples of this type at the current depth
@ -138,10 +140,15 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
continue // we can't have a matching field or interface method
}
// continue with underlying type
typ = named.underlying
// continue with underlying type, but only if it's not a type parameter
// TODO(gri) is this what we want to do for type parameters? (spec question)
typ = named.under()
if asTypeParam(typ) != nil {
continue
}
}
tpar = nil
switch t := typ.(type) {
case *Struct:
// look for a matching field and collect embedded types
@ -187,6 +194,20 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
obj = m
indirect = e.indirect
}
case *TypeParam:
// only consider explicit methods in the type parameter bound, not
// methods that may be common to all types in the type list.
if i, m := lookupMethod(t.Bound().allMethods, pkg, name); m != nil {
assert(m.typ != nil)
index = concat(e.index, i)
if obj != nil || e.multiples {
return nil, index, false // collision
}
tpar = t
obj = m
indirect = e.indirect
}
}
}
@ -196,8 +217,12 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
// contains m and the argument list can be assigned to the parameter
// list of m. If x is addressable and &x's method set contains m, x.m()
// is shorthand for (&x).m()".
if f, _ := obj.(*Func); f != nil && ptrRecv(f) && !indirect && !addressable {
return nil, nil, true // pointer/addressable receiver required
if f, _ := obj.(*Func); f != nil {
// determine if method has a pointer receiver
hasPtrRecv := tpar == nil && ptrRecv(f)
if hasPtrRecv && !indirect && !addressable {
return nil, nil, true // pointer/addressable receiver required
}
}
return
}
@ -269,7 +294,8 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b
return m, typ != nil
}
// missingMethod is like MissingMethod but accepts a receiver.
// missingMethod is like MissingMethod but accepts a *Checker as
// receiver and an addressable flag.
// The receiver may be nil if missingMethod is invoked through
// an exported API call (such as MissingMethod), i.e., when all
// methods have been type-checked.
@ -285,25 +311,37 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
return
}
if ityp, _ := V.Underlying().(*Interface); ityp != nil {
if ityp := asInterface(V); ityp != nil {
check.completeInterface(token.NoPos, ityp)
// TODO(gri) allMethods is sorted - can do this more efficiently
for _, m := range T.allMethods {
_, obj := lookupMethod(ityp.allMethods, m.pkg, m.name)
switch {
case obj == nil:
if static {
return m, nil
_, f := lookupMethod(ityp.allMethods, m.pkg, m.name)
if f == nil {
// if m is the magic method == we're ok (interfaces are comparable)
if m.name == "==" || !static {
continue
}
case !check.identical(obj.Type(), m.typ):
return m, obj
return m, f
}
if !check.identical(f.Type(), m.Type()) {
return m, f
}
// TODO(rFindley) delete this note once the spec has stabilized to
// exclude method type parameters.
// NOTE: if enabling method type parameters, we need to unify f.Type()
// and m.Type() here to verify that their type parameters align (assuming
// this behaves correctly with respect to type bounds).
}
return
}
// A concrete type implements T if it implements all methods of T.
for _, m := range T.allMethods {
// TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)?
obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name)
// Check if *V implements this method of T.
@ -318,6 +356,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// we must have a method (not a field of matching function type)
f, _ := obj.(*Func)
if f == nil {
// if m is the magic method == and V is comparable, we're ok
if m.name == "==" && Comparable(V) {
continue
}
return m, nil
}
@ -326,9 +368,16 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
check.objDecl(f, nil)
}
if !check.identical(f.typ, m.typ) {
if !check.identical(f.Type(), m.Type()) {
return m, f
}
// TODO(rFindley) delete this note once the spec has stabilized to exclude
// method type parameters.
// NOTE: if enabling method type parameters, one needs to subst any
// receiver type parameters for V here, and unify f.Type() with m.Type() to
// verify that their type parameters align (assuming this behaves correctly
// with respect to type bounds).
}
return
@ -339,11 +388,13 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// method required by V and whether it is missing or just has the wrong type.
// The receiver may be nil if assertableTo is invoked through an exported API call
// (such as AssertableTo), i.e., when all methods have been type-checked.
// If the global constant forceStrict is set, assertions that are known to fail
// are not permitted.
func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Func) {
// no static check is required if T is an interface
// spec: "If T is an interface type, x.(T) asserts that the
// dynamic type of x implements the interface T."
if _, ok := T.Underlying().(*Interface); ok && !forceStrict {
if asInterface(T) != nil && !forceStrict {
return
}
return check.missingMethod(T, V, false)
@ -361,8 +412,8 @@ func deref(typ Type) (Type, bool) {
// derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a
// (named or unnamed) struct and returns its base. Otherwise it returns typ.
func derefStructPtr(typ Type) Type {
if p, _ := typ.Underlying().(*Pointer); p != nil {
if _, ok := p.base.Underlying().(*Struct); ok {
if p := asPointer(typ); p != nil {
if asStruct(p.base) != nil {
return p.base
}
}

View File

@ -73,6 +73,9 @@ func NewMethodSet(T Type) *MethodSet {
// WARNING: The code in this function is extremely subtle - do not modify casually!
// This function and lookupFieldOrMethod should be kept in sync.
// TODO(gri) This code is out-of-sync with the lookup code at this point.
// Need to update.
// method set up to the current depth, allocated lazily
var base methodSet
@ -108,7 +111,7 @@ func NewMethodSet(T Type) *MethodSet {
// If we have a named type, we may have associated methods.
// Look for those first.
if named, _ := typ.(*Named); named != nil {
if named := asNamed(typ); named != nil {
if seen[named] {
// We have seen this type before, at a more shallow depth
// (note that multiples of this type at the current depth