diff --git a/src/go/types/NOTES b/src/go/types/NOTES index 795d992b2b..6d521d61cc 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -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. diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index 7cbbf1d0da..5a767e5cad 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -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 diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 07377c4e4e..efdcaba5aa 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -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 diff --git a/src/go/types/object.go b/src/go/types/object.go index 4c0613fcea..45517280e0 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -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 { diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index c7491a31f4..c44702a09e 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -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 { diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 0904ac6a58..e297ce8a84 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -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) diff --git a/src/go/types/type.go b/src/go/types/type.go index 4d183fcaf6..8a118f00a3 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -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. diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go index 8ec016679f..c2b1026344 100644 --- a/src/go/types/typestring.go +++ b/src/go/types/typestring.go @@ -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