1
0
mirror of https://github.com/golang/go synced 2024-11-24 09:20:02 -07:00

cmd/compile/internal/types2: refactor the Context type map to accept arbitrary types

This CL is a clean port of CL 362799 from go/types to types2.

Change-Id: Id670aa4b1ca0b568a79bb6e4855747807dcf00f3
Reviewed-on: https://go-review.googlesource.com/c/go/+/364154
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-11-15 15:21:42 -08:00
parent a52e4b9c7e
commit 313cae3861
4 changed files with 41 additions and 32 deletions

View File

@ -17,15 +17,21 @@ import (
// It is safe for concurrent use. // It is safe for concurrent use.
type Context struct { type Context struct {
mu sync.Mutex mu sync.Mutex
typeMap map[string][]*Named // type hash -> instances typeMap map[string][]ctxtEntry // type hash -> instances entries
nextID int // next unique ID nextID int // next unique ID
seen map[*Named]int // assigned unique IDs seen map[*Named]int // assigned unique IDs
} }
type ctxtEntry struct {
orig Type
targs []Type
instance Type // = orig[targs]
}
// NewContext creates a new Context. // NewContext creates a new Context.
func NewContext() *Context { func NewContext() *Context {
return &Context{ return &Context{
typeMap: make(map[string][]*Named), typeMap: make(map[string][]ctxtEntry),
seen: make(map[*Named]int), seen: make(map[*Named]int),
} }
} }
@ -59,17 +65,17 @@ func (ctxt *Context) typeHash(typ Type, targs []Type) string {
// lookup returns an existing instantiation of orig with targs, if it exists. // lookup returns an existing instantiation of orig with targs, if it exists.
// Otherwise, it returns nil. // 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() ctxt.mu.Lock()
defer ctxt.mu.Unlock() defer ctxt.mu.Unlock()
for _, e := range ctxt.typeMap[h] { for _, e := range ctxt.typeMap[h] {
if identicalInstance(orig, targs, e.orig, e.TypeArgs().list()) { if identicalInstance(orig, targs, e.orig, e.targs) {
return e return e.instance
} }
if debug { if debug {
// Panic during development to surface any imperfections in our hash. // 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))
} }
} }
@ -80,24 +86,28 @@ func (ctxt *Context) lookup(h string, orig *Named, targs []Type) *Named {
// identical type is found with the type hash h, the previously seen type is // 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 // returned. Otherwise, n is returned, and recorded in the Context for the hash
// h. // h.
func (ctxt *Context) update(h string, n *Named) *Named { func (ctxt *Context) update(h string, orig Type, targs []Type, inst Type) Type {
assert(n != nil) assert(inst != nil)
ctxt.mu.Lock() ctxt.mu.Lock()
defer ctxt.mu.Unlock() defer ctxt.mu.Unlock()
for _, e := range ctxt.typeMap[h] { for _, e := range ctxt.typeMap[h] {
if n == nil || Identical(n, e) { if inst == nil || Identical(inst, e.instance) {
return e return e.instance
} }
if debug { if debug {
// Panic during development to surface any imperfections in our hash. // 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) ctxt.typeMap[h] = append(ctxt.typeMap[h], ctxtEntry{
return n orig: orig,
targs: targs,
instance: inst,
})
return inst
} }
// idForType returns a unique ID for the pointer n. // idForType returns a unique ID for the pointer n.

View File

@ -52,20 +52,20 @@ func Instantiate(ctxt *Context, typ Type, targs []Type, validate bool) (Type, er
// instance creates a type or function instance using the given original type // instance creates a type or function instance using the given original type
// typ and arguments targs. For Named types the resulting instance will be // typ and arguments targs. For Named types the resulting instance will be
// unexpanded. // unexpanded.
func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, ctxt *Context) Type { func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, ctxt *Context) Type {
switch t := typ.(type) { switch orig := orig.(type) {
case *Named: case *Named:
var h string var h string
if ctxt != nil { if ctxt != nil {
h = ctxt.typeHash(t, targs) h = ctxt.typeHash(orig, targs)
// typ may already have been instantiated with identical type arguments. In // typ may already have been instantiated with identical type arguments. In
// that case, re-use the existing instance. // 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 return named
} }
} }
tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil) tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
named := check.newNamed(tname, t, nil, nil, nil) // underlying, tparams, and methods are set when named is resolved named := check.newNamed(tname, orig, nil, nil, nil) // underlying, tparams, and methods are set when named is resolved
named.targs = NewTypeList(targs) named.targs = NewTypeList(targs)
named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) { named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) {
return expandNamed(ctxt, n, pos) return expandNamed(ctxt, n, pos)
@ -73,23 +73,23 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, ctxt *Con
if ctxt != nil { if ctxt != nil {
// It's possible that we've lost a race to add named to the context. // 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. // 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 return named
case *Signature: case *Signature:
tparams := t.TypeParams() tparams := orig.TypeParams()
if !check.validateTArgLen(pos, tparams.Len(), len(targs)) { if !check.validateTArgLen(pos, tparams.Len(), len(targs)) {
return Typ[Invalid] return Typ[Invalid]
} }
if tparams.Len() == 0 { 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 // If the signature doesn't use its type parameters, subst
// will not make a copy. In that case, make a copy now (so // will not make a copy. In that case, make a copy now (so
// we can set tparams to nil w/o causing side-effects). // we can set tparams to nil w/o causing side-effects).
if sig == t { if sig == orig {
copy := *sig copy := *sig
sig = &copy sig = &copy
} }
@ -98,9 +98,8 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, ctxt *Con
sig.tparams = nil sig.tparams = nil
return sig return sig
} }
// only types and functions can be generic // 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, // validateTArgLen verifies that the length of targs and tparams matches,

View File

@ -253,7 +253,7 @@ func expandNamed(ctxt *Context, n *Named, instPos syntax.Pos) (tparams *TypePara
ctxt = check.bestContext(ctxt) ctxt = check.bestContext(ctxt)
h := ctxt.typeHash(n.orig, n.targs.list()) h := ctxt.typeHash(n.orig, n.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) ctxt.update(h, n.orig, n.TypeArgs().list(), n)
smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) smap := makeSubstMap(n.orig.tparams.list(), n.targs.list())
underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt) underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt)

View File

@ -439,7 +439,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, targsx []syntax.Expr, def
// create the instance // create the instance
h := check.conf.Context.typeHash(orig, targs) h := check.conf.Context.typeHash(orig, targs)
// targs may be incomplete, and require inference. In any case we should de-duplicate. // 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 // 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 // constructed via recursive substitution, in which case we wouldn't do the
// validation below. Ensure that the validation (and resulting errors) runs // validation below. Ensure that the validation (and resulting errors) runs
@ -448,7 +448,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, targsx []syntax.Expr, def
tname := NewTypeName(x.Pos(), orig.obj.pkg, orig.obj.name, nil) 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 = check.newNamed(tname, orig, nil, nil, nil) // underlying, methods and tparams are set when named is resolved
inst.targs = NewTypeList(targs) inst.targs = NewTypeList(targs)
inst = check.conf.Context.update(h, inst) inst = check.conf.Context.update(h, orig, targs, inst).(*Named)
} }
def.setUnderlying(inst) def.setUnderlying(inst)