aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--test/typeparam/list.go65
-rw-r--r--test/typeparam/pair.go32
-rw-r--r--test/typeparam/stringable.go46
-rw-r--r--test/typeparam/struct.go49
-rw-r--r--test/typeparam/value.go75
14 files changed, 582 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 }
diff --git a/test/typeparam/list.go b/test/typeparam/list.go
new file mode 100644
index 0000000000..64230060de
--- /dev/null
+++ b/test/typeparam/list.go
@@ -0,0 +1,65 @@
+// run -gcflags=-G=3
+
+// 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 main
+
+import (
+ "fmt"
+)
+
+type Ordered interface {
+ type int, int8, int16, int32, int64,
+ uint, uint8, uint16, uint32, uint64, uintptr,
+ float32, float64,
+ string
+}
+
+// List is a linked list of ordered values of type T.
+type list[T Ordered] struct {
+ next *list[T]
+ val T
+}
+
+func (l *list[T]) largest() T {
+ var max T
+ for p := l; p != nil; p = p.next {
+ if p.val > max {
+ max = p.val
+ }
+ }
+ return max
+}
+
+
+func main() {
+ i3 := &list[int]{nil, 1}
+ i2 := &list[int]{i3, 3}
+ i1 := &list[int]{i2, 2}
+ if got, want := i1.largest(), 3; got != want {
+ panic(fmt.Sprintf("got %d, want %d", got, want))
+ }
+
+ b3 := &list[byte]{nil, byte(1)}
+ b2 := &list[byte]{b3, byte(3)}
+ b1 := &list[byte]{b2, byte(2)}
+ if got, want := b1.largest(), byte(3); got != want {
+ panic(fmt.Sprintf("got %d, want %d", got, want))
+ }
+
+ f3 := &list[float64]{nil, 13.5}
+ f2 := &list[float64]{f3, 1.2}
+ f1 := &list[float64]{f2, 4.5}
+ if got, want := f1.largest(), 13.5; got != want {
+ panic(fmt.Sprintf("got %f, want %f", got, want))
+ }
+
+ s3 := &list[string]{nil, "dd"}
+ s2 := &list[string]{s3, "aa"}
+ s1 := &list[string]{s2, "bb"}
+ if got, want := s1.largest(), "dd"; got != want {
+ panic(fmt.Sprintf("got %s, want %s", got, want))
+ }
+}
diff --git a/test/typeparam/pair.go b/test/typeparam/pair.go
new file mode 100644
index 0000000000..7faf083c89
--- /dev/null
+++ b/test/typeparam/pair.go
@@ -0,0 +1,32 @@
+// run -gcflags=-G=3
+
+// 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 main
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+type pair[F1, F2 any] struct {
+ f1 F1
+ f2 F2
+}
+
+func main() {
+ p := pair[int32, int64]{1, 2}
+ if got, want := unsafe.Sizeof(p.f1), uintptr(4); got != want {
+ panic(fmt.Sprintf("unexpected f1 size == %d, want %d", got, want))
+ }
+ if got, want := unsafe.Sizeof(p.f2), uintptr(8); got != want {
+ panic(fmt.Sprintf("unexpected f2 size == %d, want %d", got, want))
+ }
+ type mypair struct { f1 int32; f2 int64 }
+ mp := mypair(p)
+ if mp.f1 != 1 || mp.f2 != 2 {
+ panic(fmt.Sprintf("mp == %#v, want %#v", mp, mypair{1, 2}))
+ }
+}
diff --git a/test/typeparam/stringable.go b/test/typeparam/stringable.go
new file mode 100644
index 0000000000..9340a3b10a
--- /dev/null
+++ b/test/typeparam/stringable.go
@@ -0,0 +1,46 @@
+// run -gcflags=-G=3
+
+// 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 main
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+type Stringer interface {
+ String() string
+}
+
+// stringableList is a slice of some type, where the type
+// must have a String method.
+type stringableList[T Stringer] []T
+
+func (s stringableList[T]) String() string {
+ var sb strings.Builder
+ for i, v := range s {
+ if i > 0 {
+ sb.WriteString(", ")
+ }
+ sb.WriteString(v.String())
+ }
+ return sb.String()
+}
+
+type myint int
+
+func (a myint) String() string {
+ return strconv.Itoa(int(a))
+}
+
+func main() {
+ v := stringableList[myint]{ myint(1), myint(2) }
+
+ if got, want := v.String(), "1, 2"; got != want {
+ panic(fmt.Sprintf("got %s, want %s", got, want))
+ }
+}
diff --git a/test/typeparam/struct.go b/test/typeparam/struct.go
new file mode 100644
index 0000000000..98f0fcd888
--- /dev/null
+++ b/test/typeparam/struct.go
@@ -0,0 +1,49 @@
+// run -gcflags=-G=3
+
+// 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 main
+
+import (
+ "fmt"
+)
+
+type _E[T any] struct {
+ v T
+}
+
+type _S1 struct {
+ _E[int]
+ v string
+}
+
+type _Eint = _E[int]
+type _Ebool = _E[bool]
+
+type _S2 struct {
+ _Eint
+ _Ebool
+ v string
+}
+
+type _S3 struct {
+ *_E[int]
+}
+
+func main() {
+ s1 := _S1{_Eint{2}, "foo"}
+ if got, want := s1._E.v, 2; got != want {
+ panic(fmt.Sprintf("got %d, want %d", got, want))
+ }
+ s2 := _S2{_Eint{3}, _Ebool{true}, "foo"}
+ if got, want := s2._Eint.v, 3; got != want {
+ panic(fmt.Sprintf("got %d, want %d", got, want))
+ }
+ var s3 _S3
+ s3._E = &_Eint{4}
+ if got, want := s3._E.v, 4; got != want {
+ panic(fmt.Sprintf("got %d, want %d", got, want))
+ }
+}
diff --git a/test/typeparam/value.go b/test/typeparam/value.go
new file mode 100644
index 0000000000..5dd7449d9c
--- /dev/null
+++ b/test/typeparam/value.go
@@ -0,0 +1,75 @@
+// run -gcflags=-G=3
+
+// 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 main
+
+import "fmt"
+
+type value[T any] struct {
+ val T
+}
+
+func get[T2 any](v *value[T2]) T2 {
+ return v.val
+}
+
+func set[T any](v *value[T], val T) {
+ v.val = val
+}
+
+func (v *value[T2]) set(val T2) {
+ v.val = val
+}
+
+func (v *value[T2]) get() T2 {
+ return v.val
+}
+
+func main() {
+ var v1 value[int]
+ set(&v1, 1)
+ if got, want := get(&v1), 1; got != want {
+ panic(fmt.Sprintf("get() == %d, want %d", got, want))
+ }
+
+ v1.set(2)
+ if got, want := v1.get(), 2; got != want {
+ panic(fmt.Sprintf("get() == %d, want %d", got, want))
+ }
+
+ v1p := new(value[int])
+ set(v1p, 3)
+ if got, want := get(v1p), 3; got != want {
+ panic(fmt.Sprintf("get() == %d, want %d", got, want))
+ }
+
+ v1p.set(4)
+ if got, want := v1p.get(), 4; got != want {
+ panic(fmt.Sprintf("get() == %d, want %d", got, want))
+ }
+
+ var v2 value[string]
+ set(&v2, "a")
+ if got, want := get(&v2), "a"; got != want {
+ panic(fmt.Sprintf("get() == %q, want %q", got, want))
+ }
+
+ v2.set("b")
+ if got, want := get(&v2), "b"; got != want {
+ panic(fmt.Sprintf("get() == %q, want %q", got, want))
+ }
+
+ v2p := new(value[string])
+ set(v2p, "c")
+ if got, want := get(v2p), "c"; got != want {
+ panic(fmt.Sprintf("get() == %d, want %d", got, want))
+ }
+
+ v2p.set("d")
+ if got, want := v2p.get(), "d"; got != want {
+ panic(fmt.Sprintf("get() == %d, want %d", got, want))
+ }
+}