mirror of
https://github.com/golang/go
synced 2024-11-24 09:40:08 -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:
parent
a52e4b9c7e
commit
313cae3861
@ -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.
|
||||||
|
@ -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 = ©
|
sig = ©
|
||||||
}
|
}
|
||||||
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user