mirror of
https://github.com/golang/go.git
synced 2024-09-21 10:28:27 +00:00
go/types: use a TypeList type to hold type arguments
This resolves an asymmetry between the TParams and TArgs APIs, and reduces the size of the Named struct at the cost of some additional nil checks. While at it, move TParamList and TypeList to a new file:typelists.go, and change TParamList to access the tparams slice directly in At. There is no reason to guard against a nil receiver, as accessing an index on the empty slice will panic anyway. Change-Id: I9b65247e06c697a57a4efe40c3390e0faff91441 Reviewed-on: https://go-review.googlesource.com/c/go/+/343933 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
2438660602
commit
7a6d64fed6
@ -330,7 +330,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
|
||||||
|
@ -131,7 +131,7 @@ func (check *Checker) instance(pos token.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 = &TypeList{targs}
|
||||||
named.instance = &instance{pos}
|
named.instance = &instance{pos}
|
||||||
if check != nil {
|
if check != nil {
|
||||||
check.typMap[h] = named
|
check.typMap[h] = named
|
||||||
@ -139,7 +139,7 @@ func (check *Checker) instance(pos token.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 {
|
||||||
@ -174,14 +174,14 @@ func (check *Checker) instance(pos token.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 token.Pos, tparams *TParamList, targs []Type) bool {
|
func (check *Checker) validateTArgLen(pos token.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(atPos(pos), _Todo, "got %d arguments but %d type parameters", len(targs), tparams.Len())
|
check.errorf(atPos(pos), _Todo, "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
|
||||||
}
|
}
|
||||||
|
@ -392,10 +392,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(token.NoPos, ftyp, makeSubstMap(ftyp.RParams().list(), Vn.targs), nil).(*Signature)
|
ftyp = check.subst(token.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
|
||||||
|
@ -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 // syntactic information for lazy instantiation
|
instance *instance // syntactic information for lazy instantiation
|
||||||
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,12 +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) }
|
||||||
|
|
||||||
// NumTArgs returns the number of type arguments used to instantiate the named
|
// TArgs returns the type arguments used to instantiate the named type t.
|
||||||
// type t, or 0 if t is not an instantiated type.
|
func (t *Named) TArgs() *TypeList { return t.targs }
|
||||||
func (t *Named) NumTArgs() int { return len(t.targs) }
|
|
||||||
|
|
||||||
// TArgs returns the i'th type argument of the named type t for 0 <= i < t.NumTArgs().
|
|
||||||
func (t *Named) TArg(i int) Type { return t.targs[i] }
|
|
||||||
|
|
||||||
// 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) }
|
||||||
@ -263,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
|
||||||
@ -272,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]
|
||||||
}
|
}
|
||||||
|
@ -305,22 +305,22 @@ 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 x.NumTArgs() != y.NumTArgs() {
|
if len(xargs) != len(yargs) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if nargs := x.NumTArgs(); nargs > 0 {
|
if nargs := len(xargs); nargs > 0 {
|
||||||
// Instances are identical if their original type and type arguments
|
// Instances are identical if their original type and type arguments
|
||||||
// are identical.
|
// are identical.
|
||||||
if !Identical(x.orig, y.orig) {
|
if !Identical(x.orig, y.orig) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i := 0; i < nargs; i++ {
|
for i := 0; i < nargs; i++ {
|
||||||
xa := x.TArg(i)
|
xa := xargs[i]
|
||||||
ya := y.TArg(i)
|
ya := yargs[i]
|
||||||
if !Identical(xa, ya) {
|
if !Identical(xa, ya) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,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},
|
||||||
|
@ -191,21 +191,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
|
||||||
}
|
}
|
||||||
@ -233,7 +233,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 = &TypeList{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
|
||||||
|
|
||||||
|
61
src/go/types/typelists.go
Normal file
61
src/go/types/typelists.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// 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 types
|
||||||
|
|
||||||
|
// 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 }
|
||||||
|
|
||||||
|
// 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}
|
||||||
|
}
|
@ -88,40 +88,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
|
||||||
|
|
||||||
|
@ -204,7 +204,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
|
||||||
|
@ -426,13 +426,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user