}
// 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 {
// 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.
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
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()
package types2
import (
+ "cmd/compile/internal/syntax"
"sync"
"sync/atomic"
)
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
// 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
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.
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)
}
}
// 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
}
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.
//
// 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.
// 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 {
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)
}
}
// 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
// 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)
}
// 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")
}
// 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)
// 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
}
}()
}
- 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
iface.methods = methods
}
}
+
return underlying
}
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.
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 {
{Interface{}, 40, 80},
{Map{}, 16, 32},
{Chan{}, 12, 24},
- {Named{}, 68, 128},
+ {Named{}, 60, 112},
{TypeParam{}, 28, 48},
{term{}, 12, 24},
// 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
}
}
}
- // 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
}
// 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
}
}
// 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
}
// 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
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())
// 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) {
}
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)
}
}
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
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
}
// 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 {
// 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.
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
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()
package types
import (
+ "go/token"
"sync"
"sync/atomic"
)
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
// 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
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.
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)
}
}
// 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
}
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.
//
// 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.
// 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 {
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)
}
}
// 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
// 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)
}
// 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")
}
// 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)
// 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
}
}()
}
- 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
iface.methods = methods
}
}
+
return underlying
}
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.
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 {
{Interface{}, 40, 80},
{Map{}, 16, 32},
{Chan{}, 12, 24},
- {Named{}, 68, 128},
+ {Named{}, 60, 112},
{TypeParam{}, 28, 48},
{term{}, 12, 24},
// 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
}
}
}
- // 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
}
// 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
}
}
// 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
}
// 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
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())
// 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) {
}
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)
}
}
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
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