]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] go/types: import lookup logic from dev.go2go
authorRob Findley <rfindley@google.com>
Tue, 15 Dec 2020 16:37:06 +0000 (11:37 -0500)
committerRobert Findley <rfindley@google.com>
Tue, 15 Dec 2020 23:59:39 +0000 (23:59 +0000)
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>
src/go/types/lookup.go
src/go/types/methodset.go

index e7091a63e52a0ee6d16dd4c85a378b6764a674d9..f385ac993f7bd3a8467c9ee5525f03a900cd3edf 100644 (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
                }
        }
index c34d732b7ac36494427b329301397dbb7f848a2c..c44009f1a5e3a1ae2159cf54e34f9dd82297f3f6 100644 (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