mirror of
https://github.com/golang/go.git
synced 2025-05-25 17:31:22 +00:00
go/types, types2: store Named instance information separately
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>
This commit is contained in:
parent
1323b0e8f0
commit
02e69cfa96
@ -508,7 +508,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
|
|||||||
}
|
}
|
||||||
|
|
||||||
// type definition or generic type declaration
|
// type definition or generic type declaration
|
||||||
named := check.newNamed(obj, nil, nil, nil)
|
named := check.newNamed(obj, nil, nil)
|
||||||
def.setUnderlying(named)
|
def.setUnderlying(named)
|
||||||
|
|
||||||
if tdecl.TParamList != nil {
|
if tdecl.TParamList != nil {
|
||||||
@ -635,7 +635,7 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
|||||||
// and field names must be distinct."
|
// and field names must be distinct."
|
||||||
base, _ := obj.typ.(*Named) // shouldn't fail but be conservative
|
base, _ := obj.typ.(*Named) // shouldn't fail but be conservative
|
||||||
if base != nil {
|
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
|
// See issue #52529: we must delay the expansion of underlying here, as
|
||||||
// base may not be fully set-up.
|
// base may not be fully set-up.
|
||||||
|
@ -434,7 +434,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
|
|||||||
return w.isParameterized(t.elem)
|
return w.isParameterized(t.elem)
|
||||||
|
|
||||||
case *Named:
|
case *Named:
|
||||||
return w.isParameterizedTypeList(t.targs.list())
|
return w.isParameterizedTypeList(t.TypeArgs().list())
|
||||||
|
|
||||||
case *TypeParam:
|
case *TypeParam:
|
||||||
// t must be one of w.tparams
|
// t must be one of w.tparams
|
||||||
|
@ -76,10 +76,7 @@ func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, ctxt *Co
|
|||||||
|
|
||||||
switch orig := orig.(type) {
|
switch orig := orig.(type) {
|
||||||
case *Named:
|
case *Named:
|
||||||
tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
|
res = check.newNamedInstance(pos, orig, targs)
|
||||||
named := check.newNamed(tname, orig, nil, nil) // underlying, tparams, and methods are set when named is resolved
|
|
||||||
named.targs = newTypeList(targs)
|
|
||||||
res = named
|
|
||||||
|
|
||||||
case *Signature:
|
case *Signature:
|
||||||
tparams := orig.TypeParams()
|
tparams := orig.TypeParams()
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package types2
|
package types2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
@ -83,14 +84,15 @@ import (
|
|||||||
type Named struct {
|
type Named struct {
|
||||||
check *Checker // non-nil during type-checking; nil otherwise
|
check *Checker // non-nil during type-checking; nil otherwise
|
||||||
obj *TypeName // corresponding declared object for declared types; see above for instantiated types
|
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
|
// 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
|
// from (for cycle reporting). Only used by validType, and therefore does not
|
||||||
// require synchronization.
|
// require synchronization.
|
||||||
fromRHS Type
|
fromRHS Type
|
||||||
|
|
||||||
|
// information for instantiated types; nil otherwise
|
||||||
|
inst *instance
|
||||||
|
|
||||||
mu sync.Mutex // guards all fields below
|
mu sync.Mutex // guards all fields below
|
||||||
state_ uint32 // the current state of this type; must only be accessed atomically
|
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
|
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
|
// instantiated types, methods are individually expanded when they are first
|
||||||
// accessed.
|
// accessed.
|
||||||
methods []*Func
|
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 may be provided to lazily load type parameters, underlying type, and methods.
|
||||||
loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func)
|
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.
|
// namedState represents the possible states that a named type may assume.
|
||||||
type namedState uint32
|
type namedState uint32
|
||||||
|
|
||||||
@ -125,7 +133,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
|||||||
if _, ok := underlying.(*Named); ok {
|
if _, ok := underlying.(*Named); ok {
|
||||||
panic("underlying type must not be *Named")
|
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.
|
// resolve resolves the type parameters, methods, and underlying type of n.
|
||||||
@ -149,19 +157,20 @@ func (n *Named) resolve(ctxt *Context) *Named {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.TypeArgs().Len() > 0 {
|
if n.inst != nil {
|
||||||
assert(n.underlying == nil) // n is an unresolved instance
|
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
|
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)
|
underlying := n.expandUnderlying(ctxt)
|
||||||
|
|
||||||
n.tparams = n.orig.tparams
|
n.tparams = orig.tparams
|
||||||
n.underlying = underlying
|
n.underlying = underlying
|
||||||
n.fromRHS = n.orig.fromRHS // for cycle detection
|
n.fromRHS = orig.fromRHS // for cycle detection
|
||||||
|
|
||||||
if len(n.orig.methods) == 0 {
|
if len(orig.methods) == 0 {
|
||||||
n.setState(complete)
|
n.setState(complete) // nothing further to do
|
||||||
} else {
|
} else {
|
||||||
n.setState(resolved)
|
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.
|
// 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 {
|
func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||||
typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods}
|
typ := &Named{check: check, obj: obj, fromRHS: underlying, underlying: underlying, methods: methods}
|
||||||
if typ.orig == nil {
|
|
||||||
typ.orig = typ
|
|
||||||
}
|
|
||||||
if obj.typ == nil {
|
if obj.typ == nil {
|
||||||
obj.typ = typ
|
obj.typ = typ
|
||||||
}
|
}
|
||||||
@ -219,8 +225,22 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, meth
|
|||||||
return 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() {
|
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
|
// Ensure that every defined type created in the course of type-checking has
|
||||||
// either non-*Named underlying type, or is unexpanded.
|
// 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
|
// 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.
|
// 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
|
// Origin returns the generic type from which the named type t is
|
||||||
// instantiated. If t is not an instantiated type, the result is t.
|
// instantiated. If t is not an instantiated type, the result is t.
|
||||||
func (t *Named) Origin() *Named { return t.orig }
|
func (t *Named) Origin() *Named {
|
||||||
|
if t.inst == nil {
|
||||||
// TODO(gri) Come up with a better representation and API to distinguish
|
return t
|
||||||
// between parameterized instantiated and non-instantiated types.
|
}
|
||||||
|
return t.inst.orig
|
||||||
|
}
|
||||||
|
|
||||||
// TypeParams returns the type parameters of the named type t, or nil.
|
// 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.
|
// 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.
|
// SetTypeParams sets the type parameters of the named type t.
|
||||||
// t must not have type arguments.
|
// t must not have type arguments.
|
||||||
func (t *Named) SetTypeParams(tparams []*TypeParam) {
|
func (t *Named) SetTypeParams(tparams []*TypeParam) {
|
||||||
assert(t.targs.Len() == 0)
|
assert(t.inst == nil)
|
||||||
t.resolve(nil).tparams = bindTParams(tparams)
|
t.resolve(nil).tparams = bindTParams(tparams)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeArgs returns the type arguments used to instantiate the named type t.
|
// 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.
|
// NumMethods returns the number of explicit methods defined for t.
|
||||||
//
|
//
|
||||||
// For an ordinary or instantiated type t, the receiver base type of these
|
// 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
|
// methods will be the named type t. For an uninstantiated generic type t, each
|
||||||
// method receiver will be instantiated with its receiver type parameters.
|
// 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().
|
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||||
func (t *Named) Method(i int) *Func {
|
func (t *Named) Method(i int) *Func {
|
||||||
@ -280,23 +314,24 @@ func (t *Named) Method(i int) *Func {
|
|||||||
return t.methods[i]
|
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()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
|
|
||||||
if len(t.methods) != len(t.orig.methods) {
|
if len(t.methods) != len(orig.methods) {
|
||||||
assert(len(t.methods) == 0)
|
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 {
|
if t.methods[i] == nil {
|
||||||
t.methods[i] = t.expandMethod(i)
|
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
|
// Check if we've created all methods at this point. If we have, mark the
|
||||||
// type as fully expanded.
|
// type as fully expanded.
|
||||||
if t.expandedMethods == len(t.orig.methods) {
|
if t.inst.expandedMethods == len(orig.methods) {
|
||||||
t.setState(complete)
|
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
|
// expandMethod substitutes type arguments in the i'th method for an
|
||||||
// instantiated receiver.
|
// instantiated receiver.
|
||||||
func (t *Named) expandMethod(i int) *Func {
|
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
|
// t.orig.methods is not lazy. origm is the method instantiated with its
|
||||||
// receiver type parameters (the "origin" method).
|
// receiver type parameters (the "origin" method).
|
||||||
origm := t.orig.Method(i)
|
origm := t.inst.orig.Method(i)
|
||||||
assert(origm != nil)
|
assert(origm != nil)
|
||||||
|
|
||||||
check := t.check
|
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
|
// We can only substitute if we have a correspondence between type arguments
|
||||||
// and type parameters. This check is necessary in the presence of invalid
|
// and type parameters. This check is necessary in the presence of invalid
|
||||||
// code.
|
// code.
|
||||||
if origSig.RecvTypeParams().Len() == t.targs.Len() {
|
if origSig.RecvTypeParams().Len() == t.inst.targs.Len() {
|
||||||
ctxt := check.bestContext(nil)
|
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)
|
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.
|
// SetUnderlying sets the underlying type and marks t as complete.
|
||||||
// t must not have type arguments.
|
// t must not have type arguments.
|
||||||
func (t *Named) SetUnderlying(underlying Type) {
|
func (t *Named) SetUnderlying(underlying Type) {
|
||||||
assert(t.targs.Len() == 0)
|
assert(t.inst == nil)
|
||||||
if underlying == nil {
|
if underlying == nil {
|
||||||
panic("underlying type must not be 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.
|
// AddMethod adds method m unless it is already in the method list.
|
||||||
// t must not have type arguments.
|
// t must not have type arguments.
|
||||||
func (t *Named) AddMethod(m *Func) {
|
func (t *Named) AddMethod(m *Func) {
|
||||||
assert(t.targs.Len() == 0)
|
assert(t.inst == nil)
|
||||||
t.resolve(nil)
|
t.resolve(nil)
|
||||||
if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 {
|
if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 {
|
||||||
t.methods = append(t.methods, m)
|
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.
|
// 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
|
// Look up the method index in orig, and only instantiate method at the
|
||||||
// matching index (if any).
|
// matching index (if any).
|
||||||
i, _ := lookupMethod(n.orig.methods, pkg, name, foldCase)
|
i, _ := lookupMethod(n.Origin().methods, pkg, name, foldCase)
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return -1, nil
|
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
|
// We should only get a Named underlying type here during type checking
|
||||||
// (for example, in recursive type declarations).
|
// (for example, in recursive type declarations).
|
||||||
assert(check != nil)
|
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.
|
// Mismatching arg and tparam length may be checked elsewhere.
|
||||||
return Typ[Invalid]
|
return Typ[Invalid]
|
||||||
}
|
}
|
||||||
|
|
||||||
// We must always have a context, to avoid infinite recursion.
|
// We must always have a context, to avoid infinite recursion.
|
||||||
ctxt = check.bestContext(ctxt)
|
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.
|
// 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())
|
smap := makeSubstMap(orig.tparams.list(), targs.list())
|
||||||
underlying := n.check.subst(n.obj.pos, n.orig.underlying, smap, ctxt)
|
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
|
// 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
|
// of its methods accurately -- we set the receiver of interface methods on
|
||||||
// the RHS of a type declaration to the defined type.
|
// the RHS of a type declaration to the defined type.
|
||||||
if iface, _ := underlying.(*Interface); iface != nil {
|
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
|
// 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
|
// possible that it wasn't substituted. In this case we need to create
|
||||||
// a new *Interface before modifying receivers.
|
// a new *Interface before modifying receivers.
|
||||||
if iface == n.orig.underlying {
|
if iface == orig.underlying {
|
||||||
old := iface
|
old := iface
|
||||||
iface = check.newInterface()
|
iface = check.newInterface()
|
||||||
iface.embeddeds = old.embeddeds
|
iface.embeddeds = old.embeddeds
|
||||||
@ -571,6 +607,7 @@ func (n *Named) expandUnderlying(ctxt *Context) Type {
|
|||||||
iface.methods = methods
|
iface.methods = methods
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return underlying
|
return underlying
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ func isTypeParam(t Type) bool {
|
|||||||
func isGeneric(t Type) bool {
|
func isGeneric(t Type) bool {
|
||||||
// A parameterized type is only generic if it doesn't have an instantiation already.
|
// A parameterized type is only generic if it doesn't have an instantiation already.
|
||||||
named, _ := t.(*Named)
|
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.
|
// 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 {
|
if len(xargs) > 0 {
|
||||||
// Instances are identical if their original type and type arguments
|
// Instances are identical if their original type and type arguments
|
||||||
// are identical.
|
// are identical.
|
||||||
if !Identical(x.orig, y.orig) {
|
if !Identical(x.Origin(), y.Origin()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i, xa := range xargs {
|
for i, xa := range xargs {
|
||||||
|
@ -31,7 +31,7 @@ func TestSizeof(t *testing.T) {
|
|||||||
{Interface{}, 40, 80},
|
{Interface{}, 40, 80},
|
||||||
{Map{}, 16, 32},
|
{Map{}, 16, 32},
|
||||||
{Chan{}, 12, 24},
|
{Chan{}, 12, 24},
|
||||||
{Named{}, 68, 128},
|
{Named{}, 60, 112},
|
||||||
{TypeParam{}, 28, 48},
|
{TypeParam{}, 28, 48},
|
||||||
{term{}, 12, 24},
|
{term{}, 12, 24},
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ func (subst *subster) typ(typ Type) Type {
|
|||||||
// In this case the interface will not be substituted here, because its
|
// 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
|
// method signatures do not depend on the type parameter P, but we still
|
||||||
// need to create new interface methods to hold the instantiated
|
// 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)
|
iface.methods, _ = replaceRecvType(methods, t, iface)
|
||||||
return 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
|
// careful not to call any methods that would cause t to be expanded: doing
|
||||||
// so would result in deadlock.
|
// so would result in deadlock.
|
||||||
//
|
//
|
||||||
// So we call t.orig.TypeParams() rather than t.TypeParams() here and
|
// So we call t.Origin().TypeParams() rather than t.TypeParams().
|
||||||
// below.
|
orig := t.Origin()
|
||||||
if t.orig.TypeParams().Len() == 0 {
|
n := orig.TypeParams().Len()
|
||||||
|
if n == 0 {
|
||||||
dump(">>> %s is not parameterized", t)
|
dump(">>> %s is not parameterized", t)
|
||||||
return t // type is not parameterized
|
return t // type is not parameterized
|
||||||
}
|
}
|
||||||
|
|
||||||
var newTArgs []Type
|
var newTArgs []Type
|
||||||
if t.targs.Len() != t.orig.TypeParams().Len() {
|
if t.TypeArgs().Len() != n {
|
||||||
return Typ[Invalid] // error reported elsewhere
|
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
|
// For each (existing) type argument targ, determine if it needs
|
||||||
// to be substituted; i.e., if it is or contains a type parameter
|
// to be substituted; i.e., if it is or contains a type parameter
|
||||||
// that has a type argument for it.
|
// 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)
|
dump(">>> %d targ = %s", i, targ)
|
||||||
new_targ := subst.typ(targ)
|
new_targ := subst.typ(targ)
|
||||||
if new_targ != targ {
|
if new_targ != targ {
|
||||||
dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
|
dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
|
||||||
if newTArgs == nil {
|
if newTArgs == nil {
|
||||||
newTArgs = make([]Type, t.orig.TypeParams().Len())
|
newTArgs = make([]Type, n)
|
||||||
copy(newTArgs, t.targs.list())
|
copy(newTArgs, t.TypeArgs().list())
|
||||||
}
|
}
|
||||||
newTArgs[i] = new_targ
|
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
|
// 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)
|
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)
|
dump(">>> found %s", named)
|
||||||
return named
|
return named
|
||||||
}
|
}
|
||||||
@ -258,7 +259,7 @@ func (subst *subster) typ(typ Type) Type {
|
|||||||
// recursion. The position used here is irrelevant because validation only
|
// 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
|
// occurs on t (we don't call validType on named), but we use subst.pos to
|
||||||
// help with debugging.
|
// 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
|
// Note that if we were to expose substitution more generally (not just in
|
||||||
// the context of a declaration), we'd have to substitute in
|
// the context of a declaration), we'd have to substitute in
|
||||||
|
@ -284,9 +284,9 @@ func (w *typeWriter) typ(typ Type) {
|
|||||||
w.string(strconv.Itoa(w.ctxt.getID(t)))
|
w.string(strconv.Itoa(w.ctxt.getID(t)))
|
||||||
}
|
}
|
||||||
w.typeName(t.obj) // when hashing written for readability of the hash only
|
w.typeName(t.obj) // when hashing written for readability of the hash only
|
||||||
if t.targs != nil {
|
if t.inst != nil {
|
||||||
// instantiated type
|
// 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
|
} else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
|
||||||
// parameterized type
|
// parameterized type
|
||||||
w.tParamList(t.TypeParams().list())
|
w.tParamList(t.TypeParams().list())
|
||||||
|
@ -444,8 +444,8 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
|
|||||||
// errors.
|
// errors.
|
||||||
check.recordInstance(x, inst.TypeArgs().list(), inst)
|
check.recordInstance(x, inst.TypeArgs().list(), inst)
|
||||||
|
|
||||||
if check.validateTArgLen(x.Pos(), inst.TypeParams().Len(), inst.targs.Len()) {
|
if check.validateTArgLen(x.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) {
|
||||||
if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.targs.list()); err != nil {
|
if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list()); err != nil {
|
||||||
// best position for error reporting
|
// best position for error reporting
|
||||||
pos := x.Pos()
|
pos := x.Pos()
|
||||||
if i < len(xlist) {
|
if i < len(xlist) {
|
||||||
@ -453,7 +453,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
|
|||||||
}
|
}
|
||||||
check.softErrorf(pos, "%s", err)
|
check.softErrorf(pos, "%s", err)
|
||||||
} else {
|
} 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,8 +546,8 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
|
|||||||
case *Named:
|
case *Named:
|
||||||
// TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate.
|
// TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate.
|
||||||
if y, ok := y.(*Named); ok {
|
if y, ok := y.(*Named); ok {
|
||||||
xargs := x.targs.list()
|
xargs := x.TypeArgs().list()
|
||||||
yargs := y.targs.list()
|
yargs := y.TypeArgs().list()
|
||||||
|
|
||||||
if len(xargs) != len(yargs) {
|
if len(xargs) != len(yargs) {
|
||||||
return false
|
return false
|
||||||
|
@ -72,7 +72,7 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn
|
|||||||
switch check.infoMap[t] {
|
switch check.infoMap[t] {
|
||||||
case unknown:
|
case unknown:
|
||||||
check.infoMap[t] = marked
|
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:
|
case marked:
|
||||||
// We have seen type t before and thus must have a cycle.
|
// We have seen type t before and thus must have a cycle.
|
||||||
check.infoMap[t] = invalid
|
check.infoMap[t] = invalid
|
||||||
|
@ -565,7 +565,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// type definition or generic type declaration
|
// type definition or generic type declaration
|
||||||
named := check.newNamed(obj, nil, nil, nil)
|
named := check.newNamed(obj, nil, nil)
|
||||||
def.setUnderlying(named)
|
def.setUnderlying(named)
|
||||||
|
|
||||||
if tdecl.TypeParams != nil {
|
if tdecl.TypeParams != nil {
|
||||||
@ -711,7 +711,7 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
|||||||
// and field names must be distinct."
|
// and field names must be distinct."
|
||||||
base, _ := obj.typ.(*Named) // shouldn't fail but be conservative
|
base, _ := obj.typ.(*Named) // shouldn't fail but be conservative
|
||||||
if base != nil {
|
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
|
// See issue #52529: we must delay the expansion of underlying here, as
|
||||||
// base may not be fully set-up.
|
// base may not be fully set-up.
|
||||||
|
@ -434,7 +434,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
|
|||||||
return w.isParameterized(t.elem)
|
return w.isParameterized(t.elem)
|
||||||
|
|
||||||
case *Named:
|
case *Named:
|
||||||
return w.isParameterizedTypeList(t.targs.list())
|
return w.isParameterizedTypeList(t.TypeArgs().list())
|
||||||
|
|
||||||
case *TypeParam:
|
case *TypeParam:
|
||||||
// t must be one of w.tparams
|
// t must be one of w.tparams
|
||||||
|
@ -76,10 +76,7 @@ func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, ctxt *Con
|
|||||||
|
|
||||||
switch orig := orig.(type) {
|
switch orig := orig.(type) {
|
||||||
case *Named:
|
case *Named:
|
||||||
tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
|
res = check.newNamedInstance(pos, orig, targs)
|
||||||
named := check.newNamed(tname, orig, nil, nil) // underlying, tparams, and methods are set when named is resolved
|
|
||||||
named.targs = newTypeList(targs)
|
|
||||||
res = named
|
|
||||||
|
|
||||||
case *Signature:
|
case *Signature:
|
||||||
tparams := orig.TypeParams()
|
tparams := orig.TypeParams()
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/token"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
@ -83,14 +84,15 @@ import (
|
|||||||
type Named struct {
|
type Named struct {
|
||||||
check *Checker // non-nil during type-checking; nil otherwise
|
check *Checker // non-nil during type-checking; nil otherwise
|
||||||
obj *TypeName // corresponding declared object for declared types; see above for instantiated types
|
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
|
// 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
|
// from (for cycle reporting). Only used by validType, and therefore does not
|
||||||
// require synchronization.
|
// require synchronization.
|
||||||
fromRHS Type
|
fromRHS Type
|
||||||
|
|
||||||
|
// information for instantiated types; nil otherwise
|
||||||
|
inst *instance
|
||||||
|
|
||||||
mu sync.Mutex // guards all fields below
|
mu sync.Mutex // guards all fields below
|
||||||
state_ uint32 // the current state of this type; must only be accessed atomically
|
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
|
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
|
// instantiated types, methods are individually expanded when they are first
|
||||||
// accessed.
|
// accessed.
|
||||||
methods []*Func
|
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 may be provided to lazily load type parameters, underlying type, and methods.
|
||||||
loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func)
|
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.
|
// namedState represents the possible states that a named type may assume.
|
||||||
type namedState uint32
|
type namedState uint32
|
||||||
|
|
||||||
@ -125,7 +133,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
|||||||
if _, ok := underlying.(*Named); ok {
|
if _, ok := underlying.(*Named); ok {
|
||||||
panic("underlying type must not be *Named")
|
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.
|
// resolve resolves the type parameters, methods, and underlying type of n.
|
||||||
@ -149,19 +157,20 @@ func (n *Named) resolve(ctxt *Context) *Named {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.TypeArgs().Len() > 0 {
|
if n.inst != nil {
|
||||||
assert(n.underlying == nil) // n is an unresolved instance
|
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
|
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)
|
underlying := n.expandUnderlying(ctxt)
|
||||||
|
|
||||||
n.tparams = n.orig.tparams
|
n.tparams = orig.tparams
|
||||||
n.underlying = underlying
|
n.underlying = underlying
|
||||||
n.fromRHS = n.orig.fromRHS // for cycle detection
|
n.fromRHS = orig.fromRHS // for cycle detection
|
||||||
|
|
||||||
if len(n.orig.methods) == 0 {
|
if len(orig.methods) == 0 {
|
||||||
n.setState(complete)
|
n.setState(complete) // nothing further to do
|
||||||
} else {
|
} else {
|
||||||
n.setState(resolved)
|
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.
|
// 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 {
|
func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||||
typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods}
|
typ := &Named{check: check, obj: obj, fromRHS: underlying, underlying: underlying, methods: methods}
|
||||||
if typ.orig == nil {
|
|
||||||
typ.orig = typ
|
|
||||||
}
|
|
||||||
if obj.typ == nil {
|
if obj.typ == nil {
|
||||||
obj.typ = typ
|
obj.typ = typ
|
||||||
}
|
}
|
||||||
@ -219,8 +225,22 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, meth
|
|||||||
return 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() {
|
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
|
// Ensure that every defined type created in the course of type-checking has
|
||||||
// either non-*Named underlying type, or is unexpanded.
|
// 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
|
// 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.
|
// instantiated types, this is same as the type name of the origin type.
|
||||||
func (t *Named) Obj() *TypeName {
|
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
|
// Origin returns the generic type from which the named type t is
|
||||||
// instantiated. If t is not an instantiated type, the result is t.
|
// instantiated. If t is not an instantiated type, the result is t.
|
||||||
func (t *Named) Origin() *Named { return t.orig }
|
func (t *Named) Origin() *Named {
|
||||||
|
if t.inst == nil {
|
||||||
// TODO(gri) Come up with a better representation and API to distinguish
|
return t
|
||||||
// between parameterized instantiated and non-instantiated types.
|
}
|
||||||
|
return t.inst.orig
|
||||||
|
}
|
||||||
|
|
||||||
// TypeParams returns the type parameters of the named type t, or nil.
|
// 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.
|
// 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.
|
// SetTypeParams sets the type parameters of the named type t.
|
||||||
// t must not have type arguments.
|
// t must not have type arguments.
|
||||||
func (t *Named) SetTypeParams(tparams []*TypeParam) {
|
func (t *Named) SetTypeParams(tparams []*TypeParam) {
|
||||||
assert(t.targs.Len() == 0)
|
assert(t.inst == nil)
|
||||||
t.resolve(nil).tparams = bindTParams(tparams)
|
t.resolve(nil).tparams = bindTParams(tparams)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeArgs returns the type arguments used to instantiate the named type t.
|
// 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.
|
// NumMethods returns the number of explicit methods defined for t.
|
||||||
//
|
//
|
||||||
// For an ordinary or instantiated type t, the receiver base type of these
|
// 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
|
// methods will be the named type t. For an uninstantiated generic type t, each
|
||||||
// method receiver will be instantiated with its receiver type parameters.
|
// 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().
|
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||||
func (t *Named) Method(i int) *Func {
|
func (t *Named) Method(i int) *Func {
|
||||||
@ -282,23 +314,24 @@ func (t *Named) Method(i int) *Func {
|
|||||||
return t.methods[i]
|
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()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
|
|
||||||
if len(t.methods) != len(t.orig.methods) {
|
if len(t.methods) != len(orig.methods) {
|
||||||
assert(len(t.methods) == 0)
|
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 {
|
if t.methods[i] == nil {
|
||||||
t.methods[i] = t.expandMethod(i)
|
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
|
// Check if we've created all methods at this point. If we have, mark the
|
||||||
// type as fully expanded.
|
// type as fully expanded.
|
||||||
if t.expandedMethods == len(t.orig.methods) {
|
if t.inst.expandedMethods == len(orig.methods) {
|
||||||
t.setState(complete)
|
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
|
// expandMethod substitutes type arguments in the i'th method for an
|
||||||
// instantiated receiver.
|
// instantiated receiver.
|
||||||
func (t *Named) expandMethod(i int) *Func {
|
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
|
// t.orig.methods is not lazy. origm is the method instantiated with its
|
||||||
// receiver type parameters (the "origin" method).
|
// receiver type parameters (the "origin" method).
|
||||||
origm := t.orig.Method(i)
|
origm := t.inst.orig.Method(i)
|
||||||
assert(origm != nil)
|
assert(origm != nil)
|
||||||
|
|
||||||
check := t.check
|
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
|
// We can only substitute if we have a correspondence between type arguments
|
||||||
// and type parameters. This check is necessary in the presence of invalid
|
// and type parameters. This check is necessary in the presence of invalid
|
||||||
// code.
|
// code.
|
||||||
if origSig.RecvTypeParams().Len() == t.targs.Len() {
|
if origSig.RecvTypeParams().Len() == t.inst.targs.Len() {
|
||||||
ctxt := check.bestContext(nil)
|
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)
|
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.
|
// SetUnderlying sets the underlying type and marks t as complete.
|
||||||
// t must not have type arguments.
|
// t must not have type arguments.
|
||||||
func (t *Named) SetUnderlying(underlying Type) {
|
func (t *Named) SetUnderlying(underlying Type) {
|
||||||
assert(t.targs.Len() == 0)
|
assert(t.inst == nil)
|
||||||
if underlying == nil {
|
if underlying == nil {
|
||||||
panic("underlying type must not be 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.
|
// AddMethod adds method m unless it is already in the method list.
|
||||||
// t must not have type arguments.
|
// t must not have type arguments.
|
||||||
func (t *Named) AddMethod(m *Func) {
|
func (t *Named) AddMethod(m *Func) {
|
||||||
assert(t.targs.Len() == 0)
|
assert(t.inst == nil)
|
||||||
t.resolve(nil)
|
t.resolve(nil)
|
||||||
if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 {
|
if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 {
|
||||||
t.methods = append(t.methods, m)
|
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.
|
// 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
|
// Look up the method index in orig, and only instantiate method at the
|
||||||
// matching index (if any).
|
// matching index (if any).
|
||||||
i, _ := lookupMethod(n.orig.methods, pkg, name, foldCase)
|
i, _ := lookupMethod(n.Origin().methods, pkg, name, foldCase)
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return -1, nil
|
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
|
// We should only get a Named underlying type here during type checking
|
||||||
// (for example, in recursive type declarations).
|
// (for example, in recursive type declarations).
|
||||||
assert(check != nil)
|
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.
|
// Mismatching arg and tparam length may be checked elsewhere.
|
||||||
return Typ[Invalid]
|
return Typ[Invalid]
|
||||||
}
|
}
|
||||||
|
|
||||||
// We must always have a context, to avoid infinite recursion.
|
// We must always have a context, to avoid infinite recursion.
|
||||||
ctxt = check.bestContext(ctxt)
|
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.
|
// 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())
|
smap := makeSubstMap(orig.tparams.list(), targs.list())
|
||||||
underlying := n.check.subst(n.obj.pos, n.orig.underlying, smap, ctxt)
|
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
|
// 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
|
// of its methods accurately -- we set the receiver of interface methods on
|
||||||
// the RHS of a type declaration to the defined type.
|
// the RHS of a type declaration to the defined type.
|
||||||
if iface, _ := underlying.(*Interface); iface != nil {
|
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
|
// 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
|
// possible that it wasn't substituted. In this case we need to create
|
||||||
// a new *Interface before modifying receivers.
|
// a new *Interface before modifying receivers.
|
||||||
if iface == n.orig.underlying {
|
if iface == orig.underlying {
|
||||||
old := iface
|
old := iface
|
||||||
iface = check.newInterface()
|
iface = check.newInterface()
|
||||||
iface.embeddeds = old.embeddeds
|
iface.embeddeds = old.embeddeds
|
||||||
@ -573,6 +607,7 @@ func (n *Named) expandUnderlying(ctxt *Context) Type {
|
|||||||
iface.methods = methods
|
iface.methods = methods
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return underlying
|
return underlying
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ func isTypeParam(t Type) bool {
|
|||||||
func isGeneric(t Type) bool {
|
func isGeneric(t Type) bool {
|
||||||
// A parameterized type is only generic if it doesn't have an instantiation already.
|
// A parameterized type is only generic if it doesn't have an instantiation already.
|
||||||
named, _ := t.(*Named)
|
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.
|
// 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 {
|
if len(xargs) > 0 {
|
||||||
// Instances are identical if their original type and type arguments
|
// Instances are identical if their original type and type arguments
|
||||||
// are identical.
|
// are identical.
|
||||||
if !Identical(x.orig, y.orig) {
|
if !Identical(x.Origin(), y.Origin()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i, xa := range xargs {
|
for i, xa := range xargs {
|
||||||
|
@ -30,7 +30,7 @@ func TestSizeof(t *testing.T) {
|
|||||||
{Interface{}, 40, 80},
|
{Interface{}, 40, 80},
|
||||||
{Map{}, 16, 32},
|
{Map{}, 16, 32},
|
||||||
{Chan{}, 12, 24},
|
{Chan{}, 12, 24},
|
||||||
{Named{}, 68, 128},
|
{Named{}, 60, 112},
|
||||||
{TypeParam{}, 28, 48},
|
{TypeParam{}, 28, 48},
|
||||||
{term{}, 12, 24},
|
{term{}, 12, 24},
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ func (subst *subster) typ(typ Type) Type {
|
|||||||
// In this case the interface will not be substituted here, because its
|
// 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
|
// method signatures do not depend on the type parameter P, but we still
|
||||||
// need to create new interface methods to hold the instantiated
|
// 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)
|
iface.methods, _ = replaceRecvType(methods, t, iface)
|
||||||
return 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
|
// careful not to call any methods that would cause t to be expanded: doing
|
||||||
// so would result in deadlock.
|
// so would result in deadlock.
|
||||||
//
|
//
|
||||||
// So we call t.orig.TypeParams() rather than t.TypeParams() here and
|
// So we call t.Origin().TypeParams() rather than t.TypeParams().
|
||||||
// below.
|
orig := t.Origin()
|
||||||
if t.orig.TypeParams().Len() == 0 {
|
n := orig.TypeParams().Len()
|
||||||
|
if n == 0 {
|
||||||
dump(">>> %s is not parameterized", t)
|
dump(">>> %s is not parameterized", t)
|
||||||
return t // type is not parameterized
|
return t // type is not parameterized
|
||||||
}
|
}
|
||||||
|
|
||||||
var newTArgs []Type
|
var newTArgs []Type
|
||||||
if t.targs.Len() != t.orig.TypeParams().Len() {
|
if t.TypeArgs().Len() != n {
|
||||||
return Typ[Invalid] // error reported elsewhere
|
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
|
// For each (existing) type argument targ, determine if it needs
|
||||||
// to be substituted; i.e., if it is or contains a type parameter
|
// to be substituted; i.e., if it is or contains a type parameter
|
||||||
// that has a type argument for it.
|
// 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)
|
dump(">>> %d targ = %s", i, targ)
|
||||||
new_targ := subst.typ(targ)
|
new_targ := subst.typ(targ)
|
||||||
if new_targ != targ {
|
if new_targ != targ {
|
||||||
dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
|
dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
|
||||||
if newTArgs == nil {
|
if newTArgs == nil {
|
||||||
newTArgs = make([]Type, t.orig.TypeParams().Len())
|
newTArgs = make([]Type, n)
|
||||||
copy(newTArgs, t.targs.list())
|
copy(newTArgs, t.TypeArgs().list())
|
||||||
}
|
}
|
||||||
newTArgs[i] = new_targ
|
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
|
// 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)
|
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)
|
dump(">>> found %s", named)
|
||||||
return named
|
return named
|
||||||
}
|
}
|
||||||
@ -258,7 +259,7 @@ func (subst *subster) typ(typ Type) Type {
|
|||||||
// recursion. The position used here is irrelevant because validation only
|
// 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
|
// occurs on t (we don't call validType on named), but we use subst.pos to
|
||||||
// help with debugging.
|
// 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
|
// Note that if we were to expose substitution more generally (not just in
|
||||||
// the context of a declaration), we'd have to substitute in
|
// the context of a declaration), we'd have to substitute in
|
||||||
|
@ -285,9 +285,9 @@ func (w *typeWriter) typ(typ Type) {
|
|||||||
w.string(strconv.Itoa(w.ctxt.getID(t)))
|
w.string(strconv.Itoa(w.ctxt.getID(t)))
|
||||||
}
|
}
|
||||||
w.typeName(t.obj) // when hashing written for readability of the hash only
|
w.typeName(t.obj) // when hashing written for readability of the hash only
|
||||||
if t.targs != nil {
|
if t.inst != nil {
|
||||||
// instantiated type
|
// 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
|
} else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
|
||||||
// parameterized type
|
// parameterized type
|
||||||
w.tParamList(t.TypeParams().list())
|
w.tParamList(t.TypeParams().list())
|
||||||
|
@ -428,8 +428,8 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re
|
|||||||
// errors.
|
// errors.
|
||||||
check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst)
|
check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst)
|
||||||
|
|
||||||
if check.validateTArgLen(ix.Pos(), inst.TypeParams().Len(), inst.targs.Len()) {
|
if check.validateTArgLen(ix.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) {
|
||||||
if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.targs.list()); err != nil {
|
if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list()); err != nil {
|
||||||
// best position for error reporting
|
// best position for error reporting
|
||||||
pos := ix.Pos()
|
pos := ix.Pos()
|
||||||
if i < len(ix.Indices) {
|
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())
|
check.softErrorf(atPos(pos), _InvalidTypeArg, err.Error())
|
||||||
} else {
|
} 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,8 +546,8 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
|
|||||||
case *Named:
|
case *Named:
|
||||||
// TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate.
|
// TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate.
|
||||||
if y, ok := y.(*Named); ok {
|
if y, ok := y.(*Named); ok {
|
||||||
xargs := x.targs.list()
|
xargs := x.TypeArgs().list()
|
||||||
yargs := y.targs.list()
|
yargs := y.TypeArgs().list()
|
||||||
|
|
||||||
if len(xargs) != len(yargs) {
|
if len(xargs) != len(yargs) {
|
||||||
return false
|
return false
|
||||||
|
@ -71,7 +71,7 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn
|
|||||||
switch check.infoMap[t] {
|
switch check.infoMap[t] {
|
||||||
case unknown:
|
case unknown:
|
||||||
check.infoMap[t] = marked
|
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:
|
case marked:
|
||||||
// We have seen type t before and thus must have a cycle.
|
// We have seen type t before and thus must have a cycle.
|
||||||
check.infoMap[t] = invalid
|
check.infoMap[t] = invalid
|
||||||
|
Loading…
x
Reference in New Issue
Block a user