return inst, nil
}
-// instance resolves a type or function instance for the given original type
-// and type arguments. It looks for an existing identical instance in the given
-// contexts, creating a new instance if none is found.
+// instance instantiates the given original (generic) function or type with the
+// provided type arguments and returns the resulting instance. If an identical
+// instance exists already in the given contexts, it returns that instance,
+// otherwise it creates a new one.
//
-// If local is non-nil, it is the context associated with a Named instance
-// type currently being expanded. If global is non-nil, it is the context
-// associated with the current type-checking pass or call to Instantiate. At
-// least one of local or global must be non-nil.
+// If expanding is non-nil, it is the Named instance type currently being
+// expanded. If ctxt is non-nil, it is the context associated with the current
+// type-checking pass or call to Instantiate. At least one of expanding or ctxt
+// must be non-nil.
//
// For Named types the resulting instance may be unexpanded.
-func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, local, global *Context) (res Type) {
- // The order of the contexts below matters: we always prefer instances in
- // local in order to preserve reference cycles.
+func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, expanding *Named, ctxt *Context) (res Type) {
+ // The order of the contexts below matters: we always prefer instances in the
+ // expanding instance context in order to preserve reference cycles.
//
- // Invariant: if local != nil, the returned instance will be the instance
- // recorded in local.
+ // Invariant: if expanding != nil, the returned instance will be the instance
+ // recorded in expanding.inst.ctxt.
var ctxts []*Context
- if local != nil {
- ctxts = append(ctxts, local)
+ if expanding != nil {
+ ctxts = append(ctxts, expanding.inst.ctxt)
}
- if global != nil {
- ctxts = append(ctxts, global)
+ if ctxt != nil {
+ ctxts = append(ctxts, ctxt)
}
assert(len(ctxts) > 0)
switch orig := orig.(type) {
case *Named:
- res = check.newNamedInstance(pos, orig, targs, local) // substituted lazily
+ res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
case *Signature:
- assert(local == nil) // function instances cannot be reached from Named types
+ assert(expanding == nil) // function instances cannot be reached from Named types
tparams := orig.TypeParams()
if !check.validateTArgLen(pos, tparams.Len(), len(targs)) {
if tparams.Len() == 0 {
return orig // nothing to do (minor optimization)
}
- sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, global).(*Signature)
+ sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, ctxt).(*Signature)
// If the signature doesn't use its type parameters, subst
// will not make a copy. In that case, make a copy now (so
// we can set tparams to nil w/o causing side-effects).
if obj.typ == nil {
obj.typ = typ
}
- // Ensure that typ is always expanded and sanity-checked.
+ // Ensure that typ is always sanity-checked.
if check != nil {
check.needsCleanup(typ)
}
return typ
}
-func (check *Checker) newNamedInstance(pos syntax.Pos, orig *Named, targs []Type, local *Context) *Named {
+// newNamedInstance creates a new named instance for the given origin and type
+// arguments, recording pos as the position of its synthetic object (for error
+// reporting).
+//
+// If set, expanding is the named type instance currently being expanded, that
+// led to the creation of this instance.
+func (check *Checker) newNamedInstance(pos syntax.Pos, orig *Named, targs []Type, expanding *Named) *Named {
assert(len(targs) > 0)
obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
- inst := &instance{orig: orig, targs: newTypeList(targs), ctxt: local}
+ inst := &instance{orig: orig, targs: newTypeList(targs)}
+
+ // Only pass the expanding context to the new instance if their packages
+ // match. Since type reference cycles are only possible within a single
+ // package, this is sufficient for the purposes of short-circuiting cycles.
+ // Avoiding passing the context in other cases prevents unnecessary coupling
+ // of types across packages.
+ if expanding != nil && expanding.Obj().pkg == obj.pkg {
+ inst.ctxt = expanding.inst.ctxt
+ }
typ := &Named{check: check, obj: obj, inst: inst}
obj.typ = typ
- // Ensure that typ is always expanded and sanity-checked.
+ // Ensure that typ is always sanity-checked.
if check != nil {
check.needsCleanup(typ)
}
// code.
if origSig.RecvTypeParams().Len() == t.inst.targs.Len() {
smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list())
- var global *Context
+ var ctxt *Context
if check != nil {
- global = check.context()
+ ctxt = check.context()
}
- sig = check.subst(origm.pos, origSig, smap, t.inst.ctxt, global).(*Signature)
+ sig = check.subst(origm.pos, origSig, smap, t, ctxt).(*Signature)
}
if sig == origSig {
assert(n == n2)
smap := makeSubstMap(orig.tparams.list(), targs.list())
- var global *Context
+ var ctxt *Context
if check != nil {
- global = check.context()
+ ctxt = check.context()
}
- underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n.inst.ctxt, global)
+ underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n, 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.
// incoming type. If a substitution took place, the result type is different
// from the incoming type.
//
-// If the given context is non-nil, it is used in lieu of check.Config.Context.
-func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, local, global *Context) Type {
- assert(local != nil || global != nil)
+// If expanding is non-nil, it is the instance type currently being expanded.
+// One of expanding or ctxt must be non-nil.
+func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, expanding *Named, ctxt *Context) Type {
+ assert(expanding != nil || ctxt != nil)
if smap.empty() {
return typ
// general case
subst := subster{
- pos: pos,
- smap: smap,
- check: check,
- local: local,
- global: global,
+ pos: pos,
+ smap: smap,
+ check: check,
+ expanding: expanding,
+ ctxt: ctxt,
}
return subst.typ(typ)
}
type subster struct {
- pos syntax.Pos
- smap substMap
- check *Checker // nil if called via Instantiate
- local, global *Context
+ pos syntax.Pos
+ smap substMap
+ check *Checker // nil if called via Instantiate
+ expanding *Named // if non-nil, the instance that is being expanded
+ ctxt *Context
}
func (subst *subster) typ(typ Type) Type {
// recursion. The position used here is irrelevant because validation only
// occurs on t (we don't call validType on named), but we use subst.pos to
// help with debugging.
- return subst.check.instance(subst.pos, orig, newTArgs, subst.local, subst.global)
+ return subst.check.instance(subst.pos, orig, newTArgs, subst.expanding, subst.ctxt)
case *TypeParam:
return subst.smap.lookup(t)
return inst, nil
}
-// instance resolves a type or function instance for the given original type
-// and type arguments. It looks for an existing identical instance in the given
-// contexts, creating a new instance if none is found.
+// instance instantiates the given original (generic) function or type with the
+// provided type arguments and returns the resulting instance. If an identical
+// instance exists already in the given contexts, it returns that instance,
+// otherwise it creates a new one.
//
-// If local is non-nil, it is the context associated with a Named instance
-// type currently being expanded. If global is non-nil, it is the context
-// associated with the current type-checking pass or call to Instantiate. At
-// least one of local or global must be non-nil.
+// If expanding is non-nil, it is the Named instance type currently being
+// expanded. If ctxt is non-nil, it is the context associated with the current
+// type-checking pass or call to Instantiate. At least one of expanding or ctxt
+// must be non-nil.
//
// For Named types the resulting instance may be unexpanded.
-func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, local, global *Context) (res Type) {
- // The order of the contexts below matters: we always prefer instances in
- // local in order to preserve reference cycles.
+func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, expanding *Named, ctxt *Context) (res Type) {
+ // The order of the contexts below matters: we always prefer instances in the
+ // expanding instance context in order to preserve reference cycles.
//
- // Invariant: if local != nil, the returned instance will be the instance
- // recorded in local.
+ // Invariant: if expanding != nil, the returned instance will be the instance
+ // recorded in expanding.inst.ctxt.
var ctxts []*Context
- if local != nil {
- ctxts = append(ctxts, local)
+ if expanding != nil {
+ ctxts = append(ctxts, expanding.inst.ctxt)
}
- if global != nil {
- ctxts = append(ctxts, global)
+ if ctxt != nil {
+ ctxts = append(ctxts, ctxt)
}
assert(len(ctxts) > 0)
switch orig := orig.(type) {
case *Named:
- res = check.newNamedInstance(pos, orig, targs, local) // substituted lazily
+ res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
case *Signature:
- assert(local == nil) // function instances cannot be reached from Named types
+ assert(expanding == nil) // function instances cannot be reached from Named types
tparams := orig.TypeParams()
if !check.validateTArgLen(pos, tparams.Len(), len(targs)) {
if tparams.Len() == 0 {
return orig // nothing to do (minor optimization)
}
- sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, global).(*Signature)
+ sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, ctxt).(*Signature)
// If the signature doesn't use its type parameters, subst
// will not make a copy. In that case, make a copy now (so
// we can set tparams to nil w/o causing side-effects).
if obj.typ == nil {
obj.typ = typ
}
- // Ensure that typ is always expanded and sanity-checked.
+ // Ensure that typ is always sanity-checked.
if check != nil {
check.needsCleanup(typ)
}
return typ
}
-func (check *Checker) newNamedInstance(pos token.Pos, orig *Named, targs []Type, local *Context) *Named {
+// newNamedInstance creates a new named instance for the given origin and type
+// arguments, recording pos as the position of its synthetic object (for error
+// reporting).
+//
+// If set, expanding is the named type instance currently being expanded, that
+// led to the creation of this instance.
+func (check *Checker) newNamedInstance(pos token.Pos, orig *Named, targs []Type, expanding *Named) *Named {
assert(len(targs) > 0)
obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
- inst := &instance{orig: orig, targs: newTypeList(targs), ctxt: local}
+ inst := &instance{orig: orig, targs: newTypeList(targs)}
+
+ // Only pass the expanding context to the new instance if their packages
+ // match. Since type reference cycles are only possible within a single
+ // package, this is sufficient for the purposes of short-circuiting cycles.
+ // Avoiding passing the context in other cases prevents unnecessary coupling
+ // of types across packages.
+ if expanding != nil && expanding.Obj().pkg == obj.pkg {
+ inst.ctxt = expanding.inst.ctxt
+ }
typ := &Named{check: check, obj: obj, inst: inst}
obj.typ = typ
- // Ensure that typ is always expanded and sanity-checked.
+ // Ensure that typ is always sanity-checked.
if check != nil {
check.needsCleanup(typ)
}
// code.
if origSig.RecvTypeParams().Len() == t.inst.targs.Len() {
smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list())
- var global *Context
+ var ctxt *Context
if check != nil {
- global = check.context()
+ ctxt = check.context()
}
- sig = check.subst(origm.pos, origSig, smap, t.inst.ctxt, global).(*Signature)
+ sig = check.subst(origm.pos, origSig, smap, t, ctxt).(*Signature)
}
if sig == origSig {
assert(n == n2)
smap := makeSubstMap(orig.tparams.list(), targs.list())
- var global *Context
+ var ctxt *Context
if check != nil {
- global = check.context()
+ ctxt = check.context()
}
- underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n.inst.ctxt, global)
+ underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n, 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.
// that it doesn't modify the incoming type. If a substitution took place, the
// result type is different from the incoming type.
//
-// If the given context is non-nil, it is used in lieu of check.Config.Context
-func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, local, global *Context) Type {
- assert(local != nil || global != nil)
+// If expanding is non-nil, it is the instance type currently being expanded.
+// One of expanding or ctxt must be non-nil.
+func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, expanding *Named, ctxt *Context) Type {
+ assert(expanding != nil || ctxt != nil)
if smap.empty() {
return typ
// general case
subst := subster{
- pos: pos,
- smap: smap,
- check: check,
- local: local,
- global: global,
+ pos: pos,
+ smap: smap,
+ check: check,
+ expanding: expanding,
+ ctxt: ctxt,
}
return subst.typ(typ)
}
type subster struct {
- pos token.Pos
- smap substMap
- check *Checker // nil if called via Instantiate
- local, global *Context
+ pos token.Pos
+ smap substMap
+ check *Checker // nil if called via Instantiate
+ expanding *Named // if non-nil, the instance that is being expanded
+ ctxt *Context
}
func (subst *subster) typ(typ Type) Type {
// recursion. The position used here is irrelevant because validation only
// occurs on t (we don't call validType on named), but we use subst.pos to
// help with debugging.
- return subst.check.instance(subst.pos, orig, newTArgs, subst.local, subst.global)
+ return subst.check.instance(subst.pos, orig, newTArgs, subst.expanding, subst.ctxt)
case *TypeParam:
return subst.smap.lookup(t)