mirror of
https://github.com/golang/go
synced 2024-11-26 16:46:58 -07:00
[dev.typeparams] go/types: backport lazy loading changes from CL 336252
When CL 336252 was created (itself a port of CL 335929), types2 tests revealed that lazy expansion of instances was not behaving correctly with respect to lazy loading of Named types. This CL ports the fixes from CL 336252 back to go/types. Change-Id: Iffc6c84a708449633153b800dfb98ff57402893c Reviewed-on: https://go-review.googlesource.com/c/go/+/338369 Trust: Robert Findley <rfindley@google.com> Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
27283d208f
commit
4480e3b11a
@ -317,7 +317,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
|
||||
}
|
||||
|
||||
case *Named:
|
||||
t.complete()
|
||||
t.expand()
|
||||
// don't touch the type if it is from a different package or the Universe scope
|
||||
// (doing so would lead to a race condition - was issue #35049)
|
||||
if t.obj.pkg != check.pkg {
|
||||
@ -747,7 +747,7 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
||||
}
|
||||
|
||||
if base != nil {
|
||||
base.expand() // TODO(mdempsky): Probably unnecessary.
|
||||
base.load() // TODO(mdempsky): Probably unnecessary.
|
||||
base.methods = append(base.methods, m)
|
||||
}
|
||||
}
|
||||
|
@ -8,34 +8,37 @@ package types
|
||||
|
||||
import "go/token"
|
||||
|
||||
// instance holds a Checker along with syntactic information
|
||||
// information, for use in lazy instantiation.
|
||||
// instance holds position information for use in lazy instantiation.
|
||||
//
|
||||
// TODO(rfindley): come up with a better name for this type, now that its usage
|
||||
// has changed.
|
||||
type instance struct {
|
||||
check *Checker
|
||||
pos token.Pos // position of type instantiation; for error reporting only
|
||||
posList []token.Pos // position of each targ; for error reporting only
|
||||
}
|
||||
|
||||
// complete ensures that the underlying type of n is instantiated.
|
||||
// expand ensures that the underlying type of n is instantiated.
|
||||
// The underlying type will be Typ[Invalid] if there was an error.
|
||||
// TODO(rfindley): expand would be a better name for this method, but conflicts
|
||||
// with the existing concept of lazy expansion. Need to reconcile this.
|
||||
func (n *Named) complete() {
|
||||
if n.instance != nil && len(n.targs) > 0 && n.underlying == nil {
|
||||
check := n.instance.check
|
||||
inst := check.instantiate(n.instance.pos, n.orig.underlying, n.TParams().list(), n.targs, n.instance.posList)
|
||||
func (n *Named) expand() {
|
||||
if n.instance != nil {
|
||||
// n must be loaded before instantiation, in order to have accurate
|
||||
// tparams. This is done implicitly by the call to n.TParams, but making it
|
||||
// explicit is harmless: load is idempotent.
|
||||
n.load()
|
||||
inst := n.check.instantiate(n.instance.pos, n.orig.underlying, n.TParams().list(), n.targs, n.instance.posList)
|
||||
n.underlying = inst
|
||||
n.fromRHS = inst
|
||||
n.methods = n.orig.methods
|
||||
n.instance = nil
|
||||
}
|
||||
}
|
||||
|
||||
// expand expands a type instance into its instantiated
|
||||
// type and leaves all other types alone. expand does
|
||||
// not recurse.
|
||||
// expand expands uninstantiated named types and leaves all other types alone.
|
||||
// expand does not recurse.
|
||||
func expand(typ Type) Type {
|
||||
if t, _ := typ.(*Named); t != nil {
|
||||
t.complete()
|
||||
t.expand()
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
@ -105,7 +105,9 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, tparams []*TypeName,
|
||||
// instantiating the type until needed. typ must be a *Named
|
||||
// type.
|
||||
func (check *Checker) InstantiateLazy(pos token.Pos, typ Type, targs []Type, posList []token.Pos, verify bool) Type {
|
||||
base := asNamed(typ)
|
||||
// Don't use asNamed here: we don't want to expand the base during lazy
|
||||
// instantiation.
|
||||
base := typ.(*Named)
|
||||
if base == nil {
|
||||
panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
|
||||
}
|
||||
@ -116,15 +118,18 @@ func (check *Checker) InstantiateLazy(pos token.Pos, typ Type, targs []Type, pos
|
||||
}
|
||||
h := instantiatedHash(base, targs)
|
||||
if check != nil {
|
||||
// typ may already have been instantiated with identical type arguments. In
|
||||
// that case, re-use the existing instance.
|
||||
if named := check.typMap[h]; named != nil {
|
||||
return named
|
||||
}
|
||||
}
|
||||
|
||||
tname := NewTypeName(pos, base.obj.pkg, base.obj.name, nil)
|
||||
named := check.newNamed(tname, base, nil, base.TParams(), base.methods) // methods are instantiated lazily
|
||||
named := check.newNamed(tname, base, nil, nil, nil) // methods and tparams are set when named is loaded.
|
||||
named.targs = targs
|
||||
named.instance = &instance{check, pos, posList}
|
||||
named.instance = &instance{pos, posList}
|
||||
|
||||
if check != nil {
|
||||
check.typMap[h] = named
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||
seen[named] = true
|
||||
|
||||
// look for a matching attached method
|
||||
named.expand()
|
||||
named.load()
|
||||
if i, m := lookupMethod(named.methods, pkg, name); m != nil {
|
||||
// potential match
|
||||
// caution: method may not have a proper signature yet
|
||||
|
@ -10,12 +10,13 @@ import "sync"
|
||||
|
||||
// A Named represents a named (defined) type.
|
||||
type Named struct {
|
||||
instance *instance // syntactic information for lazy instantiation
|
||||
check *Checker
|
||||
info typeInfo // for cycle detection
|
||||
obj *TypeName // corresponding declared object
|
||||
orig *Named // original, uninstantiated type
|
||||
fromRHS Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
|
||||
underlying Type // possibly a *Named during setup; never a *Named once set up completely
|
||||
instance *instance // syntactic information for lazy instantiation
|
||||
tparams *TypeParams // type parameters, or nil
|
||||
targs []Type // type arguments (after instantiation), or nil
|
||||
methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
|
||||
@ -34,7 +35,19 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||
return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
|
||||
}
|
||||
|
||||
func (t *Named) expand() *Named {
|
||||
func (t *Named) load() *Named {
|
||||
// If t is an instantiated type, it derives its methods and tparams from its
|
||||
// base type. Since we expect type parameters and methods to be set after a
|
||||
// call to load, we must load the base and copy here.
|
||||
//
|
||||
// underlying is set when t is expanded.
|
||||
//
|
||||
// By convention, a type instance is loaded iff its tparams are set.
|
||||
if len(t.targs) > 0 && t.tparams == nil {
|
||||
t.orig.load()
|
||||
t.tparams = t.orig.tparams
|
||||
t.methods = t.orig.methods
|
||||
}
|
||||
if t.resolve == nil {
|
||||
return t
|
||||
}
|
||||
@ -65,13 +78,7 @@ func (t *Named) expand() *Named {
|
||||
|
||||
// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
|
||||
func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParams, methods []*Func) *Named {
|
||||
var inst *instance
|
||||
if check != nil {
|
||||
inst = &instance{
|
||||
check: check,
|
||||
}
|
||||
}
|
||||
typ := &Named{instance: inst, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
|
||||
typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
|
||||
if typ.orig == nil {
|
||||
typ.orig = typ
|
||||
}
|
||||
@ -92,7 +99,7 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tpar
|
||||
case *Named:
|
||||
panic("internal error: unexpanded underlying type")
|
||||
}
|
||||
typ.instance = nil
|
||||
typ.check = nil
|
||||
})
|
||||
}
|
||||
return typ
|
||||
@ -110,10 +117,10 @@ func (t *Named) _Orig() *Named { return t.orig }
|
||||
|
||||
// TParams returns the type parameters of the named type t, or nil.
|
||||
// The result is non-nil for an (originally) parameterized type even if it is instantiated.
|
||||
func (t *Named) TParams() *TypeParams { return t.expand().tparams }
|
||||
func (t *Named) TParams() *TypeParams { return t.load().tparams }
|
||||
|
||||
// SetTParams sets the type parameters of the named type t.
|
||||
func (t *Named) SetTParams(tparams []*TypeName) { t.expand().tparams = bindTParams(tparams) }
|
||||
func (t *Named) SetTParams(tparams []*TypeName) { t.load().tparams = bindTParams(tparams) }
|
||||
|
||||
// TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated.
|
||||
func (t *Named) TArgs() []Type { return t.targs }
|
||||
@ -122,10 +129,10 @@ func (t *Named) TArgs() []Type { return t.targs }
|
||||
func (t *Named) SetTArgs(args []Type) { t.targs = args }
|
||||
|
||||
// NumMethods returns the number of explicit methods whose receiver is named type t.
|
||||
func (t *Named) NumMethods() int { return len(t.expand().methods) }
|
||||
func (t *Named) NumMethods() int { return len(t.load().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.expand().methods[i] }
|
||||
func (t *Named) Method(i int) *Func { return t.load().methods[i] }
|
||||
|
||||
// SetUnderlying sets the underlying type and marks t as complete.
|
||||
func (t *Named) SetUnderlying(underlying Type) {
|
||||
@ -135,18 +142,18 @@ func (t *Named) SetUnderlying(underlying Type) {
|
||||
if _, ok := underlying.(*Named); ok {
|
||||
panic("types.Named.SetUnderlying: underlying type must not be *Named")
|
||||
}
|
||||
t.expand().underlying = underlying
|
||||
t.load().underlying = underlying
|
||||
}
|
||||
|
||||
// AddMethod adds method m unless it is already in the method list.
|
||||
func (t *Named) AddMethod(m *Func) {
|
||||
t.expand()
|
||||
t.load()
|
||||
if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
|
||||
t.methods = append(t.methods, m)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Named) Underlying() Type { return t.expand().underlying }
|
||||
func (t *Named) Underlying() Type { return t.load().underlying }
|
||||
func (t *Named) String() string { return TypeString(t, nil) }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -159,7 +166,7 @@ func (t *Named) String() string { return TypeString(t, nil) }
|
||||
// is detected, the result is Typ[Invalid]. If a cycle is detected and
|
||||
// n0.check != nil, the cycle is reported.
|
||||
func (n0 *Named) under() Type {
|
||||
n0.complete()
|
||||
n0.expand()
|
||||
|
||||
u := n0.Underlying()
|
||||
|
||||
@ -180,13 +187,13 @@ func (n0 *Named) under() Type {
|
||||
// handled below
|
||||
}
|
||||
|
||||
if n0.instance == nil || n0.instance.check == nil {
|
||||
if n0.check == nil {
|
||||
panic("internal error: Named.check == nil but type is incomplete")
|
||||
}
|
||||
|
||||
// Invariant: after this point n0 as well as any named types in its
|
||||
// underlying chain should be set up when this function exits.
|
||||
check := n0.instance.check
|
||||
check := n0.check
|
||||
|
||||
// If we can't expand u at this point, it is invalid.
|
||||
n := asNamed(u)
|
||||
@ -207,7 +214,7 @@ func (n0 *Named) under() Type {
|
||||
var n1 *Named
|
||||
switch u1 := u.(type) {
|
||||
case *Named:
|
||||
u1.complete()
|
||||
u1.expand()
|
||||
n1 = u1
|
||||
}
|
||||
if n1 == nil {
|
||||
|
@ -21,7 +21,7 @@ func isNamed(typ Type) bool {
|
||||
func isGeneric(typ Type) bool {
|
||||
// A parameterized type is only instantiated if it doesn't have an instantiation already.
|
||||
named, _ := typ.(*Named)
|
||||
return named != nil && named.obj != nil && named.TParams() != nil && named.targs == nil
|
||||
return named != nil && named.obj != nil && named.targs == nil && named.TParams() != nil
|
||||
}
|
||||
|
||||
func is(typ Type, what BasicInfo) bool {
|
||||
|
@ -30,7 +30,7 @@ func TestSizeof(t *testing.T) {
|
||||
{Interface{}, 40, 80},
|
||||
{Map{}, 16, 32},
|
||||
{Chan{}, 12, 24},
|
||||
{Named{}, 76, 144},
|
||||
{Named{}, 80, 152},
|
||||
{TypeParam{}, 28, 48},
|
||||
{top{}, 0, 0},
|
||||
|
||||
|
@ -244,7 +244,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||
named := subst.check.newNamed(tname, t, t.Underlying(), t.TParams(), t.methods) // method signatures are updated lazily
|
||||
named.targs = newTargs
|
||||
subst.typMap[h] = named
|
||||
t.complete() // must happen after typMap update to avoid infinite recursion
|
||||
t.expand() // must happen after typMap update to avoid infinite recursion
|
||||
|
||||
// do the substitution
|
||||
dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTargs)
|
||||
|
@ -270,6 +270,9 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
|
||||
}
|
||||
|
||||
case *Named:
|
||||
if t.instance != nil {
|
||||
buf.WriteByte(instanceMarker)
|
||||
}
|
||||
writeTypeName(buf, t.obj, qf)
|
||||
if t.targs != nil {
|
||||
// instantiated type
|
||||
|
Loading…
Reference in New Issue
Block a user