diff options
Diffstat (limited to 'src/go/types/named.go')
-rw-r--r-- | src/go/types/named.go | 189 |
1 files changed, 115 insertions, 74 deletions
diff --git a/src/go/types/named.go b/src/go/types/named.go index 51c4a236da7..302e43174e6 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -17,13 +17,13 @@ type Named struct { orig *Named // original, uninstantiated type fromRHS Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting) underlying Type // possibly a *Named during setup; never a *Named once set up completely - instPos *token.Pos // position information for lazy instantiation, or nil tparams *TypeParamList // type parameters, or nil targs *TypeList // type arguments (after instantiation), or nil methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily - resolve func(*Named) ([]*TypeParam, Type, []*Func) - once sync.Once + // resolver may be provided to lazily resolve type parameters, underlying, and methods. + resolver func(*Environment, *Named) (tparams *TypeParamList, underlying Type, methods []*Func) + once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing } // NewNamed returns a new named type for the given type name, underlying type, and associated methods. @@ -36,43 +36,22 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods) } -func (t *Named) load() *Named { - // If t is an instantiated type, it derives its methods and tparams from its - // base type. Since we expect type parameters and methods to be set after a - // call to load, we must load the base and copy here. - // - // underlying is set when t is expanded. - // - // By convention, a type instance is loaded iff its tparams are set. - if t.targs.Len() > 0 && t.tparams == nil { - t.orig.load() - t.tparams = t.orig.tparams - t.methods = t.orig.methods - } - if t.resolve == nil { +func (t *Named) resolve(env *Environment) *Named { + if t.resolver == nil { return t } t.once.Do(func() { - // TODO(mdempsky): Since we're passing t to resolve anyway + // TODO(mdempsky): Since we're passing t to the resolver anyway // (necessary because types2 expects the receiver type for methods // on defined interface types to be the Named rather than the // underlying Interface), maybe it should just handle calling // SetTypeParams, SetUnderlying, and AddMethod instead? Those - // methods would need to support reentrant calls though. It would + // methods would need to support reentrant calls though. It would // also make the API more future-proof towards further extensions // (like SetTypeParams). - - tparams, underlying, methods := t.resolve(t) - - switch underlying.(type) { - case nil, *Named: - panic("invalid underlying type") - } - - t.tparams = bindTParams(tparams) - t.underlying = underlying - t.methods = methods + t.tparams, t.underlying, t.methods = t.resolver(env, t) + t.fromRHS = t.underlying // for cycle detection }) return t } @@ -112,28 +91,28 @@ func (t *Named) Obj() *TypeName { return t.orig.obj // for non-instances this is the same as t.obj } -// _Orig returns the original generic type an instantiated type is derived from. -// If t is not an instantiated type, the result is t. -func (t *Named) _Orig() *Named { return t.orig } +// Origin returns the parameterized type from which the named type t is +// instantiated. If t is not an instantiated type, the result is t. +func (t *Named) Origin() *Named { return t.orig } // TODO(gri) Come up with a better representation and API to distinguish // between parameterized instantiated and non-instantiated types. // TypeParams returns the type parameters of the named type t, or nil. // The result is non-nil for an (originally) parameterized type even if it is instantiated. -func (t *Named) TypeParams() *TypeParamList { return t.load().tparams } +func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams } // SetTypeParams sets the type parameters of the named type t. -func (t *Named) SetTypeParams(tparams []*TypeParam) { t.load().tparams = bindTParams(tparams) } +func (t *Named) SetTypeParams(tparams []*TypeParam) { t.resolve(nil).tparams = bindTParams(tparams) } // TypeArgs returns the type arguments used to instantiate the named type t. func (t *Named) TypeArgs() *TypeList { return t.targs } // NumMethods returns the number of explicit methods whose receiver is named type t. -func (t *Named) NumMethods() int { return len(t.load().methods) } +func (t *Named) NumMethods() int { return len(t.resolve(nil).methods) } // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). -func (t *Named) Method(i int) *Func { return t.load().methods[i] } +func (t *Named) Method(i int) *Func { return t.resolve(nil).methods[i] } // SetUnderlying sets the underlying type and marks t as complete. func (t *Named) SetUnderlying(underlying Type) { @@ -143,18 +122,18 @@ func (t *Named) SetUnderlying(underlying Type) { if _, ok := underlying.(*Named); ok { panic("underlying type must not be *Named") } - t.load().underlying = underlying + t.resolve(nil).underlying = underlying } // AddMethod adds method m unless it is already in the method list. func (t *Named) AddMethod(m *Func) { - t.load() + t.resolve(nil) if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 { t.methods = append(t.methods, m) } } -func (t *Named) Underlying() Type { return t.load().expand(nil).underlying } +func (t *Named) Underlying() Type { return t.resolve(nil).underlying } func (t *Named) String() string { return TypeString(t, nil) } // ---------------------------------------------------------------------------- @@ -240,43 +219,105 @@ func (n *Named) setUnderlying(typ Type) { } } -// expand ensures that the underlying type of n is instantiated. +// bestEnv returns the best available environment. In order of preference: +// - the given env, if non-nil +// - the Checker env, if check is non-nil +// - a new environment +func (check *Checker) bestEnv(env *Environment) *Environment { + if env != nil { + return env + } + if check != nil { + assert(check.conf.Environment != nil) + return check.conf.Environment + } + return NewEnvironment() +} + +// expandNamed ensures that the underlying type of n is instantiated. // The underlying type will be Typ[Invalid] if there was an error. -func (n *Named) expand(env *Environment) *Named { - if n.instPos != nil { - // n must be loaded before instantiation, in order to have accurate - // tparams. This is done implicitly by the call to n.TypeParams, but making - // it explicit is harmless: load is idempotent. - n.load() - var u Type - if n.check.validateTArgLen(*n.instPos, n.tparams.Len(), n.targs.Len()) { - // TODO(rfindley): handling an optional Checker and Environment here (and - // in subst) feels overly complicated. Can we simplify? - if env == nil { - if n.check != nil { - env = n.check.env - } else { - // If we're instantiating lazily, we might be outside the scope of a - // type-checking pass. In that case we won't have a pre-existing - // environment, but don't want to create a duplicate of the current - // instance in the process of expansion. - env = NewEnvironment() - } - h := env.typeHash(n.orig, n.targs.list()) - // add the instance to the environment to avoid infinite recursion. - // addInstance may return a different, existing instance, but we - // shouldn't return that instance from expand. - env.typeForHash(h, n) +func expandNamed(env *Environment, n *Named, instPos token.Pos) (tparams *TypeParamList, underlying Type, methods []*Func) { + n.orig.resolve(env) + + check := n.check + + if check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) { + // We must always have an env, to avoid infinite recursion. + env = check.bestEnv(env) + h := env.typeHash(n.orig, n.targs.list()) + // ensure that an instance is recorded for h to avoid infinite recursion. + env.typeForHash(h, n) + + smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) + underlying = n.check.subst(instPos, n.orig.underlying, smap, env) + + for i := 0; i < n.orig.NumMethods(); i++ { + origm := n.orig.Method(i) + + // During type checking origm may not have a fully set up type, so defer + // instantiation of its signature until later. + m := NewFunc(origm.pos, origm.pkg, origm.name, nil) + m.hasPtrRecv = origm.hasPtrRecv + // Setting instRecv here allows us to complete later (we need the + // instRecv to get targs and the original method). + m.instRecv = n + + methods = append(methods, m) + } + } else { + underlying = Typ[Invalid] + } + + // Methods should not escape the type checker API without being completed. If + // we're in the context of a type checking pass, we need to defer this until + // later (not all methods may have types). + completeMethods := func() { + for _, m := range methods { + if m.instRecv != nil { + check.completeMethod(env, m) } - u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TypeParams().list(), n.targs.list()), env) - } else { - u = Typ[Invalid] } - n.underlying = u - n.fromRHS = u - n.instPos = nil } - return n + if check != nil { + check.later(completeMethods) + } else { + completeMethods() + } + + return n.orig.tparams, underlying, methods +} + +func (check *Checker) completeMethod(env *Environment, m *Func) { + assert(m.instRecv != nil) + rtyp := m.instRecv + m.instRecv = nil + m.setColor(black) + + assert(rtyp.TypeArgs().Len() > 0) + + // Look up the original method. + _, orig := lookupMethod(rtyp.orig.methods, rtyp.obj.pkg, m.name) + assert(orig != nil) + if check != nil { + check.objDecl(orig, nil) + } + origSig := orig.typ.(*Signature) + if origSig.RecvTypeParams().Len() != rtyp.targs.Len() { + m.typ = origSig // or new(Signature), but we can't use Typ[Invalid]: Funcs must have Signature type + return // error reported elsewhere + } + + smap := makeSubstMap(origSig.RecvTypeParams().list(), rtyp.targs.list()) + sig := check.subst(orig.pos, origSig, smap, env).(*Signature) + if sig == origSig { + // No substitution occurred, but we still need to create a copy to hold the + // instantiated receiver. + copy := *origSig + sig = © + } + sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp) + + m.typ = sig } // safeUnderlying returns the underlying of typ without expanding instances, to @@ -285,7 +326,7 @@ func (n *Named) expand(env *Environment) *Named { // TODO(rfindley): eliminate this function or give it a better name. func safeUnderlying(typ Type) Type { if t, _ := typ.(*Named); t != nil { - return t.load().underlying + return t.resolve(nil).underlying } return typ.Underlying() } |