]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: store Named instance information separately
authorRobert Findley <rfindley@google.com>
Sat, 7 May 2022 23:26:35 +0000 (19:26 -0400)
committerRobert Findley <rfindley@google.com>
Mon, 6 Jun 2022 15:21:40 +0000 (15:21 +0000)
Separate instance information into an instance struct, to reduce memory
footprint for non-instance Named types. This may induce a sense of
deja-vu: we had a similar construct in the past that was removed as
unnecessary. With additional new fields being added that only apply to
instances, having a separate struct makes sense again.

Updates #52728

Change-Id: I0bb5982d71c27e6b574bfb4f7b886a6aeb9c5390
Reviewed-on: https://go-review.googlesource.com/c/go/+/404884
Reviewed-by: Robert Griesemer <gri@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

22 files changed:
src/cmd/compile/internal/types2/decl.go
src/cmd/compile/internal/types2/infer.go
src/cmd/compile/internal/types2/instantiate.go
src/cmd/compile/internal/types2/named.go
src/cmd/compile/internal/types2/predicates.go
src/cmd/compile/internal/types2/sizeof_test.go
src/cmd/compile/internal/types2/subst.go
src/cmd/compile/internal/types2/typestring.go
src/cmd/compile/internal/types2/typexpr.go
src/cmd/compile/internal/types2/unify.go
src/cmd/compile/internal/types2/validtype.go
src/go/types/decl.go
src/go/types/infer.go
src/go/types/instantiate.go
src/go/types/named.go
src/go/types/predicates.go
src/go/types/sizeof_test.go
src/go/types/subst.go
src/go/types/typestring.go
src/go/types/typexpr.go
src/go/types/unify.go
src/go/types/validtype.go

index a5d29765c65d19bcfdc51c656e67871656cd2ed1..0bc5f9f3e1cc017e383af7a1b2eb9feae477ab84 100644 (file)
@@ -508,7 +508,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
        }
 
        // type definition or generic type declaration
-       named := check.newNamed(obj, nil, nil, nil)
+       named := check.newNamed(obj, nil, nil)
        def.setUnderlying(named)
 
        if tdecl.TParamList != nil {
@@ -635,7 +635,7 @@ func (check *Checker) collectMethods(obj *TypeName) {
        // and field names must be distinct."
        base, _ := obj.typ.(*Named) // shouldn't fail but be conservative
        if base != nil {
-               assert(base.targs.Len() == 0) // collectMethods should not be called on an instantiated type
+               assert(base.TypeArgs().Len() == 0) // collectMethods should not be called on an instantiated type
 
                // See issue #52529: we must delay the expansion of underlying here, as
                // base may not be fully set-up.
index 9e77d67a7de2ea508a88b158e58e1419d23eaa35..8ab568be596109e942c5c756c9f7ee43973c4d58 100644 (file)
@@ -434,7 +434,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
                return w.isParameterized(t.elem)
 
        case *Named:
-               return w.isParameterizedTypeList(t.targs.list())
+               return w.isParameterizedTypeList(t.TypeArgs().list())
 
        case *TypeParam:
                // t must be one of w.tparams
index 44ed0319c822ebe35b47828e0a231e6cb862958c..ed6206e150fe0179bf9f39a336bb231f026f2df0 100644 (file)
@@ -76,10 +76,7 @@ func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, ctxt *Co
 
        switch orig := orig.(type) {
        case *Named:
-               tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
-               named := check.newNamed(tname, orig, nil, nil) // underlying, tparams, and methods are set when named is resolved
-               named.targs = newTypeList(targs)
-               res = named
+               res = check.newNamedInstance(pos, orig, targs)
 
        case *Signature:
                tparams := orig.TypeParams()
index 77655bc821b0a16eaae7c2249f990f41c3814a43..133fb6fa88e0caf4fe47b10e00164d900fca7018 100644 (file)
@@ -5,6 +5,7 @@
 package types2
 
 import (
+       "cmd/compile/internal/syntax"
        "sync"
        "sync/atomic"
 )
@@ -83,14 +84,15 @@ import (
 type Named struct {
        check *Checker  // non-nil during type-checking; nil otherwise
        obj   *TypeName // corresponding declared object for declared types; see above for instantiated types
-       orig  *Named    // origin type for instantiated types, this type for declared types
-       targs *TypeList // type arguments (after instantiation), or nil
 
        // fromRHS holds the type (on RHS of declaration) this *Named type is derived
        // from (for cycle reporting). Only used by validType, and therefore does not
        // require synchronization.
        fromRHS Type
 
+       // information for instantiated types; nil otherwise
+       inst *instance
+
        mu         sync.Mutex     // guards all fields below
        state_     uint32         // the current state of this type; must only be accessed atomically
        underlying Type           // possibly a *Named during setup; never a *Named once set up completely
@@ -102,13 +104,19 @@ type Named struct {
        // instantiated types, methods are individually expanded when they are first
        // accessed.
        methods []*Func
-       // number of expanded methods (only valid for instantiated named types)
-       expandedMethods int // expandedMethods <= len(orig.methods)
 
        // loader may be provided to lazily load type parameters, underlying type, and methods.
        loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func)
 }
 
+// instance holds information that is only necessary for instantiated named
+// types.
+type instance struct {
+       orig            *Named    // original, uninstantiated type
+       targs           *TypeList // type arguments
+       expandedMethods int       // number of expanded methods; expandedMethods <= len(orig.methods)
+}
+
 // namedState represents the possible states that a named type may assume.
 type namedState uint32
 
@@ -125,7 +133,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
        if _, ok := underlying.(*Named); ok {
                panic("underlying type must not be *Named")
        }
-       return (*Checker)(nil).newNamed(obj, nil, underlying, methods)
+       return (*Checker)(nil).newNamed(obj, underlying, methods)
 }
 
 // resolve resolves the type parameters, methods, and underlying type of n.
@@ -149,19 +157,20 @@ func (n *Named) resolve(ctxt *Context) *Named {
                return n
        }
 
-       if n.TypeArgs().Len() > 0 {
+       if n.inst != nil {
                assert(n.underlying == nil) // n is an unresolved instance
                assert(n.loader == nil)     // instances are created by instantiation, in which case n.loader is nil
-               n.orig.resolve(ctxt)
 
+               orig := n.inst.orig
+               orig.resolve(ctxt)
                underlying := n.expandUnderlying(ctxt)
 
-               n.tparams = n.orig.tparams
+               n.tparams = orig.tparams
                n.underlying = underlying
-               n.fromRHS = n.orig.fromRHS // for cycle detection
+               n.fromRHS = orig.fromRHS // for cycle detection
 
-               if len(n.orig.methods) == 0 {
-                       n.setState(complete)
+               if len(orig.methods) == 0 {
+                       n.setState(complete) // nothing further to do
                } else {
                        n.setState(resolved)
                }
@@ -204,11 +213,8 @@ func (n *Named) setState(state namedState) {
 }
 
 // newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
-func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods []*Func) *Named {
-       typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods}
-       if typ.orig == nil {
-               typ.orig = typ
-       }
+func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
+       typ := &Named{check: check, obj: obj, fromRHS: underlying, underlying: underlying, methods: methods}
        if obj.typ == nil {
                obj.typ = typ
        }
@@ -219,8 +225,22 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, meth
        return typ
 }
 
+func (check *Checker) newNamedInstance(pos syntax.Pos, orig *Named, targs []Type) *Named {
+       assert(len(targs) > 0)
+
+       obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
+       inst := &instance{orig: orig, targs: newTypeList(targs)}
+       typ := &Named{check: check, obj: obj, inst: inst}
+       obj.typ = typ
+       // Ensure that typ is always expanded and sanity-checked.
+       if check != nil {
+               check.needsCleanup(typ)
+       }
+       return typ
+}
+
 func (t *Named) cleanup() {
-       assert(t.orig.orig == t.orig)
+       assert(t.inst == nil || t.inst.orig.inst == nil)
        // Ensure that every defined type created in the course of type-checking has
        // either non-*Named underlying type, or is unexpanded.
        //
@@ -242,14 +262,21 @@ func (t *Named) cleanup() {
 
 // Obj returns the type name for the declaration defining the named type t. For
 // instantiated types, this is same as the type name of the origin type.
-func (t *Named) Obj() *TypeName { return t.orig.obj } // for non-instances this is the same as t.obj
+func (t *Named) Obj() *TypeName {
+       if t.inst == nil {
+               return t.obj
+       }
+       return t.inst.orig.obj
+}
 
 // Origin returns the generic 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.
+func (t *Named) Origin() *Named {
+       if t.inst == nil {
+               return t
+       }
+       return t.inst.orig
+}
 
 // TypeParams returns the type parameters of the named type t, or nil.
 // The result is non-nil for an (originally) generic type even if it is instantiated.
@@ -258,19 +285,26 @@ func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams }
 // SetTypeParams sets the type parameters of the named type t.
 // t must not have type arguments.
 func (t *Named) SetTypeParams(tparams []*TypeParam) {
-       assert(t.targs.Len() == 0)
+       assert(t.inst == nil)
        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 }
+func (t *Named) TypeArgs() *TypeList {
+       if t.inst == nil {
+               return nil
+       }
+       return t.inst.targs
+}
 
 // NumMethods returns the number of explicit methods defined for t.
 //
 // For an ordinary or instantiated type t, the receiver base type of these
 // methods will be the named type t. For an uninstantiated generic type t, each
 // method receiver will be instantiated with its receiver type parameters.
-func (t *Named) NumMethods() int { return len(t.orig.resolve(nil).methods) }
+func (t *Named) NumMethods() int {
+       return len(t.Origin().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 {
@@ -280,23 +314,24 @@ func (t *Named) Method(i int) *Func {
                return t.methods[i]
        }
 
-       assert(t.TypeArgs().Len() > 0) // only instances should have incomplete methods
+       assert(t.inst != nil) // only instances should have incomplete methods
+       orig := t.inst.orig
 
        t.mu.Lock()
        defer t.mu.Unlock()
 
-       if len(t.methods) != len(t.orig.methods) {
+       if len(t.methods) != len(orig.methods) {
                assert(len(t.methods) == 0)
-               t.methods = make([]*Func, len(t.orig.methods))
+               t.methods = make([]*Func, len(orig.methods))
        }
 
        if t.methods[i] == nil {
                t.methods[i] = t.expandMethod(i)
-               t.expandedMethods++
+               t.inst.expandedMethods++
 
                // Check if we've created all methods at this point. If we have, mark the
                // type as fully expanded.
-               if t.expandedMethods == len(t.orig.methods) {
+               if t.inst.expandedMethods == len(orig.methods) {
                        t.setState(complete)
                }
        }
@@ -307,11 +342,9 @@ func (t *Named) Method(i int) *Func {
 // expandMethod substitutes type arguments in the i'th method for an
 // instantiated receiver.
 func (t *Named) expandMethod(i int) *Func {
-       assert(t.TypeArgs().Len() > 0) // t must be an instance
-
        // t.orig.methods is not lazy. origm is the method instantiated with its
        // receiver type parameters (the "origin" method).
-       origm := t.orig.Method(i)
+       origm := t.inst.orig.Method(i)
        assert(origm != nil)
 
        check := t.check
@@ -338,9 +371,9 @@ func (t *Named) expandMethod(i int) *Func {
        // We can only substitute if we have a correspondence between type arguments
        // and type parameters. This check is necessary in the presence of invalid
        // code.
-       if origSig.RecvTypeParams().Len() == t.targs.Len() {
+       if origSig.RecvTypeParams().Len() == t.inst.targs.Len() {
                ctxt := check.bestContext(nil)
-               smap := makeSubstMap(origSig.RecvTypeParams().list(), t.targs.list())
+               smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list())
                sig = check.subst(origm.pos, origSig, smap, ctxt).(*Signature)
        }
 
@@ -365,7 +398,7 @@ func (t *Named) expandMethod(i int) *Func {
 // SetUnderlying sets the underlying type and marks t as complete.
 // t must not have type arguments.
 func (t *Named) SetUnderlying(underlying Type) {
-       assert(t.targs.Len() == 0)
+       assert(t.inst == nil)
        if underlying == nil {
                panic("underlying type must not be nil")
        }
@@ -381,7 +414,7 @@ func (t *Named) SetUnderlying(underlying Type) {
 // AddMethod adds method m unless it is already in the method list.
 // t must not have type arguments.
 func (t *Named) AddMethod(m *Func) {
-       assert(t.targs.Len() == 0)
+       assert(t.inst == nil)
        t.resolve(nil)
        if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 {
                t.methods = append(t.methods, m)
@@ -493,7 +526,7 @@ func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Fu
        // If n is an instance, we may not have yet instantiated all of its methods.
        // Look up the method index in orig, and only instantiate method at the
        // matching index (if any).
-       i, _ := lookupMethod(n.orig.methods, pkg, name, foldCase)
+       i, _ := lookupMethod(n.Origin().methods, pkg, name, foldCase)
        if i < 0 {
                return -1, nil
        }
@@ -531,36 +564,39 @@ func (n *Named) expandUnderlying(ctxt *Context) Type {
                }()
        }
 
-       assert(n.orig.underlying != nil)
+       assert(n.inst.orig.underlying != nil)
 
-       if _, unexpanded := n.orig.underlying.(*Named); unexpanded {
+       orig := n.inst.orig
+       targs := n.inst.targs
+
+       if _, unexpanded := orig.underlying.(*Named); unexpanded {
                // We should only get a Named underlying type here during type checking
                // (for example, in recursive type declarations).
                assert(check != nil)
        }
 
-       if n.orig.tparams.Len() != n.targs.Len() {
+       if orig.tparams.Len() != targs.Len() {
                // Mismatching arg and tparam length may be checked elsewhere.
                return Typ[Invalid]
        }
 
        // We must always have a context, to avoid infinite recursion.
        ctxt = check.bestContext(ctxt)
-       h := ctxt.instanceHash(n.orig, n.targs.list())
+       h := ctxt.instanceHash(orig, targs.list())
        // ensure that an instance is recorded for h to avoid infinite recursion.
-       ctxt.update(h, n.orig, n.TypeArgs().list(), n)
+       ctxt.update(h, orig, targs.list(), n)
 
-       smap := makeSubstMap(n.orig.tparams.list(), n.targs.list())
-       underlying := n.check.subst(n.obj.pos, n.orig.underlying, smap, ctxt)
+       smap := makeSubstMap(orig.tparams.list(), targs.list())
+       underlying := n.check.subst(n.obj.pos, orig.underlying, smap, ctxt)
        // If the underlying type of n is an interface, we need to set the receiver
        // of its methods accurately -- we set the receiver of interface methods on
        // the RHS of a type declaration to the defined type.
        if iface, _ := underlying.(*Interface); iface != nil {
-               if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied {
+               if methods, copied := replaceRecvType(iface.methods, orig, n); copied {
                        // If the underlying type doesn't actually use type parameters, it's
                        // possible that it wasn't substituted. In this case we need to create
                        // a new *Interface before modifying receivers.
-                       if iface == n.orig.underlying {
+                       if iface == orig.underlying {
                                old := iface
                                iface = check.newInterface()
                                iface.embeddeds = old.embeddeds
@@ -571,6 +607,7 @@ func (n *Named) expandUnderlying(ctxt *Context) Type {
                        iface.methods = methods
                }
        }
+
        return underlying
 }
 
index 6bce26137eb772d029f41aed15b5d1ff304141ff..6b6c21c780195618c7c8c3e2ba21629a1dfdbd5f 100644 (file)
@@ -102,7 +102,7 @@ func isTypeParam(t Type) bool {
 func isGeneric(t Type) bool {
        // A parameterized type is only generic if it doesn't have an instantiation already.
        named, _ := t.(*Named)
-       return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil
+       return named != nil && named.obj != nil && named.inst == nil && named.TypeParams().Len() > 0
 }
 
 // Comparable reports whether values of type T are comparable.
@@ -401,7 +401,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
                        if len(xargs) > 0 {
                                // Instances are identical if their original type and type arguments
                                // are identical.
-                               if !Identical(x.orig, y.orig) {
+                               if !Identical(x.Origin(), y.Origin()) {
                                        return false
                                }
                                for i, xa := range xargs {
index 3f0bf8f3c50f077872b208f538ab22e5b5a4cd95..17876d1f3c2354a1d9fc09cee096157424e99a78 100644 (file)
@@ -31,7 +31,7 @@ func TestSizeof(t *testing.T) {
                {Interface{}, 40, 80},
                {Map{}, 16, 32},
                {Chan{}, 12, 24},
-               {Named{}, 68, 128},
+               {Named{}, 60, 112},
                {TypeParam{}, 28, 48},
                {term{}, 12, 24},
 
index 6e41ebdf53907207ac784538cd548e7cd9ce1cba..9af1a71cfebccf16f3da9ea1b06e564c23a15a7d 100644 (file)
@@ -176,7 +176,7 @@ func (subst *subster) typ(typ Type) Type {
                        // In this case the interface will not be substituted here, because its
                        // method signatures do not depend on the type parameter P, but we still
                        // need to create new interface methods to hold the instantiated
-                       // receiver. This is handled by expandNamed.
+                       // receiver. This is handled by Named.expandUnderlying.
                        iface.methods, _ = replaceRecvType(methods, t, iface)
                        return iface
                }
@@ -207,19 +207,20 @@ func (subst *subster) typ(typ Type) Type {
                        }
                }
 
-               // subst is called by expandNamed, so in this function we need to be
+               // subst is called during expansion, so in this function we need to be
                // careful not to call any methods that would cause t to be expanded: doing
                // so would result in deadlock.
                //
-               // So we call t.orig.TypeParams() rather than t.TypeParams() here and
-               // below.
-               if t.orig.TypeParams().Len() == 0 {
+               // So we call t.Origin().TypeParams() rather than t.TypeParams().
+               orig := t.Origin()
+               n := orig.TypeParams().Len()
+               if n == 0 {
                        dump(">>> %s is not parameterized", t)
                        return t // type is not parameterized
                }
 
                var newTArgs []Type
-               if t.targs.Len() != t.orig.TypeParams().Len() {
+               if t.TypeArgs().Len() != n {
                        return Typ[Invalid] // error reported elsewhere
                }
 
@@ -228,14 +229,14 @@ func (subst *subster) typ(typ Type) Type {
                // For each (existing) type argument targ, determine if it needs
                // to be substituted; i.e., if it is or contains a type parameter
                // that has a type argument for it.
-               for i, targ := range t.targs.list() {
+               for i, targ := range t.TypeArgs().list() {
                        dump(">>> %d targ = %s", i, targ)
                        new_targ := subst.typ(targ)
                        if new_targ != targ {
                                dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
                                if newTArgs == nil {
-                                       newTArgs = make([]Type, t.orig.TypeParams().Len())
-                                       copy(newTArgs, t.targs.list())
+                                       newTArgs = make([]Type, n)
+                                       copy(newTArgs, t.TypeArgs().list())
                                }
                                newTArgs[i] = new_targ
                        }
@@ -247,9 +248,9 @@ func (subst *subster) typ(typ Type) Type {
                }
 
                // before creating a new named type, check if we have this one already
-               h := subst.ctxt.instanceHash(t.orig, newTArgs)
+               h := subst.ctxt.instanceHash(orig, newTArgs)
                dump(">>> new type hash: %s", h)
-               if named := subst.ctxt.lookup(h, t.orig, newTArgs); named != nil {
+               if named := subst.ctxt.lookup(h, orig, newTArgs); named != nil {
                        dump(">>> found %s", named)
                        return named
                }
@@ -258,7 +259,7 @@ func (subst *subster) typ(typ Type) Type {
                // recursion. The position used here is irrelevant because validation only
                // occurs on t (we don't call validType on named), but we use subst.pos to
                // help with debugging.
-               return subst.check.instance(subst.pos, t.orig, newTArgs, subst.ctxt)
+               return subst.check.instance(subst.pos, orig, newTArgs, subst.ctxt)
 
                // Note that if we were to expose substitution more generally (not just in
                // the context of a declaration), we'd have to substitute in
index e0f36fcec47cfdfd1c007ecafc835417bf06cc2a..c10c2d8973916acecaa0295f127fc63cd32c018e 100644 (file)
@@ -284,9 +284,9 @@ func (w *typeWriter) typ(typ Type) {
                        w.string(strconv.Itoa(w.ctxt.getID(t)))
                }
                w.typeName(t.obj) // when hashing written for readability of the hash only
-               if t.targs != nil {
+               if t.inst != nil {
                        // instantiated type
-                       w.typeList(t.targs.list())
+                       w.typeList(t.inst.targs.list())
                } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
                        // parameterized type
                        w.tParamList(t.TypeParams().list())
index 020653332d1809b5243102b41f9b2b315d3ee3c9..ea13eb622d82e529a217ac99303c860c3acbf20e 100644 (file)
@@ -444,8 +444,8 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
                // errors.
                check.recordInstance(x, inst.TypeArgs().list(), inst)
 
-               if check.validateTArgLen(x.Pos(), inst.TypeParams().Len(), inst.targs.Len()) {
-                       if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.targs.list()); err != nil {
+               if check.validateTArgLen(x.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) {
+                       if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list()); err != nil {
                                // best position for error reporting
                                pos := x.Pos()
                                if i < len(xlist) {
@@ -453,7 +453,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
                                }
                                check.softErrorf(pos, "%s", err)
                        } else {
-                               check.mono.recordInstance(check.pkg, x.Pos(), inst.TypeParams().list(), inst.targs.list(), xlist)
+                               check.mono.recordInstance(check.pkg, x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), xlist)
                        }
                }
 
index a7f68a05b1d2878443a67671704f8028f0c941de..7063789f3f0c0fdcc79e6461aa145d4915599ea1 100644 (file)
@@ -546,8 +546,8 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
        case *Named:
                // TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate.
                if y, ok := y.(*Named); ok {
-                       xargs := x.targs.list()
-                       yargs := y.targs.list()
+                       xargs := x.TypeArgs().list()
+                       yargs := y.TypeArgs().list()
 
                        if len(xargs) != len(yargs) {
                                return false
index d495c6788ee0722495467e3c53b55dc12b155b5b..b69120481b18107a2a5b7942ebbb90c036f151cc 100644 (file)
@@ -72,7 +72,7 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn
                switch check.infoMap[t] {
                case unknown:
                        check.infoMap[t] = marked
-                       check.infoMap[t] = check.validType0(t.orig.fromRHS, env.push(t), append(path, t.obj))
+                       check.infoMap[t] = check.validType0(t.Origin().fromRHS, env.push(t), append(path, t.obj))
                case marked:
                        // We have seen type t before and thus must have a cycle.
                        check.infoMap[t] = invalid
index b5ff1214dd548158311dad47ba230e1ec6a83e0f..c176042852c639306d2a319bb0a88c836a295abe 100644 (file)
@@ -565,7 +565,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
        }
 
        // type definition or generic type declaration
-       named := check.newNamed(obj, nil, nil, nil)
+       named := check.newNamed(obj, nil, nil)
        def.setUnderlying(named)
 
        if tdecl.TypeParams != nil {
@@ -711,7 +711,7 @@ func (check *Checker) collectMethods(obj *TypeName) {
        // and field names must be distinct."
        base, _ := obj.typ.(*Named) // shouldn't fail but be conservative
        if base != nil {
-               assert(base.targs.Len() == 0) // collectMethods should not be called on an instantiated type
+               assert(base.TypeArgs().Len() == 0) // collectMethods should not be called on an instantiated type
 
                // See issue #52529: we must delay the expansion of underlying here, as
                // base may not be fully set-up.
index 031850b8dad4fcdfdf9de22ca95c4b3d7752f0ca..ebe6d8ced7f308db563bf92f256f3dfbc5bd5443 100644 (file)
@@ -434,7 +434,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
                return w.isParameterized(t.elem)
 
        case *Named:
-               return w.isParameterizedTypeList(t.targs.list())
+               return w.isParameterizedTypeList(t.TypeArgs().list())
 
        case *TypeParam:
                // t must be one of w.tparams
index 8be0eab40762d4bd4e2bf3b51e96e3327a782f89..d420a615728e7904f1a9c88949e6157ce6a7386d 100644 (file)
@@ -76,10 +76,7 @@ func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, ctxt *Con
 
        switch orig := orig.(type) {
        case *Named:
-               tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
-               named := check.newNamed(tname, orig, nil, nil) // underlying, tparams, and methods are set when named is resolved
-               named.targs = newTypeList(targs)
-               res = named
+               res = check.newNamedInstance(pos, orig, targs)
 
        case *Signature:
                tparams := orig.TypeParams()
index f1c5dd4f81963c14b391471e5546656b753e6613..71a26f96a16c79fac4a04a60dfc4dde7007439af 100644 (file)
@@ -5,6 +5,7 @@
 package types
 
 import (
+       "go/token"
        "sync"
        "sync/atomic"
 )
@@ -83,14 +84,15 @@ import (
 type Named struct {
        check *Checker  // non-nil during type-checking; nil otherwise
        obj   *TypeName // corresponding declared object for declared types; see above for instantiated types
-       orig  *Named    // origin type for instantiated types, this type for declared types
-       targs *TypeList // type arguments (after instantiation), or nil
 
        // fromRHS holds the type (on RHS of declaration) this *Named type is derived
        // from (for cycle reporting). Only used by validType, and therefore does not
        // require synchronization.
        fromRHS Type
 
+       // information for instantiated types; nil otherwise
+       inst *instance
+
        mu         sync.Mutex     // guards all fields below
        state_     uint32         // the current state of this type; must only be accessed atomically
        underlying Type           // possibly a *Named during setup; never a *Named once set up completely
@@ -102,13 +104,19 @@ type Named struct {
        // instantiated types, methods are individually expanded when they are first
        // accessed.
        methods []*Func
-       // number of expanded methods (only valid for instantiated named types)
-       expandedMethods int // expandedMethods <= len(orig.methods)
 
        // loader may be provided to lazily load type parameters, underlying type, and methods.
        loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func)
 }
 
+// instance holds information that is only necessary for instantiated named
+// types.
+type instance struct {
+       orig            *Named    // original, uninstantiated type
+       targs           *TypeList // type arguments
+       expandedMethods int       // number of expanded methods; expandedMethods <= len(orig.methods)
+}
+
 // namedState represents the possible states that a named type may assume.
 type namedState uint32
 
@@ -125,7 +133,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
        if _, ok := underlying.(*Named); ok {
                panic("underlying type must not be *Named")
        }
-       return (*Checker)(nil).newNamed(obj, nil, underlying, methods)
+       return (*Checker)(nil).newNamed(obj, underlying, methods)
 }
 
 // resolve resolves the type parameters, methods, and underlying type of n.
@@ -149,19 +157,20 @@ func (n *Named) resolve(ctxt *Context) *Named {
                return n
        }
 
-       if n.TypeArgs().Len() > 0 {
+       if n.inst != nil {
                assert(n.underlying == nil) // n is an unresolved instance
                assert(n.loader == nil)     // instances are created by instantiation, in which case n.loader is nil
-               n.orig.resolve(ctxt)
 
+               orig := n.inst.orig
+               orig.resolve(ctxt)
                underlying := n.expandUnderlying(ctxt)
 
-               n.tparams = n.orig.tparams
+               n.tparams = orig.tparams
                n.underlying = underlying
-               n.fromRHS = n.orig.fromRHS // for cycle detection
+               n.fromRHS = orig.fromRHS // for cycle detection
 
-               if len(n.orig.methods) == 0 {
-                       n.setState(complete)
+               if len(orig.methods) == 0 {
+                       n.setState(complete) // nothing further to do
                } else {
                        n.setState(resolved)
                }
@@ -204,11 +213,8 @@ func (n *Named) setState(state namedState) {
 }
 
 // newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
-func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods []*Func) *Named {
-       typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods}
-       if typ.orig == nil {
-               typ.orig = typ
-       }
+func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
+       typ := &Named{check: check, obj: obj, fromRHS: underlying, underlying: underlying, methods: methods}
        if obj.typ == nil {
                obj.typ = typ
        }
@@ -219,8 +225,22 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, meth
        return typ
 }
 
+func (check *Checker) newNamedInstance(pos token.Pos, orig *Named, targs []Type) *Named {
+       assert(len(targs) > 0)
+
+       obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
+       inst := &instance{orig: orig, targs: newTypeList(targs)}
+       typ := &Named{check: check, obj: obj, inst: inst}
+       obj.typ = typ
+       // Ensure that typ is always expanded and sanity-checked.
+       if check != nil {
+               check.needsCleanup(typ)
+       }
+       return typ
+}
+
 func (t *Named) cleanup() {
-       assert(t.orig.orig == t.orig)
+       assert(t.inst == nil || t.inst.orig.inst == nil)
        // Ensure that every defined type created in the course of type-checking has
        // either non-*Named underlying type, or is unexpanded.
        //
@@ -243,15 +263,20 @@ func (t *Named) cleanup() {
 // Obj returns the type name for the declaration defining the named type t. For
 // instantiated types, this is same as the type name of the origin type.
 func (t *Named) Obj() *TypeName {
-       return t.orig.obj // for non-instances this is the same as t.obj
+       if t.inst == nil {
+               return t.obj
+       }
+       return t.inst.orig.obj
 }
 
 // Origin returns the generic 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.
+func (t *Named) Origin() *Named {
+       if t.inst == nil {
+               return t
+       }
+       return t.inst.orig
+}
 
 // TypeParams returns the type parameters of the named type t, or nil.
 // The result is non-nil for an (originally) generic type even if it is instantiated.
@@ -260,19 +285,26 @@ func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams }
 // SetTypeParams sets the type parameters of the named type t.
 // t must not have type arguments.
 func (t *Named) SetTypeParams(tparams []*TypeParam) {
-       assert(t.targs.Len() == 0)
+       assert(t.inst == nil)
        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 }
+func (t *Named) TypeArgs() *TypeList {
+       if t.inst == nil {
+               return nil
+       }
+       return t.inst.targs
+}
 
 // NumMethods returns the number of explicit methods defined for t.
 //
 // For an ordinary or instantiated type t, the receiver base type of these
 // methods will be the named type t. For an uninstantiated generic type t, each
 // method receiver will be instantiated with its receiver type parameters.
-func (t *Named) NumMethods() int { return len(t.orig.resolve(nil).methods) }
+func (t *Named) NumMethods() int {
+       return len(t.Origin().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 {
@@ -282,23 +314,24 @@ func (t *Named) Method(i int) *Func {
                return t.methods[i]
        }
 
-       assert(t.TypeArgs().Len() > 0) // only instances should have incomplete methods
+       assert(t.inst != nil) // only instances should have incomplete methods
+       orig := t.inst.orig
 
        t.mu.Lock()
        defer t.mu.Unlock()
 
-       if len(t.methods) != len(t.orig.methods) {
+       if len(t.methods) != len(orig.methods) {
                assert(len(t.methods) == 0)
-               t.methods = make([]*Func, len(t.orig.methods))
+               t.methods = make([]*Func, len(orig.methods))
        }
 
        if t.methods[i] == nil {
                t.methods[i] = t.expandMethod(i)
-               t.expandedMethods++
+               t.inst.expandedMethods++
 
                // Check if we've created all methods at this point. If we have, mark the
                // type as fully expanded.
-               if t.expandedMethods == len(t.orig.methods) {
+               if t.inst.expandedMethods == len(orig.methods) {
                        t.setState(complete)
                }
        }
@@ -309,11 +342,9 @@ func (t *Named) Method(i int) *Func {
 // expandMethod substitutes type arguments in the i'th method for an
 // instantiated receiver.
 func (t *Named) expandMethod(i int) *Func {
-       assert(t.TypeArgs().Len() > 0) // t must be an instance
-
        // t.orig.methods is not lazy. origm is the method instantiated with its
        // receiver type parameters (the "origin" method).
-       origm := t.orig.Method(i)
+       origm := t.inst.orig.Method(i)
        assert(origm != nil)
 
        check := t.check
@@ -340,9 +371,9 @@ func (t *Named) expandMethod(i int) *Func {
        // We can only substitute if we have a correspondence between type arguments
        // and type parameters. This check is necessary in the presence of invalid
        // code.
-       if origSig.RecvTypeParams().Len() == t.targs.Len() {
+       if origSig.RecvTypeParams().Len() == t.inst.targs.Len() {
                ctxt := check.bestContext(nil)
-               smap := makeSubstMap(origSig.RecvTypeParams().list(), t.targs.list())
+               smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list())
                sig = check.subst(origm.pos, origSig, smap, ctxt).(*Signature)
        }
 
@@ -367,7 +398,7 @@ func (t *Named) expandMethod(i int) *Func {
 // SetUnderlying sets the underlying type and marks t as complete.
 // t must not have type arguments.
 func (t *Named) SetUnderlying(underlying Type) {
-       assert(t.targs.Len() == 0)
+       assert(t.inst == nil)
        if underlying == nil {
                panic("underlying type must not be nil")
        }
@@ -383,7 +414,7 @@ func (t *Named) SetUnderlying(underlying Type) {
 // AddMethod adds method m unless it is already in the method list.
 // t must not have type arguments.
 func (t *Named) AddMethod(m *Func) {
-       assert(t.targs.Len() == 0)
+       assert(t.inst == nil)
        t.resolve(nil)
        if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 {
                t.methods = append(t.methods, m)
@@ -495,7 +526,7 @@ func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Fu
        // If n is an instance, we may not have yet instantiated all of its methods.
        // Look up the method index in orig, and only instantiate method at the
        // matching index (if any).
-       i, _ := lookupMethod(n.orig.methods, pkg, name, foldCase)
+       i, _ := lookupMethod(n.Origin().methods, pkg, name, foldCase)
        if i < 0 {
                return -1, nil
        }
@@ -533,36 +564,39 @@ func (n *Named) expandUnderlying(ctxt *Context) Type {
                }()
        }
 
-       assert(n.orig.underlying != nil)
+       assert(n.inst.orig.underlying != nil)
 
-       if _, unexpanded := n.orig.underlying.(*Named); unexpanded {
+       orig := n.inst.orig
+       targs := n.inst.targs
+
+       if _, unexpanded := orig.underlying.(*Named); unexpanded {
                // We should only get a Named underlying type here during type checking
                // (for example, in recursive type declarations).
                assert(check != nil)
        }
 
-       if n.orig.tparams.Len() != n.targs.Len() {
+       if orig.tparams.Len() != targs.Len() {
                // Mismatching arg and tparam length may be checked elsewhere.
                return Typ[Invalid]
        }
 
        // We must always have a context, to avoid infinite recursion.
        ctxt = check.bestContext(ctxt)
-       h := ctxt.instanceHash(n.orig, n.targs.list())
+       h := ctxt.instanceHash(orig, targs.list())
        // ensure that an instance is recorded for h to avoid infinite recursion.
-       ctxt.update(h, n.orig, n.TypeArgs().list(), n)
+       ctxt.update(h, orig, targs.list(), n)
 
-       smap := makeSubstMap(n.orig.tparams.list(), n.targs.list())
-       underlying := n.check.subst(n.obj.pos, n.orig.underlying, smap, ctxt)
+       smap := makeSubstMap(orig.tparams.list(), targs.list())
+       underlying := n.check.subst(n.obj.pos, orig.underlying, smap, ctxt)
        // If the underlying type of n is an interface, we need to set the receiver
        // of its methods accurately -- we set the receiver of interface methods on
        // the RHS of a type declaration to the defined type.
        if iface, _ := underlying.(*Interface); iface != nil {
-               if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied {
+               if methods, copied := replaceRecvType(iface.methods, orig, n); copied {
                        // If the underlying type doesn't actually use type parameters, it's
                        // possible that it wasn't substituted. In this case we need to create
                        // a new *Interface before modifying receivers.
-                       if iface == n.orig.underlying {
+                       if iface == orig.underlying {
                                old := iface
                                iface = check.newInterface()
                                iface.embeddeds = old.embeddeds
@@ -573,6 +607,7 @@ func (n *Named) expandUnderlying(ctxt *Context) Type {
                        iface.methods = methods
                }
        }
+
        return underlying
 }
 
index 51d056f35595183ca5771df30b1fb141426a8387..6e08b76e406029acdd0a204a6c47b7c21add317a 100644 (file)
@@ -104,7 +104,7 @@ func isTypeParam(t Type) bool {
 func isGeneric(t Type) bool {
        // A parameterized type is only generic if it doesn't have an instantiation already.
        named, _ := t.(*Named)
-       return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil
+       return named != nil && named.obj != nil && named.inst == nil && named.TypeParams().Len() > 0
 }
 
 // Comparable reports whether values of type T are comparable.
@@ -403,7 +403,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
                        if len(xargs) > 0 {
                                // Instances are identical if their original type and type arguments
                                // are identical.
-                               if !Identical(x.orig, y.orig) {
+                               if !Identical(x.Origin(), y.Origin()) {
                                        return false
                                }
                                for i, xa := range xargs {
index 66a69521d2894a9348b57ba33e48eed4f7002de2..d4ce0a7fcd0a4d362fa356a3785538833c7ad057 100644 (file)
@@ -30,7 +30,7 @@ func TestSizeof(t *testing.T) {
                {Interface{}, 40, 80},
                {Map{}, 16, 32},
                {Chan{}, 12, 24},
-               {Named{}, 68, 128},
+               {Named{}, 60, 112},
                {TypeParam{}, 28, 48},
                {term{}, 12, 24},
 
index 63849b921241ef70cb1f56a1ded477b2c2b70748..110298cbaeea3e5482f63ebb634855fb506709b4 100644 (file)
@@ -176,7 +176,7 @@ func (subst *subster) typ(typ Type) Type {
                        // In this case the interface will not be substituted here, because its
                        // method signatures do not depend on the type parameter P, but we still
                        // need to create new interface methods to hold the instantiated
-                       // receiver. This is handled by expandNamed.
+                       // receiver. This is handled by Named.expandUnderlying.
                        iface.methods, _ = replaceRecvType(methods, t, iface)
                        return iface
                }
@@ -207,19 +207,20 @@ func (subst *subster) typ(typ Type) Type {
                        }
                }
 
-               // subst is called by expandNamed, so in this function we need to be
+               // subst is called during expansion, so in this function we need to be
                // careful not to call any methods that would cause t to be expanded: doing
                // so would result in deadlock.
                //
-               // So we call t.orig.TypeParams() rather than t.TypeParams() here and
-               // below.
-               if t.orig.TypeParams().Len() == 0 {
+               // So we call t.Origin().TypeParams() rather than t.TypeParams().
+               orig := t.Origin()
+               n := orig.TypeParams().Len()
+               if n == 0 {
                        dump(">>> %s is not parameterized", t)
                        return t // type is not parameterized
                }
 
                var newTArgs []Type
-               if t.targs.Len() != t.orig.TypeParams().Len() {
+               if t.TypeArgs().Len() != n {
                        return Typ[Invalid] // error reported elsewhere
                }
 
@@ -228,14 +229,14 @@ func (subst *subster) typ(typ Type) Type {
                // For each (existing) type argument targ, determine if it needs
                // to be substituted; i.e., if it is or contains a type parameter
                // that has a type argument for it.
-               for i, targ := range t.targs.list() {
+               for i, targ := range t.TypeArgs().list() {
                        dump(">>> %d targ = %s", i, targ)
                        new_targ := subst.typ(targ)
                        if new_targ != targ {
                                dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
                                if newTArgs == nil {
-                                       newTArgs = make([]Type, t.orig.TypeParams().Len())
-                                       copy(newTArgs, t.targs.list())
+                                       newTArgs = make([]Type, n)
+                                       copy(newTArgs, t.TypeArgs().list())
                                }
                                newTArgs[i] = new_targ
                        }
@@ -247,9 +248,9 @@ func (subst *subster) typ(typ Type) Type {
                }
 
                // before creating a new named type, check if we have this one already
-               h := subst.ctxt.instanceHash(t.orig, newTArgs)
+               h := subst.ctxt.instanceHash(orig, newTArgs)
                dump(">>> new type hash: %s", h)
-               if named := subst.ctxt.lookup(h, t.orig, newTArgs); named != nil {
+               if named := subst.ctxt.lookup(h, orig, newTArgs); named != nil {
                        dump(">>> found %s", named)
                        return named
                }
@@ -258,7 +259,7 @@ func (subst *subster) typ(typ Type) Type {
                // recursion. The position used here is irrelevant because validation only
                // occurs on t (we don't call validType on named), but we use subst.pos to
                // help with debugging.
-               return subst.check.instance(subst.pos, t.orig, newTArgs, subst.ctxt)
+               return subst.check.instance(subst.pos, orig, newTArgs, subst.ctxt)
 
                // Note that if we were to expose substitution more generally (not just in
                // the context of a declaration), we'd have to substitute in
index 0325d4a77f5c16385a6e982023375761abaa8718..5a2e2c171a98ebf953f024db010502f531f341db 100644 (file)
@@ -285,9 +285,9 @@ func (w *typeWriter) typ(typ Type) {
                        w.string(strconv.Itoa(w.ctxt.getID(t)))
                }
                w.typeName(t.obj) // when hashing written for readability of the hash only
-               if t.targs != nil {
+               if t.inst != nil {
                        // instantiated type
-                       w.typeList(t.targs.list())
+                       w.typeList(t.inst.targs.list())
                } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
                        // parameterized type
                        w.tParamList(t.TypeParams().list())
index c7161e00a550123a856df8d6440e9fc656316614..05bd51a82b2b4b8628f537c72c9fbd082e38ddf0 100644 (file)
@@ -428,8 +428,8 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re
                // errors.
                check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst)
 
-               if check.validateTArgLen(ix.Pos(), inst.TypeParams().Len(), inst.targs.Len()) {
-                       if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.targs.list()); err != nil {
+               if check.validateTArgLen(ix.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) {
+                       if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list()); err != nil {
                                // best position for error reporting
                                pos := ix.Pos()
                                if i < len(ix.Indices) {
@@ -437,7 +437,7 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re
                                }
                                check.softErrorf(atPos(pos), _InvalidTypeArg, err.Error())
                        } else {
-                               check.mono.recordInstance(check.pkg, ix.Pos(), inst.TypeParams().list(), inst.targs.list(), ix.Indices)
+                               check.mono.recordInstance(check.pkg, ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), ix.Indices)
                        }
                }
 
index 0742e40d8bd9d23db34efe547d9318f8c40a5f7a..602e304b4a2c0cebc74ae20f31511d6f0d98c7ff 100644 (file)
@@ -546,8 +546,8 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
        case *Named:
                // TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate.
                if y, ok := y.(*Named); ok {
-                       xargs := x.targs.list()
-                       yargs := y.targs.list()
+                       xargs := x.TypeArgs().list()
+                       yargs := y.TypeArgs().list()
 
                        if len(xargs) != len(yargs) {
                                return false
index edb4c02ecdacd86408c2c41075dceb4d90e09c56..0d7a0f308c323f0d597521e616d5c437b75c5aba 100644 (file)
@@ -71,7 +71,7 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn
                switch check.infoMap[t] {
                case unknown:
                        check.infoMap[t] = marked
-                       check.infoMap[t] = check.validType0(t.orig.fromRHS, env.push(t), append(path, t.obj))
+                       check.infoMap[t] = check.validType0(t.Origin().fromRHS, env.push(t), append(path, t.obj))
                case marked:
                        // We have seen type t before and thus must have a cycle.
                        check.infoMap[t] = invalid