diff options
Diffstat (limited to 'src/cmd/compile/internal/types2/lookup.go')
-rw-r--r-- | src/cmd/compile/internal/types2/lookup.go | 115 |
1 files changed, 59 insertions, 56 deletions
diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index 78299502e9..0363008ad9 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -6,6 +6,11 @@ package types2 +// Internal use of LookupFieldOrMethod: If the obj result is a method +// associated with a concrete (non-interface) type, the method's signature +// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing +// the method's type. + // LookupFieldOrMethod looks up a field or method with given package and name // in T and returns the corresponding *Var or *Func, an index sequence, and a // bool indicating if there were any pointer indirections on the path to the @@ -33,19 +38,6 @@ package types2 // the method's formal receiver base type, nor was the receiver addressable. // func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { - return (*Checker)(nil).lookupFieldOrMethod(T, addressable, pkg, name) -} - -// Internal use of Checker.lookupFieldOrMethod: If the obj result is a method -// associated with a concrete (non-interface) type, the method's signature -// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing -// the method's type. -// TODO(gri) Now that we provide the *Checker, we can probably remove this -// caveat by calling Checker.objDecl from lookupFieldOrMethod. Investigate. - -// lookupFieldOrMethod is like the external version but completes interfaces -// as necessary. -func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { // Methods cannot be associated to a named pointer type // (spec: "The type denoted by T is called the receiver base type; // it must not be a pointer or interface type and it must be declared @@ -54,8 +46,8 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package // 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 := asNamed(T); t != nil { - if p, _ := t.underlying.(*Pointer); p != nil { - obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name) + if p, _ := t.Underlying().(*Pointer); p != nil { + obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name) if _, ok := obj.(*Func); ok { return nil, nil, false } @@ -63,7 +55,7 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package } } - return check.rawLookupFieldOrMethod(T, addressable, pkg, name) + return lookupFieldOrMethod(T, addressable, pkg, name) } // TODO(gri) The named type consolidation and seen maps below must be @@ -71,10 +63,9 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package // types always have only one representation (even when imported // indirectly via different packages.) -// rawLookupFieldOrMethod should only be called by lookupFieldOrMethod and missingMethod. -func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { +// lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod. +func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { // WARNING: The code in this function is extremely subtle - do not modify casually! - // This function and NewMethodSet should be kept in sync. if name == "_" { return // blank fields/methods are never found @@ -82,10 +73,12 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack typ, isPtr := deref(T) - // *typ where typ is an interface has no methods. - // Be cautious: typ may be nil (issue 39634, crash #3). - if typ == nil || isPtr && IsInterface(typ) { - return + // *typ where typ is an interface or type parameter has no methods. + switch under(typ).(type) { + case *Interface, *TypeParam: + if isPtr { + return + } } // Start with typ as single entry at shallowest depth. @@ -126,6 +119,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack seen[named] = true // look for a matching attached method + named.load() if i, m := lookupMethod(named.methods, pkg, name); m != nil { // potential match // caution: method may not have a proper signature yet @@ -181,9 +175,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack case *Interface: // look for a matching method - // TODO(gri) t.allMethods is sorted - use binary search - check.completeInterface(nopos, t) - if i, m := lookupMethod(t.allMethods, pkg, name); m != nil { + if i, m := t.typeSet().LookupMethod(pkg, name); m != nil { assert(m.typ != nil) index = concat(e.index, i) if obj != nil || e.multiples { @@ -194,7 +186,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack } case *TypeParam: - if i, m := lookupMethod(t.Bound().allMethods, pkg, name); m != nil { + if i, m := t.iface().typeSet().LookupMethod(pkg, name); m != nil { assert(m.typ != nil) index = concat(e.index, i) if obj != nil || e.multiples { @@ -229,7 +221,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack return } - current = check.consolidateMultiples(next) + current = consolidateMultiples(next) } return nil, nil, false // not found @@ -246,7 +238,7 @@ type embeddedType struct { // consolidateMultiples collects multiple list entries with the same type // into a single entry marked as containing multiples. The result is the // consolidated list. -func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType { +func consolidateMultiples(list []embeddedType) []embeddedType { if len(list) <= 1 { return list // at most one entry - nothing to do } @@ -254,7 +246,7 @@ func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType { n := 0 // number of entries w/ unique type prev := make(map[Type]int) // index at which type was previously seen for _, e := range list { - if i, found := check.lookupType(prev, e.typ); found { + if i, found := lookupType(prev, e.typ); found { list[i].multiples = true // ignore this entry } else { @@ -266,14 +258,14 @@ func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType { return list[:n] } -func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) { +func lookupType(m map[Type]int, typ Type) (int, bool) { // fast path: maybe the types are equal if i, found := m[typ]; found { return i, true } for t, i := range m { - if check.identical(t, typ) { + if Identical(t, typ) { return i, true } } @@ -306,22 +298,18 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b // To improve error messages, also report the wrong signature // when the method exists on *V instead of V. func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) { - check.completeInterface(nopos, T) - // fast path for common case if T.Empty() { return } if ityp := asInterface(V); ityp != nil { - check.completeInterface(nopos, ityp) - // TODO(gri) allMethods is sorted - can do this more efficiently - for _, m := range T.allMethods { - _, f := lookupMethod(ityp.allMethods, m.pkg, m.name) + // TODO(gri) the methods are sorted - could do this more efficiently + for _, m := range T.typeSet().methods { + _, f := ityp.typeSet().LookupMethod(m.pkg, m.name) if f == nil { - // if m is the magic method == we're ok (interfaces are comparable) - if m.name == "==" || !static { + if !static { continue } return m, f @@ -330,17 +318,20 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // both methods must have the same number of type parameters ftyp := f.typ.(*Signature) mtyp := m.typ.(*Signature) - if len(ftyp.tparams) != len(mtyp.tparams) { + if ftyp.TParams().Len() != mtyp.TParams().Len() { return m, f } + if !acceptMethodTypeParams && ftyp.TParams().Len() > 0 { + panic("method with type parameters") + } // If the methods have type parameters we don't care whether they // are the same or not, as long as they match up. Use unification // to see if they can be made to match. // TODO(gri) is this always correct? what about type bounds? // (Alternative is to rename/subst type parameters and compare.) - u := newUnifier(check, true) - u.x.init(ftyp.tparams) + u := newUnifier(true) + u.x.init(ftyp.TParams().list()) if !u.unify(ftyp, mtyp) { return m, f } @@ -352,14 +343,14 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // A concrete type implements T if it implements all methods of T. Vd, _ := deref(V) Vn := asNamed(Vd) - for _, m := range T.allMethods { + for _, m := range T.typeSet().methods { // TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)? - obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name) + obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name) // Check if *V implements this method of T. if obj == nil { ptr := NewPointer(V) - obj, _, _ = check.rawLookupFieldOrMethod(ptr, false, m.pkg, m.name) + obj, _, _ = lookupFieldOrMethod(ptr, false, m.pkg, m.name) if obj != nil { return m, obj.(*Func) } @@ -368,10 +359,6 @@ 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 } @@ -383,9 +370,12 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // both methods must have the same number of type parameters ftyp := f.typ.(*Signature) mtyp := m.typ.(*Signature) - if len(ftyp.tparams) != len(mtyp.tparams) { + if ftyp.TParams().Len() != mtyp.TParams().Len() { return m, f } + if !acceptMethodTypeParams && ftyp.TParams().Len() > 0 { + panic("method with type parameters") + } // If V is a (instantiated) generic type, its methods are still // parameterized using the original (declaration) receiver type @@ -394,17 +384,17 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // In order to compare the signatures, substitute the receiver // type parameters of ftyp with V's instantiation type arguments. // This lazily instantiates the signature of method f. - if Vn != nil && len(Vn.tparams) > 0 { + if Vn != nil && Vn.TParams().Len() > 0 { // Be careful: The number of type arguments may not match // the number of receiver parameters. If so, an error was // reported earlier but the length discrepancy is still // here. Exit early in this case to prevent an assertion // failure in makeSubstMap. // TODO(gri) Can we avoid this check by fixing the lengths? - if len(ftyp.rparams) != len(Vn.targs) { + if len(ftyp.RParams().list()) != len(Vn.targs) { return } - ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.rparams, Vn.targs)).(*Signature) + ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.RParams().list(), Vn.targs)).(*Signature) } // If the methods have type parameters we don't care whether they @@ -412,8 +402,21 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // to see if they can be made to match. // TODO(gri) is this always correct? what about type bounds? // (Alternative is to rename/subst type parameters and compare.) - u := newUnifier(check, true) - u.x.init(ftyp.tparams) + u := newUnifier(true) + if ftyp.TParams().Len() > 0 { + // We reach here only if we accept method type parameters. + // In this case, unification must consider any receiver + // and method type parameters as "free" type parameters. + assert(acceptMethodTypeParams) + // We don't have a test case for this at the moment since + // we can't parse method type parameters. Keeping the + // unimplemented call so that we test this code if we + // enable method type parameters. + unimplemented() + u.x.init(append(ftyp.RParams().list(), ftyp.TParams().list()...)) + } else { + u.x.init(ftyp.RParams().list()) + } if !u.unify(ftyp, mtyp) { return m, f } |