1
0
mirror of https://github.com/golang/go synced 2024-09-29 14:24:32 -06:00

cmd/compile/internal/types2: use a TypeList type to hold type arguments

This is a port of CL 343933 from go/types with the necessary
adjustments in the compiler.

With this CL type parameters and type lists are now held in
TParamList and TypeList data types which don't expose the
internal representation.

Change-Id: I6d60881b5db995dbc04ed3f4a96e8b5d41f83969
Reviewed-on: https://go-review.googlesource.com/c/go/+/344615
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Dan Scales <danscales@google.com>
This commit is contained in:
Robert Griesemer 2021-08-23 20:43:57 -07:00
parent 1ff0554b53
commit b1cdf860dd
15 changed files with 128 additions and 85 deletions

View File

@ -346,10 +346,10 @@ func (r *importReader) obj(name string) {
// rparams of the method (since those are the // rparams of the method (since those are the
// typeparams being used in the method sig/body). // typeparams being used in the method sig/body).
targs := baseType(msig.Recv().Type()).TArgs() targs := baseType(msig.Recv().Type()).TArgs()
if len(targs) > 0 { if targs.Len() > 0 {
rparams := make([]*types2.TypeParam, len(targs)) rparams := make([]*types2.TypeParam, targs.Len())
for i, targ := range targs { for i := range rparams {
rparams[i] = types2.AsTypeParam(targ) rparams[i] = types2.AsTypeParam(targs.At(i))
} }
msig.SetRParams(rparams) msig.SetRParams(rparams)
} }

View File

@ -360,9 +360,9 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
// selinfo.Targs() are the types used to // selinfo.Targs() are the types used to
// instantiate the type of receiver // instantiate the type of receiver
targs2 := getTargs(selinfo) targs2 := getTargs(selinfo)
targs := make([]ir.Node, len(targs2)) targs := make([]ir.Node, targs2.Len())
for i, targ2 := range targs2 { for i := range targs {
targs[i] = ir.TypeNode(g.typ(targ2)) targs[i] = ir.TypeNode(g.typ(targs2.At(i)))
} }
// Create function instantiation with the type // Create function instantiation with the type
@ -386,7 +386,7 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
} }
// getTargs gets the targs associated with the receiver of a selected method // getTargs gets the targs associated with the receiver of a selected method
func getTargs(selinfo *types2.Selection) []types2.Type { func getTargs(selinfo *types2.Selection) *types2.TypeList {
r := deref2(selinfo.Recv()) r := deref2(selinfo.Recv())
n := types2.AsNamed(r) n := types2.AsNamed(r)
if n == nil { if n == nil {

View File

@ -66,10 +66,12 @@ func (g *irgen) typ1(typ types2.Type) *types.Type {
// instTypeName2 creates a name for an instantiated type, base on the type args // instTypeName2 creates a name for an instantiated type, base on the type args
// (given as types2 types). // (given as types2 types).
func instTypeName2(name string, targs []types2.Type) string { func instTypeName2(name string, targs *types2.TypeList) string {
b := bytes.NewBufferString(name) b := bytes.NewBufferString(name)
b.WriteByte('[') b.WriteByte('[')
for i, targ := range targs { n := targs.Len()
for i := 0; i < n; i++ {
targ := targs.At(i)
if i > 0 { if i > 0 {
b.WriteByte(',') b.WriteByte(',')
} }
@ -140,9 +142,10 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
// non-generic types used to instantiate this type. We'll // non-generic types used to instantiate this type. We'll
// use these when instantiating the methods of the // use these when instantiating the methods of the
// instantiated type. // instantiated type.
rparams := make([]*types.Type, len(typ.TArgs())) targs := typ.TArgs()
for i, targ := range typ.TArgs() { rparams := make([]*types.Type, targs.Len())
rparams[i] = g.typ1(targ) for i := range rparams {
rparams[i] = g.typ1(targs.At(i))
} }
ntyp.SetRParams(rparams) ntyp.SetRParams(rparams)
//fmt.Printf("Saw new type %v %v\n", instName, ntyp.HasTParam()) //fmt.Printf("Saw new type %v %v\n", instName, ntyp.HasTParam())
@ -267,9 +270,10 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
// and for actually generating the methods for instantiated types. // and for actually generating the methods for instantiated types.
func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) { func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) {
if typ.NumMethods() != 0 { if typ.NumMethods() != 0 {
targs := make([]*types.Type, len(typ.TArgs())) targs2 := typ.TArgs()
for i, targ := range typ.TArgs() { targs := make([]*types.Type, targs2.Len())
targs[i] = g.typ1(targ) for i := range targs {
targs[i] = g.typ1(targs2.At(i))
} }
methods := make([]*types.Field, typ.NumMethods()) methods := make([]*types.Field, typ.NumMethods())

View File

@ -299,7 +299,7 @@ func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
// Type aliases can refer to uninstantiated generic types, so we // Type aliases can refer to uninstantiated generic types, so we
// might see len(TParams) != 0 && len(TArgs) == 0 here. // might see len(TParams) != 0 && len(TArgs) == 0 here.
// TODO(mdempsky): Revisit after #46477 is resolved. // TODO(mdempsky): Revisit after #46477 is resolved.
assert(typ.TParams().Len() == len(typ.TArgs()) || len(typ.TArgs()) == 0) assert(typ.TParams().Len() == typ.TArgs().Len() || typ.TArgs().Len() == 0)
// TODO(mdempsky): Why do we need to loop here? // TODO(mdempsky): Why do we need to loop here?
orig := typ orig := typ
@ -441,10 +441,10 @@ func (w *writer) param(param *types2.Var) {
// @@@ Objects // @@@ Objects
func (w *writer) obj(obj types2.Object, explicits []types2.Type) { func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) {
explicitInfos := make([]typeInfo, len(explicits)) explicitInfos := make([]typeInfo, explicits.Len())
for i, explicit := range explicits { for i := range explicitInfos {
explicitInfos[i] = w.p.typIdx(explicit, w.dict) explicitInfos[i] = w.p.typIdx(explicits.At(i), w.dict)
} }
info := objInfo{idx: w.p.objIdx(obj), explicits: explicitInfos} info := objInfo{idx: w.p.objIdx(obj), explicits: explicitInfos}
@ -1212,7 +1212,7 @@ func (w *writer) expr(expr syntax.Expr) {
if obj != nil { if obj != nil {
if isGlobal(obj) { if isGlobal(obj) {
w.code(exprName) w.code(exprName)
w.obj(obj, targs) w.obj(obj, types2.NewTypeList(targs))
return return
} }
@ -1321,7 +1321,7 @@ func (w *writer) expr(expr syntax.Expr) {
// As if w.expr(expr.Fun), but using inf.TArgs instead. // As if w.expr(expr.Fun), but using inf.TArgs instead.
w.code(exprName) w.code(exprName)
w.obj(obj, inf.TArgs) w.obj(obj, types2.NewTypeList(inf.TArgs))
} else { } else {
w.expr(expr.Fun) w.expr(expr.Fun)
} }
@ -1711,7 +1711,7 @@ func (w *writer) pkgDecl(decl syntax.Decl) {
// TODO(mdempsky): Revisit after #46477 is resolved. // TODO(mdempsky): Revisit after #46477 is resolved.
if name.IsAlias() { if name.IsAlias() {
named, ok := name.Type().(*types2.Named) named, ok := name.Type().(*types2.Named)
if ok && named.TParams().Len() != 0 && len(named.TArgs()) == 0 { if ok && named.TParams().Len() != 0 && named.TArgs().Len() == 0 {
break break
} }
} }

View File

@ -335,7 +335,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
return w.isParameterized(t.elem) return w.isParameterized(t.elem)
case *Named: case *Named:
return w.isParameterizedTypeList(t.targs) return w.isParameterizedTypeList(t.targs.list())
case *TypeParam: case *TypeParam:
// t must be one of w.tparams // t must be one of w.tparams

View File

@ -137,7 +137,7 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) (res Type
tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil) tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil)
named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is loaded named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is loaded
named.targs = targs named.targs = NewTypeList(targs)
named.instance = &instance{pos} named.instance = &instance{pos}
if check != nil { if check != nil {
check.typMap[h] = named check.typMap[h] = named
@ -145,7 +145,7 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) (res Type
res = named res = named
case *Signature: case *Signature:
tparams := t.TParams() tparams := t.TParams()
if !check.validateTArgLen(pos, tparams, targs) { if !check.validateTArgLen(pos, tparams.Len(), len(targs)) {
return Typ[Invalid] return Typ[Invalid]
} }
if tparams.Len() == 0 { if tparams.Len() == 0 {
@ -180,14 +180,14 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) (res Type
// validateTArgLen verifies that the length of targs and tparams matches, // validateTArgLen verifies that the length of targs and tparams matches,
// reporting an error if not. If validation fails and check is nil, // reporting an error if not. If validation fails and check is nil,
// validateTArgLen panics. // validateTArgLen panics.
func (check *Checker) validateTArgLen(pos syntax.Pos, tparams *TParamList, targs []Type) bool { func (check *Checker) validateTArgLen(pos syntax.Pos, ntparams, ntargs int) bool {
if len(targs) != tparams.Len() { if ntargs != ntparams {
// TODO(gri) provide better error message // TODO(gri) provide better error message
if check != nil { if check != nil {
check.errorf(pos, "got %d arguments but %d type parameters", len(targs), tparams.Len()) check.errorf(pos, "got %d arguments but %d type parameters", ntargs, ntparams)
return false return false
} }
panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, len(targs), tparams.Len())) panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, ntargs, ntparams))
} }
return true return true
} }

View File

@ -394,10 +394,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// here. Exit early in this case to prevent an assertion // here. Exit early in this case to prevent an assertion
// failure in makeSubstMap. // failure in makeSubstMap.
// TODO(gri) Can we avoid this check by fixing the lengths? // TODO(gri) Can we avoid this check by fixing the lengths?
if len(ftyp.RParams().list()) != len(Vn.targs) { if len(ftyp.RParams().list()) != Vn.targs.Len() {
return return
} }
ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.RParams().list(), Vn.targs), nil).(*Signature) ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.RParams().list(), Vn.targs.list()), nil).(*Signature)
} }
// If the methods have type parameters we don't care whether they // If the methods have type parameters we don't care whether they

View File

@ -21,7 +21,7 @@ type Named struct {
underlying Type // possibly a *Named during setup; never a *Named once set up completely underlying Type // possibly a *Named during setup; never a *Named once set up completely
instance *instance // position information for lazy instantiation, or nil instance *instance // position information for lazy instantiation, or nil
tparams *TParamList // type parameters, or nil tparams *TParamList // type parameters, or nil
targs []Type // type arguments (after instantiation), or nil targs *TypeList // type arguments (after instantiation), or nil
methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
resolve func(*Named) ([]*TypeParam, Type, []*Func) resolve func(*Named) ([]*TypeParam, Type, []*Func)
@ -46,7 +46,7 @@ func (t *Named) load() *Named {
// underlying is set when t is expanded. // underlying is set when t is expanded.
// //
// By convention, a type instance is loaded iff its tparams are set. // By convention, a type instance is loaded iff its tparams are set.
if len(t.targs) > 0 && t.tparams == nil { if t.targs.Len() > 0 && t.tparams == nil {
t.orig.load() t.orig.load()
t.tparams = t.orig.tparams t.tparams = t.orig.tparams
t.methods = t.orig.methods t.methods = t.orig.methods
@ -128,8 +128,8 @@ func (t *Named) TParams() *TParamList { return t.load().tparams }
// SetTParams sets the type parameters of the named type t. // SetTParams sets the type parameters of the named type t.
func (t *Named) SetTParams(tparams []*TypeParam) { t.load().tparams = bindTParams(tparams) } func (t *Named) SetTParams(tparams []*TypeParam) { t.load().tparams = bindTParams(tparams) }
// TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated. // TArgs returns the type arguments used to instantiate the named type t.
func (t *Named) TArgs() []Type { return t.targs } func (t *Named) TArgs() *TypeList { return t.targs }
// NumMethods returns the number of explicit methods whose receiver is named type t. // NumMethods returns the number of explicit methods whose receiver is named type t.
func (t *Named) NumMethods() int { return len(t.load().methods) } func (t *Named) NumMethods() int { return len(t.load().methods) }
@ -259,7 +259,7 @@ func (n *Named) expand(typMap map[string]*Named) *Named {
// explicit is harmless: load is idempotent. // explicit is harmless: load is idempotent.
n.load() n.load()
var u Type var u Type
if n.check.validateTArgLen(n.instance.pos, n.tparams, n.targs) { if n.check.validateTArgLen(n.instance.pos, n.tparams.Len(), n.targs.Len()) {
if typMap == nil { if typMap == nil {
if n.check != nil { if n.check != nil {
typMap = n.check.typMap typMap = n.check.typMap
@ -268,11 +268,11 @@ func (n *Named) expand(typMap map[string]*Named) *Named {
// type-checking pass. In that case we won't have a pre-existing // type-checking pass. In that case we won't have a pre-existing
// typMap, but don't want to create a duplicate of the current instance // typMap, but don't want to create a duplicate of the current instance
// in the process of expansion. // in the process of expansion.
h := instantiatedHash(n.orig, n.targs) h := instantiatedHash(n.orig, n.targs.list())
typMap = map[string]*Named{h: n} typMap = map[string]*Named{h: n}
} }
} }
u = n.check.subst(n.instance.pos, n.orig.underlying, makeSubstMap(n.TParams().list(), n.targs), typMap) u = n.check.subst(n.instance.pos, n.orig.underlying, makeSubstMap(n.TParams().list(), n.targs.list()), typMap)
} else { } else {
u = Typ[Invalid] u = Typ[Invalid]
} }

View File

@ -305,8 +305,8 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
x.expand(nil) x.expand(nil)
y.expand(nil) y.expand(nil)
xargs := x.TArgs() xargs := x.TArgs().list()
yargs := y.TArgs() yargs := y.TArgs().list()
if len(xargs) != len(yargs) { if len(xargs) != len(yargs) {
return false return false

View File

@ -31,7 +31,7 @@ func TestSizeof(t *testing.T) {
{Interface{}, 40, 80}, {Interface{}, 40, 80},
{Map{}, 16, 32}, {Map{}, 16, 32},
{Chan{}, 12, 24}, {Chan{}, 12, 24},
{Named{}, 80, 152}, {Named{}, 72, 136},
{TypeParam{}, 28, 48}, {TypeParam{}, 28, 48},
{term{}, 12, 24}, {term{}, 12, 24},
{top{}, 0, 0}, {top{}, 0, 0},

View File

@ -188,21 +188,21 @@ func (subst *subster) typ(typ Type) Type {
} }
var newTArgs []Type var newTArgs []Type
assert(len(t.targs) == t.TParams().Len()) assert(t.targs.Len() == t.TParams().Len())
// already instantiated // already instantiated
dump(">>> %s already instantiated", t) dump(">>> %s already instantiated", t)
// For each (existing) type argument targ, determine if it needs // For each (existing) type argument targ, determine if it needs
// to be substituted; i.e., if it is or contains a type parameter // to be substituted; i.e., if it is or contains a type parameter
// that has a type argument for it. // that has a type argument for it.
for i, targ := range t.targs { for i, targ := range t.targs.list() {
dump(">>> %d targ = %s", i, targ) dump(">>> %d targ = %s", i, targ)
new_targ := subst.typ(targ) new_targ := subst.typ(targ)
if new_targ != targ { if new_targ != targ {
dump(">>> substituted %d targ %s => %s", i, targ, new_targ) dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
if newTArgs == nil { if newTArgs == nil {
newTArgs = make([]Type, t.TParams().Len()) newTArgs = make([]Type, t.TParams().Len())
copy(newTArgs, t.targs) copy(newTArgs, t.targs.list())
} }
newTArgs[i] = new_targ newTArgs[i] = new_targ
} }
@ -230,7 +230,7 @@ func (subst *subster) typ(typ Type) Type {
// It's ok to provide a nil *Checker because the newly created type // It's ok to provide a nil *Checker because the newly created type
// doesn't need to be (lazily) expanded; it's expanded below. // doesn't need to be (lazily) expanded; it's expanded below.
named := (*Checker)(nil).newNamed(tname, t.orig, nil, t.tparams, t.methods) // t is loaded, so tparams and methods are available named := (*Checker)(nil).newNamed(tname, t.orig, nil, t.tparams, t.methods) // t is loaded, so tparams and methods are available
named.targs = newTArgs named.targs = NewTypeList(newTArgs)
subst.typMap[h] = named subst.typMap[h] = named
t.expand(subst.typMap) // must happen after typMap update to avoid infinite recursion t.expand(subst.typMap) // must happen after typMap update to avoid infinite recursion

View File

@ -0,0 +1,69 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types2
// TParamList holds a list of type parameters.
type TParamList struct{ tparams []*TypeParam }
// Len returns the number of type parameters in the list.
// It is safe to call on a nil receiver.
func (l *TParamList) Len() int { return len(l.list()) }
// At returns the i'th type parameter in the list.
func (l *TParamList) At(i int) *TypeParam { return l.tparams[i] }
// list is for internal use where we expect a []*TypeParam.
// TODO(rfindley): list should probably be eliminated: we can pass around a
// TParamList instead.
func (l *TParamList) list() []*TypeParam {
if l == nil {
return nil
}
return l.tparams
}
// TypeList holds a list of types.
type TypeList struct{ types []Type }
// NewTypeList returns a new TypeList with the types in list.
func NewTypeList(list []Type) *TypeList {
if len(list) == 0 {
return nil
}
return &TypeList{list}
}
// Len returns the number of types in the list.
// It is safe to call on a nil receiver.
func (l *TypeList) Len() int { return len(l.list()) }
// At returns the i'th type in the list.
func (l *TypeList) At(i int) Type { return l.types[i] }
// list is for internal use where we expect a []Type.
// TODO(rfindley): list should probably be eliminated: we can pass around a
// TypeList instead.
func (l *TypeList) list() []Type {
if l == nil {
return nil
}
return l.types
}
// ----------------------------------------------------------------------------
// Implementation
func bindTParams(list []*TypeParam) *TParamList {
if len(list) == 0 {
return nil
}
for i, typ := range list {
if typ.index >= 0 {
panic("type parameter bound more than once")
}
typ.index = i
}
return &TParamList{tparams: list}
}

View File

@ -83,40 +83,6 @@ func (t *TypeParam) SetConstraint(bound Type) {
func (t *TypeParam) Underlying() Type { return t } func (t *TypeParam) Underlying() Type { return t }
func (t *TypeParam) String() string { return TypeString(t, nil) } func (t *TypeParam) String() string { return TypeString(t, nil) }
// TParamList holds a list of type parameters bound to a type.
type TParamList struct{ tparams []*TypeParam }
// Len returns the number of type parameters in the list.
// It is safe to call on a nil receiver.
func (tps *TParamList) Len() int {
return len(tps.list())
}
// At returns the i'th type parameter in the list.
func (tps *TParamList) At(i int) *TypeParam {
return tps.list()[i]
}
func (tps *TParamList) list() []*TypeParam {
if tps == nil {
return nil
}
return tps.tparams
}
func bindTParams(list []*TypeParam) *TParamList {
if len(list) == 0 {
return nil
}
for i, typ := range list {
if typ.index >= 0 {
panic("type parameter bound more than once")
}
typ.index = i
}
return &TParamList{tparams: list}
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Implementation // Implementation

View File

@ -202,7 +202,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
if t.targs != nil { if t.targs != nil {
// instantiated type // instantiated type
buf.WriteByte('[') buf.WriteByte('[')
writeTypeList(buf, t.targs, qf, visited) writeTypeList(buf, t.targs.list(), qf, visited)
buf.WriteByte(']') buf.WriteByte(']')
} else if t.TParams().Len() != 0 { } else if t.TParams().Len() != 0 {
// parameterized type // parameterized type

View File

@ -429,13 +429,17 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
if y, ok := y.(*Named); ok { if y, ok := y.(*Named); ok {
x.expand(nil) x.expand(nil)
y.expand(nil) y.expand(nil)
xargs := x.targs.list()
yargs := y.targs.list()
// TODO(gri) This is not always correct: two types may have the same names // TODO(gri) This is not always correct: two types may have the same names
// in the same package if one of them is nested in a function. // in the same package if one of them is nested in a function.
// Extremely unlikely but we need an always correct solution. // Extremely unlikely but we need an always correct solution.
if x.obj.pkg == y.obj.pkg && x.obj.name == y.obj.name { if x.obj.pkg == y.obj.pkg && x.obj.name == y.obj.name {
assert(len(x.targs) == len(y.targs)) assert(len(xargs) == len(yargs))
for i, x := range x.targs { for i, x := range xargs {
if !u.nify(x, y.targs[i], p) { if !u.nify(x, yargs[i], p) {
return false return false
} }
} }