mirror of
https://github.com/golang/go
synced 2024-11-14 15:00:27 -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:
parent
1306435103
commit
a4d4c10340
@ -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
|
// 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
|
// pointer type but discard the result if it is a method since we would
|
||||||
// not have found it for T (see also issue 8590).
|
// 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 {
|
if p, _ := t.underlying.(*Pointer); p != nil {
|
||||||
obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name)
|
obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name)
|
||||||
if _, ok := obj.(*Func); ok {
|
if _, ok := obj.(*Func); ok {
|
||||||
@ -85,7 +85,8 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
|
|||||||
typ, isPtr := deref(T)
|
typ, isPtr := deref(T)
|
||||||
|
|
||||||
// *typ where typ is an interface has no methods.
|
// *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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,12 +107,13 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
|
|||||||
var next []embeddedType // embedded types found at current depth
|
var next []embeddedType // embedded types found at current depth
|
||||||
|
|
||||||
// look for (pkg, name) in all types 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 {
|
for _, e := range current {
|
||||||
typ := e.typ
|
typ := e.typ
|
||||||
|
|
||||||
// If we have a named type, we may have associated methods.
|
// If we have a named type, we may have associated methods.
|
||||||
// Look for those first.
|
// Look for those first.
|
||||||
if named, _ := typ.(*Named); named != nil {
|
if named := asNamed(typ); named != nil {
|
||||||
if seen[named] {
|
if seen[named] {
|
||||||
// We have seen this type before, at a more shallow depth
|
// We have seen this type before, at a more shallow depth
|
||||||
// (note that multiples of this type at the current 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 // we can't have a matching field or interface method
|
||||||
}
|
}
|
||||||
|
|
||||||
// continue with underlying type
|
// continue with underlying type, but only if it's not a type parameter
|
||||||
typ = named.underlying
|
// 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) {
|
switch t := typ.(type) {
|
||||||
case *Struct:
|
case *Struct:
|
||||||
// look for a matching field and collect embedded types
|
// 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
|
obj = m
|
||||||
indirect = e.indirect
|
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
|
// 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()
|
// list of m. If x is addressable and &x's method set contains m, x.m()
|
||||||
// is shorthand for (&x).m()".
|
// is shorthand for (&x).m()".
|
||||||
if f, _ := obj.(*Func); f != nil && ptrRecv(f) && !indirect && !addressable {
|
if f, _ := obj.(*Func); f != nil {
|
||||||
return nil, nil, true // pointer/addressable receiver required
|
// 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
|
return
|
||||||
}
|
}
|
||||||
@ -269,7 +294,8 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b
|
|||||||
return m, typ != nil
|
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
|
// The receiver may be nil if missingMethod is invoked through
|
||||||
// an exported API call (such as MissingMethod), i.e., when all
|
// an exported API call (such as MissingMethod), i.e., when all
|
||||||
// methods have been type-checked.
|
// methods have been type-checked.
|
||||||
@ -285,25 +311,37 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ityp, _ := V.Underlying().(*Interface); ityp != nil {
|
if ityp := asInterface(V); ityp != nil {
|
||||||
check.completeInterface(token.NoPos, ityp)
|
check.completeInterface(token.NoPos, ityp)
|
||||||
// TODO(gri) allMethods is sorted - can do this more efficiently
|
// TODO(gri) allMethods is sorted - can do this more efficiently
|
||||||
for _, m := range T.allMethods {
|
for _, m := range T.allMethods {
|
||||||
_, obj := lookupMethod(ityp.allMethods, m.pkg, m.name)
|
_, f := lookupMethod(ityp.allMethods, m.pkg, m.name)
|
||||||
switch {
|
|
||||||
case obj == nil:
|
if f == nil {
|
||||||
if static {
|
// if m is the magic method == we're ok (interfaces are comparable)
|
||||||
return m, nil
|
if m.name == "==" || !static {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
case !check.identical(obj.Type(), m.typ):
|
return m, f
|
||||||
return m, obj
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// A concrete type implements T if it implements all methods of T.
|
// A concrete type implements T if it implements all methods of T.
|
||||||
for _, m := range T.allMethods {
|
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)
|
obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name)
|
||||||
|
|
||||||
// Check if *V implements this method of T.
|
// 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)
|
// we must have a method (not a field of matching function type)
|
||||||
f, _ := obj.(*Func)
|
f, _ := obj.(*Func)
|
||||||
if f == nil {
|
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
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,9 +368,16 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
|
|||||||
check.objDecl(f, nil)
|
check.objDecl(f, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !check.identical(f.typ, m.typ) {
|
if !check.identical(f.Type(), m.Type()) {
|
||||||
return m, f
|
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
|
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.
|
// 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
|
// 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.
|
// (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) {
|
func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Func) {
|
||||||
// no static check is required if T is an interface
|
// no static check is required if T is an interface
|
||||||
// spec: "If T is an interface type, x.(T) asserts that the
|
// spec: "If T is an interface type, x.(T) asserts that the
|
||||||
// dynamic type of x implements the interface T."
|
// dynamic type of x implements the interface T."
|
||||||
if _, ok := T.Underlying().(*Interface); ok && !forceStrict {
|
if asInterface(T) != nil && !forceStrict {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return check.missingMethod(T, V, false)
|
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
|
// 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.
|
// (named or unnamed) struct and returns its base. Otherwise it returns typ.
|
||||||
func derefStructPtr(typ Type) Type {
|
func derefStructPtr(typ Type) Type {
|
||||||
if p, _ := typ.Underlying().(*Pointer); p != nil {
|
if p := asPointer(typ); p != nil {
|
||||||
if _, ok := p.base.Underlying().(*Struct); ok {
|
if asStruct(p.base) != nil {
|
||||||
return p.base
|
return p.base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,9 @@ func NewMethodSet(T Type) *MethodSet {
|
|||||||
// WARNING: The code in this function is extremely subtle - do not modify casually!
|
// WARNING: The code in this function is extremely subtle - do not modify casually!
|
||||||
// This function and lookupFieldOrMethod should be kept in sync.
|
// 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
|
// method set up to the current depth, allocated lazily
|
||||||
var base methodSet
|
var base methodSet
|
||||||
|
|
||||||
@ -108,7 +111,7 @@ func NewMethodSet(T Type) *MethodSet {
|
|||||||
|
|
||||||
// If we have a named type, we may have associated methods.
|
// If we have a named type, we may have associated methods.
|
||||||
// Look for those first.
|
// Look for those first.
|
||||||
if named, _ := typ.(*Named); named != nil {
|
if named := asNamed(typ); named != nil {
|
||||||
if seen[named] {
|
if seen[named] {
|
||||||
// We have seen this type before, at a more shallow depth
|
// We have seen this type before, at a more shallow depth
|
||||||
// (note that multiples of this type at the current depth
|
// (note that multiples of this type at the current depth
|
||||||
|
Loading…
Reference in New Issue
Block a user