aboutsummaryrefslogtreecommitdiff
path: root/src/go/types/named.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/go/types/named.go')
-rw-r--r--src/go/types/named.go79
1 files changed, 68 insertions, 11 deletions
diff --git a/src/go/types/named.go b/src/go/types/named.go
index 943d52f0fe..66ae012379 100644
--- a/src/go/types/named.go
+++ b/src/go/types/named.go
@@ -221,16 +221,17 @@ func (n *Named) setUnderlying(typ Type) {
// expandNamed ensures that the underlying type of n is instantiated.
// The underlying type will be Typ[Invalid] if there was an error.
-func expandNamed(env *Environment, n *Named, instPos token.Pos) (*TypeParamList, Type, []*Func) {
+func expandNamed(env *Environment, n *Named, instPos token.Pos) (tparams *TypeParamList, underlying Type, methods []*Func) {
n.orig.resolve(env)
- var u Type
- if n.check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) {
+ check := n.check
+
+ if check.validateTArgLen(instPos, n.orig.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.conf.Environment
+ if check != nil {
+ env = check.conf.Environment
} 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
@@ -239,16 +240,72 @@ func expandNamed(env *Environment, n *Named, instPos token.Pos) (*TypeParamList,
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.
+ // ensure that an instance is recorded for h to avoid infinite recursion.
env.typeForHash(h, n)
}
- u = n.check.subst(instPos, n.orig.underlying, makeSubstMap(n.orig.tparams.list(), n.targs.list()), env)
+ 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)
+ }
+ }
+ }
+ if check != nil {
+ check.later(completeMethods)
} else {
- u = Typ[Invalid]
+ completeMethods()
}
- return n.orig.tparams, u, n.orig.methods
+
+ 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)
+ 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