mirror of https://github.com/golang/go.git
go/types: implement substMap and use it for substitutions
Instead of passing around a type parameter list and type arguments, create a substitution map from them and use that map instead. Change-Id: Ia4a041d95bfaa98888c9c06812d33b3d2a79227d
This commit is contained in:
parent
72d320ec6e
commit
38c2ef660d
|
|
@ -329,7 +329,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, args []*oper
|
|||
// need to compute it from the adjusted list; otherwise we can
|
||||
// simply use the result signature's parameter list.
|
||||
if adjusted {
|
||||
sig_params = check.subst(call.Pos(), sig_params, sig.tparams, targs).(*Tuple)
|
||||
sig_params = check.subst(call.Pos(), sig_params, makeSubstMap(sig.tparams, targs)).(*Tuple)
|
||||
} else {
|
||||
sig_params = rsig.params
|
||||
}
|
||||
|
|
@ -523,7 +523,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
// (If we modify m, some tests will fail; possibly because the m is in use.)
|
||||
// TODO(gri) investigate and provide a correct explanation here
|
||||
copy := *m
|
||||
copy.typ = check.subst(e.Pos(), m.typ, sig.rparams, targs)
|
||||
copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.rparams, targs))
|
||||
obj = ©
|
||||
}
|
||||
// TODO(gri) we also need to do substitution for parameterized interface methods
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
|
|||
// type parameters of ftyp with V's instantiation type arguments.
|
||||
// This lazily instantiates the signature of method f.
|
||||
if Vn != nil && len(Vn.targs) > 0 {
|
||||
ftyp = check.subst(token.NoPos, ftyp, ftyp.rparams, Vn.targs).(*Signature)
|
||||
ftyp = check.subst(token.NoPos, ftyp, makeSubstMap(ftyp.rparams, Vn.targs)).(*Signature)
|
||||
}
|
||||
|
||||
// If the methods have type parameters we don't care whether they
|
||||
|
|
|
|||
|
|
@ -10,10 +10,45 @@ package types
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type substMap struct {
|
||||
// The targs field is currently needed for *Named type substitution.
|
||||
// TODO(gri) rewrite that code, get rid of this field, and make this
|
||||
// struct just the map (proj)
|
||||
targs []Type
|
||||
proj map[*TypeParam]Type
|
||||
}
|
||||
|
||||
func makeSubstMap(tpars []*TypeName, targs []Type) *substMap {
|
||||
assert(len(tpars) == len(targs))
|
||||
proj := make(map[*TypeParam]Type, len(tpars))
|
||||
for i, tpar := range tpars {
|
||||
targ := targs[i]
|
||||
assert(targ != nil)
|
||||
proj[tpar.typ.(*TypeParam)] = targs[i]
|
||||
}
|
||||
return &substMap{targs, proj}
|
||||
}
|
||||
|
||||
func (m *substMap) String() string {
|
||||
return fmt.Sprintf("%s", m.proj)
|
||||
}
|
||||
|
||||
func (m *substMap) empty() bool {
|
||||
return len(m.proj) == 0
|
||||
}
|
||||
|
||||
func (m *substMap) lookup(typ *TypeParam) Type {
|
||||
if t := m.proj[typ]; t != nil {
|
||||
return t
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist []token.Pos) (res Type) {
|
||||
if check.conf.Trace {
|
||||
check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs))
|
||||
|
|
@ -67,6 +102,8 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
|
|||
return typ // nothing to do (minor optimization)
|
||||
}
|
||||
|
||||
smap := makeSubstMap(tparams, targs)
|
||||
|
||||
// check bounds
|
||||
for i, tname := range tparams {
|
||||
tpar := tname.typ.(*TypeParam)
|
||||
|
|
@ -87,7 +124,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
|
|||
// as the instantiated type; before we can use it for bounds checking we
|
||||
// need to instantiate it with the type arguments with which we instantiate
|
||||
// the parameterized type.
|
||||
iface = check.subst(pos, iface, tparams, targs).(*Interface)
|
||||
iface = check.subst(pos, iface, smap).(*Interface)
|
||||
|
||||
// targ must implement iface (methods)
|
||||
if m, _ := check.missingMethod(targ, iface, true); m != nil {
|
||||
|
|
@ -125,14 +162,13 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
|
|||
}
|
||||
}
|
||||
|
||||
return check.subst(pos, typ, tparams, targs)
|
||||
return check.subst(pos, typ, smap)
|
||||
}
|
||||
|
||||
// subst returns the type typ with its type parameters tpars replaced by
|
||||
// the corresponding type arguments targs, recursively.
|
||||
func (check *Checker) subst(pos token.Pos, typ Type, tpars []*TypeName, targs []Type) Type {
|
||||
assert(len(tpars) == len(targs))
|
||||
if len(tpars) == 0 {
|
||||
func (check *Checker) subst(pos token.Pos, typ Type, smap *substMap) Type {
|
||||
if smap.empty() {
|
||||
return typ
|
||||
}
|
||||
|
||||
|
|
@ -141,16 +177,11 @@ func (check *Checker) subst(pos token.Pos, typ Type, tpars []*TypeName, targs []
|
|||
case *Basic:
|
||||
return typ // nothing to do
|
||||
case *TypeParam:
|
||||
for i, tpar := range tpars {
|
||||
if tpar.typ == t {
|
||||
return targs[i]
|
||||
}
|
||||
}
|
||||
return typ
|
||||
return smap.lookup(t)
|
||||
}
|
||||
|
||||
// general case
|
||||
subst := subster{check, pos, make(map[Type]Type), tpars, targs}
|
||||
subst := subster{check, pos, make(map[Type]Type), smap}
|
||||
return subst.typ(typ)
|
||||
}
|
||||
|
||||
|
|
@ -158,8 +189,7 @@ type subster struct {
|
|||
check *Checker
|
||||
pos token.Pos
|
||||
cache map[Type]Type
|
||||
tpars []*TypeName
|
||||
targs []Type
|
||||
smap *substMap
|
||||
}
|
||||
|
||||
func (subst *subster) typ(typ Type) Type {
|
||||
|
|
@ -278,7 +308,7 @@ func (subst *subster) typ(typ Type) Type {
|
|||
|
||||
// not yet instantiated
|
||||
dump(">>> first instantiation of %s", t)
|
||||
new_targs = subst.targs
|
||||
new_targs = subst.smap.targs
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -300,19 +330,14 @@ func (subst *subster) typ(typ Type) Type {
|
|||
named.tparams = t.tparams // new type is still parameterized
|
||||
named.targs = new_targs
|
||||
subst.cache[t] = named
|
||||
dump(">>> subst %s(%s) with %s (new: %s)", t.underlying, subst.tpars, subst.targs, new_targs)
|
||||
dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, new_targs)
|
||||
named.underlying = subst.typ(t.underlying)
|
||||
named.methods = t.methods // method signatures are updated lazily
|
||||
|
||||
return named
|
||||
|
||||
case *TypeParam:
|
||||
// TODO(gri) Can we do this with direct indexing somehow? Or use a map instead?
|
||||
for i, tpar := range subst.tpars {
|
||||
if tpar.typ == t {
|
||||
return subst.targs[i]
|
||||
}
|
||||
}
|
||||
return subst.smap.lookup(t)
|
||||
|
||||
default:
|
||||
panic("unimplemented")
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
|
|||
// TODO(gri) should we assume now that bounds always exist?
|
||||
// (no bound == empty interface)
|
||||
if bound != nil {
|
||||
bound = check.subst(tname.pos, bound, recvTParams, list)
|
||||
bound = check.subst(tname.pos, bound, makeSubstMap(recvTParams, list))
|
||||
tname.typ.(*TypeParam).bound = bound
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue