aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal
diff options
context:
space:
mode:
authorDan Scales <danscales@google.com>2021-02-11 10:50:20 -0800
committerDan Scales <danscales@google.com>2021-02-18 22:37:06 +0000
commit20050a15fee5b03735d6a14fcd96c059a05e149c (patch)
treeebe294d807231d8d57b0d85a448488378f7fce4e /src/cmd/compile/internal
parent2ff1e05a4ce132dec2a640d9fa941c2d33f90c36 (diff)
downloadgo-20050a15fee5b03735d6a14fcd96c059a05e149c.tar.gz
go-20050a15fee5b03735d6a14fcd96c059a05e149c.zip
[dev.typeparams] cmd/compile: support generic types (with stenciling of method calls)
A type may now have a type param in it, either because it has been composed from a function type param, or it has been declared as or derived from a reference to a generic type. No objects or types with type params can be exported yet. No generic type has a runtime descriptor (but will likely eventually be associated with a dictionary). types.Type now has an RParam field, which for a Named type can specify the type params (in order) that must be supplied to fully instantiate the type. Also, there is a new flag HasTParam to indicate if there is a type param (TTYPEPARAM) anywhere in the type. An instantiated generic type (whether fully instantiated or re-instantiated to new type params) is a defined type, even though there was no explicit declaration. This allows us to handle recursive instantiated types (and improves printing of types). To avoid the need to transform later in the compiler, an instantiation of a method of a generic type is immediately represented as a function with the method as the first argument. Added 5 tests on generic types to test/typeparams, including list.go, which tests recursive generic types. Change-Id: Ib7ff27abd369a06d1c8ea84edc6ca1fd74bbb7c2 Reviewed-on: https://go-review.googlesource.com/c/go/+/292652 Trust: Dan Scales <danscales@google.com> Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Dan Scales <danscales@google.com> Reviewed-by: Robert Griesemer <gri@golang.org>
Diffstat (limited to 'src/cmd/compile/internal')
-rw-r--r--src/cmd/compile/internal/gc/export.go5
-rw-r--r--src/cmd/compile/internal/noder/expr.go50
-rw-r--r--src/cmd/compile/internal/noder/irgen.go2
-rw-r--r--src/cmd/compile/internal/noder/stencil.go144
-rw-r--r--src/cmd/compile/internal/noder/types.go71
-rw-r--r--src/cmd/compile/internal/reflectdata/reflect.go5
-rw-r--r--src/cmd/compile/internal/types/sizeof_test.go2
-rw-r--r--src/cmd/compile/internal/types/type.go52
-rw-r--r--src/cmd/compile/internal/types2/selection.go16
9 files changed, 315 insertions, 32 deletions
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index 356fcfa671..4d8221f53b 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -25,6 +25,11 @@ func exportf(bout *bio.Writer, format string, args ...interface{}) {
func dumpexport(bout *bio.Writer) {
p := &exporter{marked: make(map[*types.Type]bool)}
for _, n := range typecheck.Target.Exports {
+ // Must catch it here rather than Export(), because the type can be
+ // not fully set (still TFORW) when Export() is called.
+ if n.Type() != nil && n.Type().HasTParam() {
+ base.Fatalf("Cannot (yet) export a generic type: %v", n)
+ }
p.markObject(n)
}
diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go
index 3d6fba2dfe..2819c8252d 100644
--- a/src/cmd/compile/internal/noder/expr.go
+++ b/src/cmd/compile/internal/noder/expr.go
@@ -209,7 +209,7 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
// interface embedding).
var n ir.Node
- method := selinfo.Obj().(*types2.Func)
+ method2 := selinfo.Obj().(*types2.Func)
if kind == types2.MethodExpr {
// OMETHEXPR is unusual in using directly the node and type of the
@@ -221,9 +221,11 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
n = MethodExpr(pos, origx, x.Type(), last)
} else {
// Add implicit addr/deref for method values, if needed.
- if !x.Type().IsInterface() {
- recvTyp := method.Type().(*types2.Signature).Recv().Type()
- _, wantPtr := recvTyp.(*types2.Pointer)
+ if x.Type().IsInterface() {
+ n = DotMethod(pos, x, last)
+ } else {
+ recvType2 := method2.Type().(*types2.Signature).Recv().Type()
+ _, wantPtr := recvType2.(*types2.Pointer)
havePtr := x.Type().IsPtr()
if havePtr != wantPtr {
@@ -233,13 +235,45 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
x = Implicit(Addr(pos, x))
}
}
- if !g.match(x.Type(), recvTyp, false) {
- base.FatalfAt(pos, "expected %L to have type %v", x, recvTyp)
+ recvType2Base := recvType2
+ if wantPtr {
+ recvType2Base = recvType2.Pointer().Elem()
+ }
+ if len(recvType2Base.Named().TParams()) > 0 {
+ // recvType2 is the original generic type that is
+ // instantiated for this method call.
+ // selinfo.Recv() is the instantiated type
+ recvType2 = recvType2Base
+ // method is the generic method associated with the gen type
+ method := g.obj(recvType2.Named().Method(last))
+ n = ir.NewSelectorExpr(pos, ir.OCALLPART, x, method.Sym())
+ n.(*ir.SelectorExpr).Selection = types.NewField(pos, method.Sym(), method.Type())
+ n.(*ir.SelectorExpr).Selection.Nname = method
+ typed(method.Type(), n)
+
+ // selinfo.Targs() are the types used to
+ // instantiate the type of receiver
+ targs2 := selinfo.TArgs()
+ targs := make([]ir.Node, len(targs2))
+ for i, targ2 := range targs2 {
+ targs[i] = ir.TypeNode(g.typ(targ2))
+ }
+
+ // Create function instantiation with the type
+ // args for the receiver type for the method call.
+ n = ir.NewInstExpr(pos, ir.OFUNCINST, n, targs)
+ typed(g.typ(typ), n)
+ return n
+ }
+
+ if !g.match(x.Type(), recvType2, false) {
+ base.FatalfAt(pos, "expected %L to have type %v", x, recvType2)
+ } else {
+ n = DotMethod(pos, x, last)
}
}
- n = DotMethod(pos, x, last)
}
- if have, want := n.Sym(), g.selector(method); have != want {
+ if have, want := n.Sym(), g.selector(method2); have != want {
base.FatalfAt(pos, "bad Sym: have %v, want %v", have, want)
}
return n
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go
index d4f1b7461a..28536cc1f7 100644
--- a/src/cmd/compile/internal/noder/irgen.go
+++ b/src/cmd/compile/internal/noder/irgen.go
@@ -195,7 +195,7 @@ Outer:
// eventually export any exportable generic functions.
j := 0
for i, decl := range g.target.Decls {
- if decl.Op() != ir.ODCLFUNC || decl.Type().NumTParams() == 0 {
+ if decl.Op() != ir.ODCLFUNC || !decl.Type().HasTParam() {
g.target.Decls[j] = g.target.Decls[i]
j++
}
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index 74ea2e0927..69461a8190 100644
--- a/src/cmd/compile/internal/noder/stencil.go
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -13,7 +13,9 @@ import (
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
+ "cmd/internal/src"
"fmt"
+ "strings"
)
// stencil scans functions for instantiated generic function calls and
@@ -61,6 +63,17 @@ func (g *irgen) stencil() {
// Replace the OFUNCINST with a direct reference to the
// new stenciled function
call.X = st.Nname
+ if inst.X.Op() == ir.OCALLPART {
+ // When we create an instantiation of a method
+ // call, we make it a function. So, move the
+ // receiver to be the first arg of the function
+ // call.
+ withRecv := make([]ir.Node, len(call.Args)+1)
+ dot := inst.X.(*ir.SelectorExpr)
+ withRecv[0] = dot.X
+ copy(withRecv[1:], call.Args)
+ call.Args = withRecv
+ }
modified = true
})
if base.Flag.W > 1 && modified {
@@ -74,7 +87,12 @@ func (g *irgen) stencil() {
// the name of the function and the types of the type params.
func makeInstName(inst *ir.InstExpr) *types.Sym {
b := bytes.NewBufferString("#")
- b.WriteString(inst.X.(*ir.Name).Name().Sym().Name)
+ if meth, ok := inst.X.(*ir.SelectorExpr); ok {
+ // Write the name of the generic method, including receiver type
+ b.WriteString(meth.Selection.Nname.Sym().Name)
+ } else {
+ b.WriteString(inst.X.(*ir.Name).Name().Sym().Name)
+ }
b.WriteString("[")
for i, targ := range inst.Targs {
if i > 0 {
@@ -90,18 +108,38 @@ func makeInstName(inst *ir.InstExpr) *types.Sym {
// instantiation of a generic function with specified type arguments.
type subster struct {
newf *ir.Func // Func node for the new stenciled function
- tparams *types.Fields
+ tparams []*types.Field
targs []ir.Node
// The substitution map from name nodes in the generic function to the
// name nodes in the new stenciled function.
vars map[*ir.Name]*ir.Name
+ seen map[*types.Type]*types.Type
}
// genericSubst returns a new function with the specified name. The function is an
-// instantiation of a generic function with type params, as specified by inst.
+// instantiation of a generic function or method with type params, as specified by
+// inst. For a method with a generic receiver, it returns an instantiated function
+// type where the receiver becomes the first parameter. Otherwise the instantiated
+// method would still need to be transformed by later compiler phases.
func genericSubst(name *types.Sym, inst *ir.InstExpr) *ir.Func {
- // Similar to noder.go: funcDecl
- nameNode := inst.X.(*ir.Name)
+ var nameNode *ir.Name
+ var tparams []*types.Field
+ if selExpr, ok := inst.X.(*ir.SelectorExpr); ok {
+ // Get the type params from the method receiver (after skipping
+ // over any pointer)
+ nameNode = ir.AsNode(selExpr.Selection.Nname).(*ir.Name)
+ recvType := selExpr.Type().Recv().Type
+ if recvType.IsPtr() {
+ recvType = recvType.Elem()
+ }
+ tparams = make([]*types.Field, len(recvType.RParams))
+ for i, rparam := range recvType.RParams {
+ tparams[i] = types.NewField(src.NoXPos, nil, rparam)
+ }
+ } else {
+ nameNode = inst.X.(*ir.Name)
+ tparams = nameNode.Type().TParams().Fields().Slice()
+ }
gf := nameNode.Func
newf := ir.NewFunc(inst.Pos())
newf.Nname = ir.NewNameAt(inst.Pos(), name)
@@ -111,9 +149,10 @@ func genericSubst(name *types.Sym, inst *ir.InstExpr) *ir.Func {
subst := &subster{
newf: newf,
- tparams: nameNode.Type().TParams().Fields(),
+ tparams: tparams,
targs: inst.Targs,
vars: make(map[*ir.Name]*ir.Name),
+ seen: make(map[*types.Type]*types.Type),
}
newf.Dcl = make([]*ir.Name, len(gf.Dcl))
@@ -125,9 +164,12 @@ func genericSubst(name *types.Sym, inst *ir.InstExpr) *ir.Func {
// Ugly: we have to insert the Name nodes of the parameters/results into
// the function type. The current function type has no Nname fields set,
// because it came via conversion from the types2 type.
- oldt := inst.Type()
- newt := types.NewSignature(oldt.Pkg(), nil, nil, subst.fields(ir.PPARAM, oldt.Params(), newf.Dcl),
- subst.fields(ir.PPARAMOUT, oldt.Results(), newf.Dcl))
+ oldt := inst.X.Type()
+ // We also transform a generic method type to the corresponding
+ // instantiated function type where the receiver is the first parameter.
+ newt := types.NewSignature(oldt.Pkg(), nil, nil,
+ subst.fields(ir.PPARAM, append(oldt.Recvs().FieldSlice(), oldt.Params().FieldSlice()...), newf.Dcl),
+ subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl))
newf.Nname.Ntype = ir.TypeNode(newt)
newf.Nname.SetType(newt)
@@ -276,41 +318,81 @@ func (subst *subster) tstruct(t *types.Type) *types.Type {
}
-// typ substitutes any type parameter found with the corresponding type argument.
-func (subst *subster) typ(t *types.Type) *types.Type {
- for i, tp := range subst.tparams.Slice() {
- if tp.Type == t {
- return subst.targs[i].Type()
+// instTypeName creates a name for an instantiated type, based on the type args
+func instTypeName(name string, targs []ir.Node) string {
+ b := bytes.NewBufferString(name)
+ b.WriteByte('[')
+ for i, targ := range targs {
+ if i > 0 {
+ b.WriteByte(',')
}
+ b.WriteString(targ.Type().String())
+ }
+ b.WriteByte(']')
+ return b.String()
+}
+
+// typ computes the type obtained by substituting any type parameter in t with the
+// corresponding type argument in subst. If t contains no type parameters, the
+// result is t; otherwise the result is a new type.
+// It deals with recursive types by using a map and TFORW types.
+// TODO(danscales) deal with recursion besides ptr/struct cases.
+func (subst *subster) typ(t *types.Type) *types.Type {
+ if !t.HasTParam() {
+ return t
+ }
+ if subst.seen[t] != nil {
+ // We've hit a recursive type
+ return subst.seen[t]
}
+ var newt *types.Type
switch t.Kind() {
+ case types.TTYPEPARAM:
+ for i, tp := range subst.tparams {
+ if tp.Type == t {
+ return subst.targs[i].Type()
+ }
+ }
+ return t
+
case types.TARRAY:
elem := t.Elem()
newelem := subst.typ(elem)
if newelem != elem {
- return types.NewArray(newelem, t.NumElem())
+ newt = types.NewArray(newelem, t.NumElem())
}
case types.TPTR:
elem := t.Elem()
+ // In order to deal with recursive generic types, create a TFORW
+ // type initially and store it in the seen map, so it can be
+ // accessed if this type appears recursively within the type.
+ forw := types.New(types.TFORW)
+ subst.seen[t] = forw
newelem := subst.typ(elem)
if newelem != elem {
- return types.NewPtr(newelem)
+ forw.SetUnderlying(types.NewPtr(newelem))
+ newt = forw
}
+ delete(subst.seen, t)
case types.TSLICE:
elem := t.Elem()
newelem := subst.typ(elem)
if newelem != elem {
- return types.NewSlice(newelem)
+ newt = types.NewSlice(newelem)
}
case types.TSTRUCT:
- newt := subst.tstruct(t)
+ forw := types.New(types.TFORW)
+ subst.seen[t] = forw
+ newt = subst.tstruct(t)
if newt != t {
- return newt
+ forw.SetUnderlying(newt)
+ newt = forw
}
+ delete(subst.seen, t)
case types.TFUNC:
newrecvs := subst.tstruct(t.Recvs())
@@ -321,21 +403,39 @@ func (subst *subster) typ(t *types.Type) *types.Type {
if newrecvs.NumFields() > 0 {
newrecv = newrecvs.Field(0)
}
- return types.NewSignature(t.Pkg(), newrecv, nil, newparams.FieldSlice(), newresults.FieldSlice())
+ newt = types.NewSignature(t.Pkg(), newrecv, nil, newparams.FieldSlice(), newresults.FieldSlice())
}
// TODO: case TCHAN
// TODO: case TMAP
// TODO: case TINTER
}
+ if newt != nil {
+ if t.Sym() != nil {
+ // Since we've substituted types, we also need to change
+ // the defined name of the type, by removing the old types
+ // (in brackets) from the name, and adding the new types.
+ oldname := t.Sym().Name
+ i := strings.Index(oldname, "[")
+ oldname = oldname[:i]
+ sym := t.Sym().Pkg.Lookup(instTypeName(oldname, subst.targs))
+ if sym.Def != nil {
+ // We've already created this instantiated defined type.
+ return sym.Def.Type()
+ }
+ newt.SetSym(sym)
+ sym.Def = ir.TypeNode(newt)
+ }
+ return newt
+ }
+
return t
}
// fields sets the Nname field for the Field nodes inside a type signature, based
// on the corresponding in/out parameters in dcl. It depends on the in and out
// parameters being in order in dcl.
-func (subst *subster) fields(class ir.Class, oldt *types.Type, dcl []*ir.Name) []*types.Field {
- oldfields := oldt.FieldSlice()
+func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field {
newfields := make([]*types.Field, len(oldfields))
var i int
diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go
index 1e71969858..c23295c3a1 100644
--- a/src/cmd/compile/internal/noder/types.go
+++ b/src/cmd/compile/internal/noder/types.go
@@ -5,6 +5,7 @@
package noder
import (
+ "bytes"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
@@ -25,6 +26,8 @@ func (g *irgen) pkg(pkg *types2.Package) *types.Pkg {
return types.NewPkg(pkg.Path(), pkg.Name())
}
+// typ converts a types2.Type to a types.Type, including caching of previously
+// translated types.
func (g *irgen) typ(typ types2.Type) *types.Type {
// Caching type mappings isn't strictly needed, because typ0 preserves
// type identity; but caching minimizes memory blow-up from mapping the
@@ -46,11 +49,79 @@ func (g *irgen) typ(typ types2.Type) *types.Type {
return res
}
+// instTypeName2 creates a name for an instantiated type, base on the type args
+// (given as types2 types).
+func instTypeName2(name string, targs []types2.Type) string {
+ b := bytes.NewBufferString(name)
+ b.WriteByte('[')
+ for i, targ := range targs {
+ if i > 0 {
+ b.WriteByte(',')
+ }
+ b.WriteString(types2.TypeString(targ,
+ func(*types2.Package) string { return "" }))
+ }
+ b.WriteByte(']')
+ return b.String()
+}
+
+// typ0 converts a types2.Type to a types.Type, but doesn't do the caching check
+// at the top level.
func (g *irgen) typ0(typ types2.Type) *types.Type {
switch typ := typ.(type) {
case *types2.Basic:
return g.basic(typ)
case *types2.Named:
+ if typ.TParams() != nil {
+ // typ is an instantiation of a defined (named) generic type.
+ // This instantiation should also be a defined (named) type.
+ // types2 gives us the substituted type in t.Underlying()
+ // The substituted type may or may not still have type
+ // params. We might, for example, be substituting one type
+ // param for another type param.
+
+ if typ.TArgs() == nil {
+ base.Fatalf("In typ0, Targs should be set if TParams is set")
+ }
+
+ // When converted to types.Type, typ must have a name,
+ // based on the names of the type arguments. We need a
+ // name to deal with recursive generic types (and it also
+ // looks better when printing types).
+ instName := instTypeName2(typ.Obj().Name(), typ.TArgs())
+ s := g.pkg(typ.Obj().Pkg()).Lookup(instName)
+ if s.Def != nil {
+ // We have already encountered this instantiation,
+ // so use the type we previously created, since there
+ // must be exactly one instance of a defined type.
+ return s.Def.Type()
+ }
+
+ // Create a forwarding type first and put it in the g.typs
+ // map, in order to deal with recursive generic types.
+ ntyp := types.New(types.TFORW)
+ g.typs[typ] = ntyp
+ ntyp.SetUnderlying(g.typ(typ.Underlying()))
+ ntyp.SetSym(s)
+
+ if ntyp.HasTParam() {
+ // If ntyp still has type params, then we must be
+ // referencing something like 'value[T2]', as when
+ // specifying the generic receiver of a method,
+ // where value was defined as "type value[T any]
+ // ...". Save the type args, which will now be the
+ // new type params of the current type.
+ ntyp.RParams = make([]*types.Type, len(typ.TArgs()))
+ for i, targ := range typ.TArgs() {
+ ntyp.RParams[i] = g.typ(targ)
+ }
+ }
+
+ // Make sure instantiated type can be uniquely found from
+ // the sym
+ s.Def = ir.TypeNode(ntyp)
+ return ntyp
+ }
obj := g.obj(typ.Obj())
if obj.Op() != ir.OTYPE {
base.FatalfAt(obj.Pos(), "expected type: %L", obj)
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index 632e0f48d4..06a7f91c52 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -1288,6 +1288,11 @@ func ITabSym(it *obj.LSym, offset int64) *obj.LSym {
// NeedRuntimeType ensures that a runtime type descriptor is emitted for t.
func NeedRuntimeType(t *types.Type) {
+ if t.HasTParam() {
+ // Generic types don't have a runtime type descriptor (but will
+ // have a dictionary)
+ return
+ }
if _, ok := signatset[t]; !ok {
signatset[t] = struct{}{}
signatslice = append(signatslice, t)
diff --git a/src/cmd/compile/internal/types/sizeof_test.go b/src/cmd/compile/internal/types/sizeof_test.go
index 6937283d69..f80de937be 100644
--- a/src/cmd/compile/internal/types/sizeof_test.go
+++ b/src/cmd/compile/internal/types/sizeof_test.go
@@ -21,7 +21,7 @@ func TestSizeof(t *testing.T) {
_64bit uintptr // size on 64bit platforms
}{
{Sym{}, 44, 72},
- {Type{}, 56, 96},
+ {Type{}, 68, 120},
{Map{}, 20, 40},
{Forward{}, 20, 32},
{Func{}, 28, 48},
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index 987aa11454..b6374e49a5 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -176,6 +176,11 @@ type Type struct {
Align uint8 // the required alignment of this type, in bytes (0 means Width and Align have not yet been computed)
flags bitset8
+
+ // Type params (in order) of this named type that need to be instantiated.
+ // TODO(danscales): for space reasons, should probably be a pointer to a
+ // slice, possibly change the name of this field.
+ RParams []*Type
}
func (*Type) CanBeAnSSAAux() {}
@@ -186,6 +191,7 @@ const (
typeNoalg // suppress hash and eq algorithm generation
typeDeferwidth // width computation has been deferred and type is on deferredTypeStack
typeRecur
+ typeHasTParam // there is a typeparam somewhere in the type (generic function or type)
)
func (t *Type) NotInHeap() bool { return t.flags&typeNotInHeap != 0 }
@@ -193,12 +199,14 @@ func (t *Type) Broke() bool { return t.flags&typeBroke != 0 }
func (t *Type) Noalg() bool { return t.flags&typeNoalg != 0 }
func (t *Type) Deferwidth() bool { return t.flags&typeDeferwidth != 0 }
func (t *Type) Recur() bool { return t.flags&typeRecur != 0 }
+func (t *Type) HasTParam() bool { return t.flags&typeHasTParam != 0 }
func (t *Type) SetNotInHeap(b bool) { t.flags.set(typeNotInHeap, b) }
func (t *Type) SetBroke(b bool) { t.flags.set(typeBroke, b) }
func (t *Type) SetNoalg(b bool) { t.flags.set(typeNoalg, b) }
func (t *Type) SetDeferwidth(b bool) { t.flags.set(typeDeferwidth, b) }
func (t *Type) SetRecur(b bool) { t.flags.set(typeRecur, b) }
+func (t *Type) SetHasTParam(b bool) { t.flags.set(typeHasTParam, b) }
// Kind returns the kind of type t.
func (t *Type) Kind() Kind { return t.kind }
@@ -527,6 +535,9 @@ func NewArray(elem *Type, bound int64) *Type {
t := New(TARRAY)
t.Extra = &Array{Elem: elem, Bound: bound}
t.SetNotInHeap(elem.NotInHeap())
+ if elem.HasTParam() {
+ t.SetHasTParam(true)
+ }
return t
}
@@ -542,6 +553,9 @@ func NewSlice(elem *Type) *Type {
t := New(TSLICE)
t.Extra = Slice{Elem: elem}
elem.cache.slice = t
+ if elem.HasTParam() {
+ t.SetHasTParam(true)
+ }
return t
}
@@ -551,6 +565,9 @@ func NewChan(elem *Type, dir ChanDir) *Type {
ct := t.ChanType()
ct.Elem = elem
ct.Dir = dir
+ if elem.HasTParam() {
+ t.SetHasTParam(true)
+ }
return t
}
@@ -558,6 +575,9 @@ func NewTuple(t1, t2 *Type) *Type {
t := New(TTUPLE)
t.Extra.(*Tuple).first = t1
t.Extra.(*Tuple).second = t2
+ if t1.HasTParam() || t2.HasTParam() {
+ t.SetHasTParam(true)
+ }
return t
}
@@ -579,6 +599,9 @@ func NewMap(k, v *Type) *Type {
mt := t.MapType()
mt.Key = k
mt.Elem = v
+ if k.HasTParam() || v.HasTParam() {
+ t.SetHasTParam(true)
+ }
return t
}
@@ -597,6 +620,12 @@ func NewPtr(elem *Type) *Type {
if t.Elem() != elem {
base.Fatalf("NewPtr: elem mismatch")
}
+ if elem.HasTParam() {
+ // Extra check when reusing the cache, since the elem
+ // might have still been undetermined (i.e. a TFORW type)
+ // when this entry was cached.
+ t.SetHasTParam(true)
+ }
return t
}
@@ -607,6 +636,9 @@ func NewPtr(elem *Type) *Type {
if NewPtrCacheEnabled {
elem.cache.ptr = t
}
+ if elem.HasTParam() {
+ t.SetHasTParam(true)
+ }
return t
}
@@ -1611,6 +1643,9 @@ func (t *Type) SetUnderlying(underlying *Type) {
if underlying.Broke() {
t.SetBroke(true)
}
+ if underlying.HasTParam() {
+ t.SetHasTParam(true)
+ }
// spec: "The declared type does not inherit any methods bound
// to the existing type, but the method set of an interface
@@ -1633,6 +1668,15 @@ func (t *Type) SetUnderlying(underlying *Type) {
}
}
+func fieldsHasTParam(fields []*Field) bool {
+ for _, f := range fields {
+ if f.Type != nil && f.Type.HasTParam() {
+ return true
+ }
+ }
+ return false
+}
+
// NewBasic returns a new basic type of the given kind.
func NewBasic(kind Kind, obj Object) *Type {
t := New(kind)
@@ -1660,6 +1704,7 @@ func NewTypeParam(pkg *Pkg, constraint *Type) *Type {
constraint.wantEtype(TINTER)
t.methods = constraint.methods
t.Extra.(*Interface).pkg = pkg
+ t.SetHasTParam(true)
return t
}
@@ -1688,6 +1733,10 @@ func NewSignature(pkg *Pkg, recv *Field, tparams, params, results []*Field) *Typ
ft.Params = funargs(params, FunargParams)
ft.Results = funargs(results, FunargResults)
ft.pkg = pkg
+ if len(tparams) > 0 || fieldsHasTParam(recvs) || fieldsHasTParam(params) ||
+ fieldsHasTParam(results) {
+ t.SetHasTParam(true)
+ }
return t
}
@@ -1700,6 +1749,9 @@ func NewStruct(pkg *Pkg, fields []*Field) *Type {
t.SetBroke(true)
}
t.Extra.(*Struct).pkg = pkg
+ if fieldsHasTParam(fields) {
+ t.SetHasTParam(true)
+ }
return t
}
diff --git a/src/cmd/compile/internal/types2/selection.go b/src/cmd/compile/internal/types2/selection.go
index 8128aeee2e..4358458b88 100644
--- a/src/cmd/compile/internal/types2/selection.go
+++ b/src/cmd/compile/internal/types2/selection.go
@@ -51,6 +51,22 @@ func (s *Selection) Kind() SelectionKind { return s.kind }
// Recv returns the type of x in x.f.
func (s *Selection) Recv() Type { return s.recv }
+// Work-around for bug where a (*instance) shows up in a final type.
+// TODO(gri): fix this bug.
+func (s *Selection) TArgs() []Type {
+ r := s.recv
+ if r.Pointer() != nil {
+ r = r.Pointer().Elem()
+ }
+ if r.Named() != nil {
+ return r.Named().TArgs()
+ }
+ // The base type (after skipping any pointer) must be a Named type. The
+ // bug is that sometimes it can be an instance type (which is supposed to
+ // be an internal type only).
+ return r.(*instance).targs
+}
+
// Obj returns the object denoted by x.f; a *Var for
// a field selection, and a *Func in all other cases.
func (s *Selection) Obj() Object { return s.obj }