From 38c2ef660d360e594278f9f321f7554e1993d8e5 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 5 Feb 2020 15:19:56 -0800 Subject: [PATCH] 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 --- src/go/types/call.go | 4 +-- src/go/types/lookup.go | 2 +- src/go/types/subst.go | 69 ++++++++++++++++++++++++++++------------- src/go/types/typexpr.go | 2 +- 4 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/go/types/call.go b/src/go/types/call.go index 56d73cb169..44a0dea1e5 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -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 diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index d30cb41f32..8d9dda69a1 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -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 diff --git a/src/go/types/subst.go b/src/go/types/subst.go index db8d520c45..e6f2c67cc3 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -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") diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 699df40a39..b8fd02ca0a 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -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 } }