go/types: move tparams field out of TypeName and into Named

Type parameters are a property of the type, not the type name.

Change-Id: I479eafea80f9bfbd638e688ac0747cfa52df5da1
This commit is contained in:
Robert Griesemer 2019-12-20 20:01:47 -08:00
parent 1a93d0a2ca
commit d45f7ef80d
8 changed files with 44 additions and 65 deletions

View File

@ -6,9 +6,9 @@ TODO
- interface embedding doesn't take care of literal type constraints yet
(need an allTypes list, like we have an allMethods list?)
- type assertions on/against parameterized types
- consolidate Signature.tparams and Signature.mtparams?
- can we always set Named.targs?
- use []*TypeParam for tparams in subst
- move Func.tparams to Signature (as we have done for TypeName.tparams to Named)
- distinguish more clearly between Signature.tparams and mtparams (the latter are solely for recv type params)
- use []*TypeParam for tparams in subst? (unclear)
OPEN ISSUES
- using a contract and enumerating type arguments currently leads to an error (e.g. func f(type T C(T)) (x T) ... )
@ -29,6 +29,13 @@ DESIGN/IMPLEMENTATION
is not valid, no matter how T1 and T2 are instantiated (but if T1 and T2 were type aliases with
both of them having type int, the return x would be valid). In fact, the type parameters act more
like named types. With their underlying type being the interface by which they are bound.
like named types with the methods described by their type bound. But type parameters are never
interfaces. To determine: Given a type parameter P, is P == underlying(P) (like for basic types),
or is the the underlying type of P something else (like for defined types). Is there an observable
difference?
- 12/19/2019: rewrote contract handling: they are now treated as Objects not Types throughout
- 12/19/2019: Rewrote contract handling: they are now treated as Objects (rather than Types) throughout.
- 12/20/2019: Decided to start moving type parameters to types (from TypeName to Named), need to do the
same for Func. This make more sense as in general any type (conceptually even literal types) could
have type parameters. It's a property of the type, not the type name. It also simplified the code.

View File

@ -46,8 +46,8 @@ func (check *Checker) contractDecl(contr *Contract, e *ast.ContractSpec) {
if named == nil {
index := tpar.typ.(*TypeParam).index
tname := NewTypeName(e.Pos(), check.pkg, contr.name+string(subscript(uint64(index))), nil)
tname.tparams = tparams
named = NewNamed(tname, new(Interface), nil)
named.tparams = tparams
bounds[tpar] = named
}
return named

View File

@ -572,7 +572,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
if tdecl.TParams != nil {
check.openScope(tdecl, "type parameters")
obj.tparams = check.collectTypeParams(tdecl.TParams)
named.tparams = check.collectTypeParams(tdecl.TParams)
}
// determine underlying type of named
@ -779,7 +779,7 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
if rname != nil {
// recv should be a Named type (otherwise an error is reported elsewhere)
if recv, _ := check.genericType(rname).(*Named); recv != nil {
recvTParams = recv.obj.tparams
recvTParams = recv.tparams
}
}
// provide type parameter bounds

View File

@ -213,7 +213,6 @@ func (*Const) isDependency() {} // a constant may be a dependency of an initiali
// A TypeName represents a name for a (defined or alias) type.
type TypeName struct {
object
tparams []*TypeName // type parameters from left to right; or nil
}
// NewTypeName returns a new type name denoting the given typ.
@ -224,12 +223,7 @@ type TypeName struct {
// argument for NewNamed, which will set the TypeName's type as a side-
// effect.
func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, nil}
}
// IsParameterized reports whether obj is a parametrized type.
func (obj *TypeName) IsParameterized() bool {
return len(obj.tparams) > 0
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}}
}
// IsAlias reports whether obj is an alias name for a type.
@ -256,17 +250,6 @@ func (obj *TypeName) IsAlias() bool {
}
}
// TypeParams returns the list if *TypeParam types for the type parameters of obj; or nil.
func (obj *TypeName) TypeParams() (tparams []*TypeParam) {
if n := len(obj.tparams); n > 0 {
tparams = make([]*TypeParam, n)
for i, tpar := range obj.tparams {
tparams[i] = tpar.typ.(*TypeParam)
}
}
return
}
// A Variable represents a declared variable (including function parameters and results, and struct fields).
type Var struct {
object
@ -499,24 +482,6 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
if _, ok := typ.(*Basic); ok {
return
}
if tname.IsParameterized() {
fmt.Fprint(buf, "(type ")
for i, p := range tname.tparams {
if i > 0 {
fmt.Fprint(buf, ", ")
}
buf.WriteString(p.name)
if p.typ != nil {
if ptyp, _ := p.typ.(*TypeParam); ptyp != nil && ptyp.bound != nil {
buf.WriteByte(' ')
WriteType(buf, ptyp.bound, qf)
// TODO(gri) if this is a generic type bound, we should print
// the type parameters
}
}
}
fmt.Fprint(buf, ")")
}
if tname.IsAlias() {
buf.WriteString(" =")
} else {

View File

@ -23,7 +23,7 @@ func isNamed(typ Type) bool {
func isGeneric(typ Type) bool {
// A parameterized type is only instantiated if it doesn't have an instantiation already.
named, _ := typ.(*Named)
return named != nil && named.obj != nil && named.obj.IsParameterized() && named.targs == nil
return named != nil && named.obj != nil && named.tparams != nil && named.targs == nil
}
func is(typ Type, what BasicInfo) bool {

View File

@ -34,7 +34,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
var tparams []*TypeName
switch typ := typ.(type) {
case *Named:
tparams = typ.obj.tparams
tparams = typ.tparams
case *Signature:
tparams = typ.tparams
default:
@ -272,7 +272,7 @@ func (subst *subster) typ(typ Type) Type {
}
}
if len(t.obj.tparams) == 0 {
if t.tparams == nil {
dump(">>> %s is not parameterized", t)
return t // type is not parameterized
}
@ -293,7 +293,7 @@ func (subst *subster) typ(typ Type) Type {
if new_targ != targ {
dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
if new_targs == nil {
new_targs = make([]Type, len(t.obj.tparams))
new_targs = make([]Type, len(t.tparams))
copy(new_targs, t.targs)
}
new_targs[i] = new_targ
@ -324,9 +324,9 @@ func (subst *subster) typ(typ Type) Type {
// create a new named type and populate caches to avoid endless recursion
tname := NewTypeName(subst.pos, subst.check.pkg, name, nil)
tname.tparams = t.obj.tparams // new type is still parameterized
subst.check.typMap[name] = tname
named := NewNamed(tname, nil, nil)
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)

View File

@ -474,12 +474,13 @@ func (c *Chan) Elem() Type { return c.elem }
// A Named represents a named (defined) type.
type Named struct {
info typeInfo // for cycle detection
obj *TypeName // corresponding declared object
orig Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
underlying Type // possibly a *Named during setup; never a *Named once set up completely
targs []Type // type arguments after instantiation
methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
info typeInfo // for cycle detection
obj *TypeName // corresponding declared object
orig Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
underlying Type // possibly a *Named during setup; never a *Named once set up completely
tparams []*TypeName // type parameters, or nil
targs []Type // 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
}
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.

View File

@ -259,16 +259,22 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
case *Named:
writeTypeName(buf, t.obj, qf)
// if t.obj != nil && len(t.obj.tparams) > 0 {
// buf.WriteByte('(')
// for i, tpar := range t.obj.tparams {
// if i > 0 {
// buf.WriteString(", ")
// }
// writeType(buf, tpar.typ, qf, visited)
// }
// buf.WriteByte(')')
// }
if t.tparams != nil {
buf.WriteString("(type ")
for i, p := range t.tparams {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(p.name)
if ptyp, _ := p.typ.(*TypeParam); ptyp != nil && ptyp.bound != nil {
buf.WriteByte(' ')
writeType(buf, ptyp.bound, qf, visited)
// TODO(gri) if this is a generic type bound, we should print
// the type parameters
}
}
buf.WriteByte(')')
}
case *TypeParam:
var s string