// It is safe for concurrent use.
type Context struct {
mu sync.Mutex
- typeMap map[string][]*Named // type hash -> instances
- nextID int // next unique ID
- seen map[*Named]int // assigned unique IDs
+ typeMap map[string][]ctxtEntry // type hash -> instances entries
+ nextID int // next unique ID
+ seen map[*Named]int // assigned unique IDs
+}
+
+type ctxtEntry struct {
+ orig Type
+ targs []Type
+ instance Type // = orig[targs]
}
// NewContext creates a new Context.
func NewContext() *Context {
return &Context{
- typeMap: make(map[string][]*Named),
+ typeMap: make(map[string][]ctxtEntry),
seen: make(map[*Named]int),
}
}
// lookup returns an existing instantiation of orig with targs, if it exists.
// Otherwise, it returns nil.
-func (ctxt *Context) lookup(h string, orig *Named, targs []Type) *Named {
+func (ctxt *Context) lookup(h string, orig *Named, targs []Type) Type {
ctxt.mu.Lock()
defer ctxt.mu.Unlock()
for _, e := range ctxt.typeMap[h] {
- if identicalInstance(orig, targs, e.orig, e.TypeArgs().list()) {
- return e
+ if identicalInstance(orig, targs, e.orig, e.targs) {
+ return e.instance
}
if debug {
// Panic during development to surface any imperfections in our hash.
- panic(fmt.Sprintf("non-identical instances: (orig: %s, targs: %v) and %s", orig, targs, e))
+ panic(fmt.Sprintf("non-identical instances: (orig: %s, targs: %v) and %s", orig, targs, e.instance))
}
}
// identical type is found with the type hash h, the previously seen type is
// returned. Otherwise, n is returned, and recorded in the Context for the hash
// h.
-func (ctxt *Context) update(h string, n *Named) *Named {
- assert(n != nil)
+func (ctxt *Context) update(h string, orig Type, targs []Type, inst Type) Type {
+ assert(inst != nil)
ctxt.mu.Lock()
defer ctxt.mu.Unlock()
for _, e := range ctxt.typeMap[h] {
- if n == nil || Identical(n, e) {
- return e
+ if inst == nil || Identical(inst, e.instance) {
+ return e.instance
}
if debug {
// Panic during development to surface any imperfections in our hash.
- panic(fmt.Sprintf("%s and %s are not identical", n, e))
+ panic(fmt.Sprintf("%s and %s are not identical", inst, e.instance))
}
}
- ctxt.typeMap[h] = append(ctxt.typeMap[h], n)
- return n
+ ctxt.typeMap[h] = append(ctxt.typeMap[h], ctxtEntry{
+ orig: orig,
+ targs: targs,
+ instance: inst,
+ })
+
+ return inst
}
// idForType returns a unique ID for the pointer n.
// instance creates a type or function instance using the given original type
// typ and arguments targs. For Named types the resulting instance will be
// unexpanded.
-func (check *Checker) instance(pos token.Pos, typ Type, targs []Type, ctxt *Context) Type {
- switch t := typ.(type) {
+func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, ctxt *Context) Type {
+ switch orig := orig.(type) {
case *Named:
var h string
if ctxt != nil {
- h = ctxt.typeHash(t, targs)
+ h = ctxt.typeHash(orig, targs)
// typ may already have been instantiated with identical type arguments. In
// that case, re-use the existing instance.
- if named := ctxt.lookup(h, t, targs); named != nil {
+ if named := ctxt.lookup(h, orig, targs); named != nil {
return named
}
}
- tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil)
- named := check.newNamed(tname, t, nil, nil, nil) // underlying, tparams, and methods are set when named is resolved
+ tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
+ named := check.newNamed(tname, orig, nil, nil, nil) // underlying, tparams, and methods are set when named is resolved
named.targs = NewTypeList(targs)
named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) {
return expandNamed(ctxt, n, pos)
if ctxt != nil {
// It's possible that we've lost a race to add named to the context.
// In this case, use whichever instance is recorded in the context.
- named = ctxt.update(h, named)
+ named = ctxt.update(h, orig, targs, named).(*Named)
}
return named
case *Signature:
- tparams := t.TypeParams()
+ tparams := orig.TypeParams()
if !check.validateTArgLen(pos, tparams.Len(), len(targs)) {
return Typ[Invalid]
}
if tparams.Len() == 0 {
- return typ // nothing to do (minor optimization)
+ return orig // nothing to do (minor optimization)
}
- sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), ctxt).(*Signature)
+ sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), 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 sig == t {
+ if sig == orig {
copy := *sig
sig = ©
}
return sig
}
// only types and functions can be generic
- panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
+ panic(fmt.Sprintf("%v: cannot instantiate %v", pos, orig))
}
// validateTArgLen verifies that the length of targs and tparams matches,
ctxt = check.bestContext(ctxt)
h := ctxt.typeHash(n.orig, n.targs.list())
// ensure that an instance is recorded for h to avoid infinite recursion.
- ctxt.update(h, n)
+ ctxt.update(h, n.orig, n.TypeArgs().list(), n)
smap := makeSubstMap(n.orig.tparams.list(), n.targs.list())
underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt)
// create the instance
h := check.conf.Context.typeHash(orig, targs)
// targs may be incomplete, and require inference. In any case we should de-duplicate.
- inst := check.conf.Context.lookup(h, orig, targs)
+ inst, _ := check.conf.Context.lookup(h, orig, targs).(*Named)
// If inst is non-nil, we can't just return here. Inst may have been
// constructed via recursive substitution, in which case we wouldn't do the
// validation below. Ensure that the validation (and resulting errors) runs
tname := NewTypeName(x.Pos(), orig.obj.pkg, orig.obj.name, nil)
inst = check.newNamed(tname, orig, nil, nil, nil) // underlying, methods and tparams are set when named is resolved
inst.targs = NewTypeList(targs)
- inst = check.conf.Context.update(h, inst)
+ inst = check.conf.Context.update(h, orig, targs, inst).(*Named)
}
def.setUnderlying(inst)