[dev.typeparams] cmd/compile: set type parameter indices when they are bound

This is a port of CL 336249 with adjustments due to slightly
different handling of type parameter declaration in types2.

The CL also contains adjustments to the compiler front-end.

With this change it is not necessary to export type parameter
indices. Filed issue #47451 so we don't forget.

Change-Id: I2834f7be313fcb4763dff2a9058f1983ee6a81b3
Reviewed-on: https://go-review.googlesource.com/c/go/+/338192
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-07-28 15:29:19 -07:00
parent af903261e7
commit 27552e9172
23 changed files with 142 additions and 93 deletions

View file

@ -368,10 +368,13 @@ func (r *importReader) obj(name string) {
if r.p.exportVersion < iexportVersionGenerics { if r.p.exportVersion < iexportVersionGenerics {
errorf("unexpected type param type") errorf("unexpected type param type")
} }
index := int(r.int64()) // Type parameter indices are lazily "allocated".
// There's no need to export them anymore.
// TODO change the export format accordingly
_ = int(r.int64())
name0, sub := parseSubscript(name) name0, sub := parseSubscript(name)
tn := types2.NewTypeName(pos, r.currPkg, name0, nil) tn := types2.NewTypeName(pos, r.currPkg, name0, nil)
t := (*types2.Checker)(nil).NewTypeParam(tn, index, nil) t := (*types2.Checker)(nil).NewTypeParam(tn, nil)
if sub == 0 { if sub == 0 {
errorf("missing subscript") errorf("missing subscript")
} }

View file

@ -167,10 +167,10 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
ntyp.SetUnderlying(g.typeExpr(decl.Type)) ntyp.SetUnderlying(g.typeExpr(decl.Type))
tparams := otyp.(*types2.Named).TParams() tparams := otyp.(*types2.Named).TParams()
if len(tparams) > 0 { if n := tparams.Len(); n > 0 {
rparams := make([]*types.Type, len(tparams)) rparams := make([]*types.Type, n)
for i := range rparams { for i := range rparams {
rparams[i] = g.typ(tparams[i].Type()) rparams[i] = g.typ(tparams.At(i).Type())
} }
// This will set hasTParam flag if any rparams are not concrete types. // This will set hasTParam flag if any rparams are not concrete types.
ntyp.SetRParams(rparams) ntyp.SetRParams(rparams)

View file

@ -337,7 +337,7 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
if wantPtr { if wantPtr {
recvType2Base = types2.AsPointer(recvType2).Elem() recvType2Base = types2.AsPointer(recvType2).Elem()
} }
if len(types2.AsNamed(recvType2Base).TParams()) > 0 { if types2.AsNamed(recvType2Base).TParams().Len() > 0 {
// recvType2 is the original generic type that is // recvType2 is the original generic type that is
// instantiated for this method call. // instantiated for this method call.
// selinfo.Recv() is the instantiated type // selinfo.Recv() is the instantiated type

View file

@ -481,7 +481,7 @@ func (r *reader2) typeParamNames() []*types2.TypeName {
pkg, name := r.localIdent() pkg, name := r.localIdent()
names[i] = types2.NewTypeName(pos, pkg, name, nil) names[i] = types2.NewTypeName(pos, pkg, name, nil)
r.dict.tparams[i] = r.p.check.NewTypeParam(names[i], i, nil) r.dict.tparams[i] = r.p.check.NewTypeParam(names[i], nil)
} }
for i, bound := range r.dict.bounds { for i, bound := range r.dict.bounds {

View file

@ -304,9 +304,9 @@ func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) {
} else { } else {
meth2 = ir.NewNameAt(meth.Pos(), newsym) meth2 = ir.NewNameAt(meth.Pos(), newsym)
rparams := types2.AsSignature(m.Type()).RParams() rparams := types2.AsSignature(m.Type()).RParams()
tparams := make([]*types.Type, len(rparams)) tparams := make([]*types.Type, rparams.Len())
for i, rparam := range rparams { for i := range tparams {
tparams[i] = g.typ1(rparam.Type()) tparams[i] = g.typ1(rparams.At(i).Type())
} }
assert(len(tparams) == len(targs)) assert(len(tparams) == len(targs))
ts := typecheck.Tsubster{ ts := typecheck.Tsubster{
@ -336,9 +336,9 @@ func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) {
func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type { func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type {
tparams2 := sig.TParams() tparams2 := sig.TParams()
tparams := make([]*types.Field, len(tparams2)) tparams := make([]*types.Field, tparams2.Len())
for i := range tparams { for i := range tparams {
tp := tparams2[i] tp := tparams2.At(i)
tparams[i] = types.NewField(g.pos(tp), g.sym(tp), g.typ1(tp.Type())) tparams[i] = types.NewField(g.pos(tp), g.sym(tp), g.typ1(tp.Type()))
} }

View file

@ -299,7 +299,7 @@ func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
// Type aliases can refer to uninstantiated generic types, so we // Type aliases can refer to uninstantiated generic types, so we
// might see len(TParams) != 0 && len(TArgs) == 0 here. // might see len(TParams) != 0 && len(TArgs) == 0 here.
// TODO(mdempsky): Revisit after #46477 is resolved. // TODO(mdempsky): Revisit after #46477 is resolved.
assert(len(typ.TParams()) == len(typ.TArgs()) || len(typ.TArgs()) == 0) assert(typ.TParams().Len() == len(typ.TArgs()) || len(typ.TArgs()) == 0)
// TODO(mdempsky): Why do we need to loop here? // TODO(mdempsky): Why do we need to loop here?
orig := typ orig := typ
@ -615,9 +615,10 @@ func (w *writer) objDict(obj types2.Object, dict *writerDict) {
w.len(len(dict.implicits)) w.len(len(dict.implicits))
tparams := objTypeParams(obj) tparams := objTypeParams(obj)
w.len(len(tparams)) ntparams := tparams.Len()
for _, tparam := range tparams { w.len(ntparams)
w.typ(tparam.Type().(*types2.TypeParam).Bound()) for i := 0; i < ntparams; i++ {
w.typ(tparams.At(i).Type().(*types2.TypeParam).Bound())
} }
nderived := len(dict.derived) nderived := len(dict.derived)
@ -641,10 +642,12 @@ func (w *writer) objDict(obj types2.Object, dict *writerDict) {
assert(len(dict.funcs) == nfuncs) assert(len(dict.funcs) == nfuncs)
} }
func (w *writer) typeParamNames(tparams []*types2.TypeName) { func (w *writer) typeParamNames(tparams *types2.TypeParams) {
w.sync(syncTypeParamNames) w.sync(syncTypeParamNames)
for _, tparam := range tparams { ntparams := tparams.Len()
for i := 0; i < ntparams; i++ {
tparam := tparams.At(i)
w.pos(tparam) w.pos(tparam)
w.localIdent(tparam) w.localIdent(tparam)
} }
@ -1468,13 +1471,16 @@ type declCollector struct {
func (c *declCollector) withTParams(obj types2.Object) *declCollector { func (c *declCollector) withTParams(obj types2.Object) *declCollector {
tparams := objTypeParams(obj) tparams := objTypeParams(obj)
if len(tparams) == 0 { n := tparams.Len()
if n == 0 {
return c return c
} }
copy := *c copy := *c
copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)] copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)]
copy.implicits = append(copy.implicits, objTypeParams(obj)...) for i := 0; i < n; i++ {
copy.implicits = append(copy.implicits, tparams.At(i))
}
return &copy return &copy
} }
@ -1705,7 +1711,7 @@ func (w *writer) pkgDecl(decl syntax.Decl) {
// TODO(mdempsky): Revisit after #46477 is resolved. // TODO(mdempsky): Revisit after #46477 is resolved.
if name.IsAlias() { if name.IsAlias() {
named, ok := name.Type().(*types2.Named) named, ok := name.Type().(*types2.Named)
if ok && len(named.TParams()) != 0 && len(named.TArgs()) == 0 { if ok && named.TParams().Len() != 0 && len(named.TArgs()) == 0 {
break break
} }
} }
@ -1851,7 +1857,7 @@ func fieldIndex(info *types2.Info, str *types2.Struct, key *syntax.Name) int {
} }
// objTypeParams returns the type parameters on the given object. // objTypeParams returns the type parameters on the given object.
func objTypeParams(obj types2.Object) []*types2.TypeName { func objTypeParams(obj types2.Object) *types2.TypeParams {
switch obj := obj.(type) { switch obj := obj.(type) {
case *types2.Func: case *types2.Func:
sig := obj.Type().(*types2.Signature) sig := obj.Type().(*types2.Signature)

View file

@ -1857,7 +1857,7 @@ func TestInstantiate(t *testing.T) {
// type T should have one type parameter // type T should have one type parameter
T := pkg.Scope().Lookup("T").Type().(*Named) T := pkg.Scope().Lookup("T").Type().(*Named)
if n := len(T.TParams()); n != 1 { if n := T.TParams().Len(); n != 1 {
t.Fatalf("expected 1 type parameter; found %d", n) t.Fatalf("expected 1 type parameter; found %d", n)
} }

View file

@ -68,7 +68,7 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
// x.typ is typed // x.typ is typed
// A generic (non-instantiated) function value cannot be assigned to a variable. // A generic (non-instantiated) function value cannot be assigned to a variable.
if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 { if sig := asSignature(x.typ); sig != nil && sig.TParams().Len() > 0 {
check.errorf(x, "cannot use generic function %s without instantiation in %s", x, context) check.errorf(x, "cannot use generic function %s without instantiation in %s", x, context)
} }

View file

@ -837,7 +837,8 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
// type param is placed in the current package so export/import // type param is placed in the current package so export/import
// works as expected. // works as expected.
tpar := NewTypeName(nopos, check.pkg, "<type parameter>", nil) tpar := NewTypeName(nopos, check.pkg, "<type parameter>", nil)
ptyp := check.NewTypeParam(tpar, tp.index, &emptyInterface) // assigns type to tpar as a side-effect ptyp := check.NewTypeParam(tpar, &emptyInterface) // assigns type to tpar as a side-effect
ptyp.index = tp.index
tsum := newUnion(rtypes, tildes) tsum := newUnion(rtypes, tildes)
ptyp.bound = &Interface{complete: true, tset: &TypeSet{types: tsum}} ptyp.bound = &Interface{complete: true, tset: &TypeSet{types: tsum}}

View file

@ -26,7 +26,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
// check number of type arguments (got) vs number of type parameters (want) // check number of type arguments (got) vs number of type parameters (want)
sig := x.typ.(*Signature) sig := x.typ.(*Signature)
got, want := len(targs), len(sig.tparams) got, want := len(targs), sig.TParams().Len()
if !useConstraintTypeInference && got != want || got > want { if !useConstraintTypeInference && got != want || got > want {
check.errorf(xlist[got-1], "got %d type arguments but want %d", got, want) check.errorf(xlist[got-1], "got %d type arguments but want %d", got, want)
x.mode = invalid x.mode = invalid
@ -37,7 +37,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
// if we don't have enough type arguments, try type inference // if we don't have enough type arguments, try type inference
inferred := false inferred := false
if got < want { if got < want {
targs = check.infer(inst.Pos(), sig.tparams, targs, nil, nil, true) targs = check.infer(inst.Pos(), sig.TParams().list(), targs, nil, nil, true)
if targs == nil { if targs == nil {
// error was already reported // error was already reported
x.mode = invalid x.mode = invalid
@ -155,7 +155,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
assert(len(targs) == len(xlist)) assert(len(targs) == len(xlist))
// check number of type arguments (got) vs number of type parameters (want) // check number of type arguments (got) vs number of type parameters (want)
got, want := len(targs), len(sig.tparams) got, want := len(targs), sig.TParams().Len()
if got > want { if got > want {
check.errorf(xlist[want], "got %d type arguments but want %d", got, want) check.errorf(xlist[want], "got %d type arguments but want %d", got, want)
check.use(call.ArgList...) check.use(call.ArgList...)
@ -189,7 +189,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
// if type inference failed, a parametrized result must be invalidated // if type inference failed, a parametrized result must be invalidated
// (operands cannot have a parametrized type) // (operands cannot have a parametrized type)
if x.mode == value && len(sig.tparams) > 0 && isParameterized(sig.tparams, x.typ) { if x.mode == value && sig.TParams().Len() > 0 && isParameterized(sig.TParams().list(), x.typ) {
x.mode = invalid x.mode = invalid
} }
@ -317,10 +317,10 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
} }
// infer type arguments and instantiate signature if necessary // infer type arguments and instantiate signature if necessary
if len(sig.tparams) > 0 { if sig.TParams().Len() > 0 {
// TODO(gri) provide position information for targs so we can feed // TODO(gri) provide position information for targs so we can feed
// it to the instantiate call for better error reporting // it to the instantiate call for better error reporting
targs = check.infer(call.Pos(), sig.tparams, targs, sigParams, args, true) targs := check.infer(call.Pos(), sig.TParams().list(), targs, sigParams, args, true)
if targs == nil { if targs == nil {
return // error already reported return // error already reported
} }
@ -334,7 +334,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
// need to compute it from the adjusted list; otherwise we can // need to compute it from the adjusted list; otherwise we can
// simply use the result signature's parameter list. // simply use the result signature's parameter list.
if adjusted { if adjusted {
sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.tparams, targs)).(*Tuple) sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TParams().list(), targs)).(*Tuple)
} else { } else {
sigParams = rsig.params sigParams = rsig.params
} }
@ -516,7 +516,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
// the signature accordingly. // the signature accordingly.
// TODO(gri) factor this code out // TODO(gri) factor this code out
sig := m.typ.(*Signature) sig := m.typ.(*Signature)
if len(sig.rparams) > 0 { if sig.RParams().Len() > 0 {
// For inference to work, we must use the receiver type // For inference to work, we must use the receiver type
// matching the receiver in the actual method declaration. // matching the receiver in the actual method declaration.
// If the method is embedded, the matching receiver is the // If the method is embedded, the matching receiver is the
@ -545,7 +545,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
// the receiver type arguments here, the receiver must be be otherwise invalid // the receiver type arguments here, the receiver must be be otherwise invalid
// and an error has been reported elsewhere. // and an error has been reported elsewhere.
arg := operand{mode: variable, expr: x.expr, typ: recv} arg := operand{mode: variable, expr: x.expr, typ: recv}
targs := check.infer(m.pos, sig.rparams, nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */) targs := check.infer(m.pos, sig.RParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */)
//check.dump("### inferred targs = %s", targs) //check.dump("### inferred targs = %s", targs)
if targs == nil { if targs == nil {
// We may reach here if there were other errors (see issue #40056). // We may reach here if there were other errors (see issue #40056).
@ -555,7 +555,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
// (If we modify m, some tests will fail; possibly because the m is in use.) // (If we modify m, some tests will fail; possibly because the m is in use.)
// TODO(gri) investigate and provide a correct explanation here // TODO(gri) investigate and provide a correct explanation here
copy := *m copy := *m
copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.rparams, targs)) copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.RParams().list(), targs))
obj = &copy obj = &copy
} }
// TODO(gri) we also need to do substitution for parameterized interface methods // TODO(gri) we also need to do substitution for parameterized interface methods

View file

@ -575,20 +575,20 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
named.underlying = under(named) named.underlying = under(named)
// If the RHS is a type parameter, it must be from this type declaration. // If the RHS is a type parameter, it must be from this type declaration.
if tpar, _ := named.underlying.(*TypeParam); tpar != nil && tparamIndex(named.tparams, tpar) < 0 { if tpar, _ := named.underlying.(*TypeParam); tpar != nil && tparamIndex(named.tparams.list(), tpar) < 0 {
check.errorf(tdecl.Type, "cannot use function type parameter %s as RHS in type declaration", tpar) check.errorf(tdecl.Type, "cannot use function type parameter %s as RHS in type declaration", tpar)
named.underlying = Typ[Invalid] named.underlying = Typ[Invalid]
} }
} }
func (check *Checker) collectTypeParams(list []*syntax.Field) []*TypeName { func (check *Checker) collectTypeParams(list []*syntax.Field) *TypeParams {
tparams := make([]*TypeName, len(list)) tparams := make([]*TypeName, len(list))
// Declare type parameters up-front. // Declare type parameters up-front.
// The scope of type parameters starts at the beginning of the type parameter // The scope of type parameters starts at the beginning of the type parameter
// list (so we can have mutually recursive parameterized type bounds). // list (so we can have mutually recursive parameterized type bounds).
for i, f := range list { for i, f := range list {
tparams[i] = check.declareTypeParam(i, f.Name) tparams[i] = check.declareTypeParam(f.Name)
} }
var bound Type var bound Type
@ -602,12 +602,12 @@ func (check *Checker) collectTypeParams(list []*syntax.Field) []*TypeName {
tparams[i].typ.(*TypeParam).bound = bound tparams[i].typ.(*TypeParam).bound = bound
} }
return tparams return bindTParams(tparams)
} }
func (check *Checker) declareTypeParam(index int, name *syntax.Name) *TypeName { func (check *Checker) declareTypeParam(name *syntax.Name) *TypeName {
tpar := NewTypeName(name.Pos(), check.pkg, name.Value, nil) tpar := NewTypeName(name.Pos(), check.pkg, name.Value, nil)
check.NewTypeParam(tpar, index, nil) // assigns type to tpar as a side-effect check.NewTypeParam(tpar, nil) // assigns type to tpar as a side-effect
check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position
return tpar return tpar
} }

View file

@ -32,7 +32,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
return false return false
case value: case value:
if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 { if sig := asSignature(x.typ); sig != nil && sig.TParams().Len() > 0 {
// function instantiation // function instantiation
return true return true
} }

View file

@ -25,7 +25,7 @@ func (n *Named) expand() {
// tparams. This is done implicitly by the call to n.TParams, but making it // tparams. This is done implicitly by the call to n.TParams, but making it
// explicit is harmless: load is idempotent. // explicit is harmless: load is idempotent.
n.load() n.load()
inst := n.check.instantiate(n.instance.pos, n.orig.underlying, n.TParams(), n.targs, n.instance.posList) inst := n.check.instantiate(n.instance.pos, n.orig.underlying, n.TParams().list(), n.targs, n.instance.posList)
n.underlying = inst n.underlying = inst
n.fromRHS = inst n.fromRHS = inst
n.instance = nil n.instance = nil

View file

@ -29,9 +29,9 @@ func (check *Checker) Instantiate(pos syntax.Pos, typ Type, targs []Type, posLis
var tparams []*TypeName var tparams []*TypeName
switch t := typ.(type) { switch t := typ.(type) {
case *Named: case *Named:
tparams = t.TParams() tparams = t.TParams().list()
case *Signature: case *Signature:
tparams = t.tparams tparams = t.TParams().list()
defer func() { defer func() {
// If we had an unexpected failure somewhere don't panic below when // If we had an unexpected failure somewhere don't panic below when
// asserting res.(*Signature). Check for *Signature in case Typ[Invalid] // asserting res.(*Signature). Check for *Signature in case Typ[Invalid]
@ -109,9 +109,9 @@ func (check *Checker) InstantiateLazy(pos syntax.Pos, typ Type, targs []Type, po
panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ)) panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
} }
if verify && len(base.tparams) == len(targs) { if verify && base.TParams().Len() == len(targs) {
check.later(func() { check.later(func() {
check.verify(pos, base.tparams, targs, posList) check.verify(pos, base.tparams.list(), targs, posList)
}) })
} }
@ -125,7 +125,7 @@ func (check *Checker) InstantiateLazy(pos syntax.Pos, typ Type, targs []Type, po
} }
tname := NewTypeName(pos, base.obj.pkg, base.obj.name, nil) tname := NewTypeName(pos, base.obj.pkg, base.obj.name, nil)
named := check.newNamed(tname, base, nil, nil, nil) // methods and tparams are set when named is loaded. named := check.newNamed(tname, base, nil, nil, nil) // methods and tparams are set when named is loaded
named.targs = targs named.targs = targs
named.instance = &instance{pos, posList} named.instance = &instance{pos, posList}
if check != nil { if check != nil {

View file

@ -315,10 +315,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// both methods must have the same number of type parameters // both methods must have the same number of type parameters
ftyp := f.typ.(*Signature) ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature) mtyp := m.typ.(*Signature)
if len(ftyp.tparams) != len(mtyp.tparams) { if ftyp.TParams().Len() != mtyp.TParams().Len() {
return m, f return m, f
} }
if !acceptMethodTypeParams && len(ftyp.tparams) > 0 { if !acceptMethodTypeParams && ftyp.TParams().Len() > 0 {
panic("internal error: method with type parameters") panic("internal error: method with type parameters")
} }
@ -328,7 +328,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// TODO(gri) is this always correct? what about type bounds? // TODO(gri) is this always correct? what about type bounds?
// (Alternative is to rename/subst type parameters and compare.) // (Alternative is to rename/subst type parameters and compare.)
u := newUnifier(true) u := newUnifier(true)
u.x.init(ftyp.tparams) u.x.init(ftyp.TParams().list())
if !u.unify(ftyp, mtyp) { if !u.unify(ftyp, mtyp) {
return m, f return m, f
} }
@ -367,10 +367,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// both methods must have the same number of type parameters // both methods must have the same number of type parameters
ftyp := f.typ.(*Signature) ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature) mtyp := m.typ.(*Signature)
if len(ftyp.tparams) != len(mtyp.tparams) { if ftyp.TParams().Len() != mtyp.TParams().Len() {
return m, f return m, f
} }
if !acceptMethodTypeParams && len(ftyp.tparams) > 0 { if !acceptMethodTypeParams && ftyp.TParams().Len() > 0 {
panic("internal error: method with type parameters") panic("internal error: method with type parameters")
} }
@ -381,17 +381,17 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// In order to compare the signatures, substitute the receiver // In order to compare the signatures, substitute the receiver
// type parameters of ftyp with V's instantiation type arguments. // type parameters of ftyp with V's instantiation type arguments.
// This lazily instantiates the signature of method f. // This lazily instantiates the signature of method f.
if Vn != nil && len(Vn.TParams()) > 0 { if Vn != nil && Vn.TParams().Len() > 0 {
// Be careful: The number of type arguments may not match // Be careful: The number of type arguments may not match
// the number of receiver parameters. If so, an error was // the number of receiver parameters. If so, an error was
// reported earlier but the length discrepancy is still // reported earlier but the length discrepancy is still
// 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) != len(Vn.targs) { if len(ftyp.RParams().list()) != len(Vn.targs) {
return return
} }
ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.rparams, Vn.targs)).(*Signature) ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.RParams().list(), Vn.targs)).(*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
@ -400,7 +400,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// TODO(gri) is this always correct? what about type bounds? // TODO(gri) is this always correct? what about type bounds?
// (Alternative is to rename/subst type parameters and compare.) // (Alternative is to rename/subst type parameters and compare.)
u := newUnifier(true) u := newUnifier(true)
if len(ftyp.tparams) > 0 { if ftyp.TParams().Len() > 0 {
// We reach here only if we accept method type parameters. // We reach here only if we accept method type parameters.
// In this case, unification must consider any receiver // In this case, unification must consider any receiver
// and method type parameters as "free" type parameters. // and method type parameters as "free" type parameters.
@ -410,9 +410,9 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// unimplemented call so that we test this code if we // unimplemented call so that we test this code if we
// enable method type parameters. // enable method type parameters.
unimplemented() unimplemented()
u.x.init(append(ftyp.rparams, ftyp.tparams...)) u.x.init(append(ftyp.RParams().list(), ftyp.TParams().list()...))
} else { } else {
u.x.init(ftyp.rparams) u.x.init(ftyp.RParams().list())
} }
if !u.unify(ftyp, mtyp) { if !u.unify(ftyp, mtyp) {
return m, f return m, f

View file

@ -17,7 +17,7 @@ type Named struct {
fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting) fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting)
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 // position information for lazy instantiation, or nil instance *instance // position information for lazy instantiation, or nil
tparams []*TypeName // type parameters, or nil tparams *TypeParams // type parameters, or nil
targs []Type // type arguments (after instantiation), 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 methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
@ -69,7 +69,7 @@ func (t *Named) load() *Named {
panic("invalid underlying type") panic("invalid underlying type")
} }
t.tparams = tparams t.tparams = bindTParams(tparams)
t.underlying = underlying t.underlying = underlying
t.methods = methods t.methods = methods
}) })
@ -77,7 +77,7 @@ func (t *Named) load() *Named {
} }
// newNamed is like NewNamed but with a *Checker receiver and additional orig argument. // newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams []*TypeName, methods []*Func) *Named { func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParams, methods []*Func) *Named {
typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods} typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
if typ.orig == nil { if typ.orig == nil {
typ.orig = typ typ.orig = typ
@ -117,12 +117,10 @@ func (t *Named) Orig() *Named { return t.orig }
// TParams returns the type parameters of the named type t, or nil. // TParams returns the type parameters of the named type t, or nil.
// The result is non-nil for an (originally) parameterized type even if it is instantiated. // The result is non-nil for an (originally) parameterized type even if it is instantiated.
func (t *Named) TParams() []*TypeName { func (t *Named) TParams() *TypeParams { return t.load().tparams }
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 []*TypeName) { t.load().tparams = tparams } func (t *Named) SetTParams(tparams []*TypeName) { t.load().tparams = bindTParams(tparams) }
// TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated. // TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated.
func (t *Named) TArgs() []Type { return t.targs } func (t *Named) TArgs() []Type { return t.targs }

View file

@ -475,8 +475,8 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
if _, ok := typ.(*Basic); ok { if _, ok := typ.(*Basic); ok {
return return
} }
if named, _ := typ.(*Named); named != nil && len(named.tparams) > 0 { if named, _ := typ.(*Named); named != nil && named.TParams().Len() > 0 {
writeTParamList(buf, named.tparams, qf, nil) writeTParamList(buf, named.TParams().list(), qf, nil)
} }
if tname.IsAlias() { if tname.IsAlias() {
buf.WriteString(" =") buf.WriteString(" =")

View file

@ -227,7 +227,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
// parameter names. // parameter names.
if y, ok := y.(*Signature); ok { if y, ok := y.(*Signature); ok {
return x.variadic == y.variadic && return x.variadic == y.variadic &&
identicalTParams(x.tparams, y.tparams, cmpTags, p) && identicalTParams(x.TParams().list(), y.TParams().list(), cmpTags, p) &&
identical(x.params, y.params, cmpTags, p) && identical(x.params, y.params, cmpTags, p) &&
identical(x.results, y.results, cmpTags, p) identical(x.results, y.results, cmpTags, p)
} }

View file

@ -19,8 +19,8 @@ type Signature struct {
// and store it in the Func Object) because when type-checking a function // and store it in the Func Object) because when type-checking a function
// literal we call the general type checker which returns a general Type. // literal we call the general type checker which returns a general Type.
// We then unpack the *Signature and use the scope for the literal body. // We then unpack the *Signature and use the scope for the literal body.
rparams []*TypeName // receiver type parameters from left to right; or nil rparams *TypeParams // receiver type parameters from left to right, or nil
tparams []*TypeName // type parameters from left to right; or nil tparams *TypeParams // type parameters from left to right, or nil
scope *Scope // function scope, present for package-local signatures scope *Scope // function scope, present for package-local signatures
recv *Var // nil if not a method recv *Var // nil if not a method
params *Tuple // (incoming) parameters from left to right; or nil params *Tuple // (incoming) parameters from left to right; or nil
@ -54,16 +54,16 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
func (s *Signature) Recv() *Var { return s.recv } func (s *Signature) Recv() *Var { return s.recv }
// TParams returns the type parameters of signature s, or nil. // TParams returns the type parameters of signature s, or nil.
func (s *Signature) TParams() []*TypeName { return s.tparams } func (s *Signature) TParams() *TypeParams { return s.tparams }
// RParams returns the receiver type params of signature s, or nil.
func (s *Signature) RParams() []*TypeName { return s.rparams }
// SetTParams sets the type parameters of signature s. // SetTParams sets the type parameters of signature s.
func (s *Signature) SetTParams(tparams []*TypeName) { s.tparams = tparams } func (s *Signature) SetTParams(tparams []*TypeName) { s.tparams = bindTParams(tparams) }
// RParams returns the receiver type parameters of signature s, or nil.
func (s *Signature) RParams() *TypeParams { return s.rparams }
// SetRParams sets the receiver type params of signature s. // SetRParams sets the receiver type params of signature s.
func (s *Signature) SetRParams(rparams []*TypeName) { s.rparams = rparams } func (s *Signature) SetRParams(rparams []*TypeName) { s.rparams = bindTParams(rparams) }
// Params returns the parameters of signature s, or nil. // Params returns the parameters of signature s, or nil.
func (s *Signature) Params() *Tuple { return s.params } func (s *Signature) Params() *Tuple { return s.params }
@ -119,10 +119,11 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
// blank identifiers were found => use rewritten receiver type // blank identifiers were found => use rewritten receiver type
recvTyp = isubst(recvPar.Type, smap) recvTyp = isubst(recvPar.Type, smap)
} }
sig.rparams = make([]*TypeName, len(rparams)) rlist := make([]*TypeName, len(rparams))
for i, rparam := range rparams { for i, rparam := range rparams {
sig.rparams[i] = check.declareTypeParam(i, rparam) rlist[i] = check.declareTypeParam(rparam)
} }
sig.rparams = bindTParams(rlist)
// determine receiver type to get its type parameters // determine receiver type to get its type parameters
// and the respective type parameter bounds // and the respective type parameter bounds
var recvTParams []*TypeName var recvTParams []*TypeName
@ -132,19 +133,19 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
// again when we type-check the signature. // again when we type-check the signature.
// TODO(gri) maybe the receiver should be marked as invalid instead? // TODO(gri) maybe the receiver should be marked as invalid instead?
if recv := asNamed(check.genericType(rname, false)); recv != nil { if recv := asNamed(check.genericType(rname, false)); recv != nil {
recvTParams = recv.TParams() recvTParams = recv.TParams().list()
} }
} }
// provide type parameter bounds // provide type parameter bounds
// - only do this if we have the right number (otherwise an error is reported elsewhere) // - only do this if we have the right number (otherwise an error is reported elsewhere)
if len(sig.rparams) == len(recvTParams) { if sig.RParams().Len() == len(recvTParams) {
// We have a list of *TypeNames but we need a list of Types. // We have a list of *TypeNames but we need a list of Types.
list := make([]Type, len(sig.rparams)) list := make([]Type, sig.RParams().Len())
for i, t := range sig.rparams { for i, t := range sig.RParams().list() {
list[i] = t.typ list[i] = t.typ
} }
smap := makeSubstMap(recvTParams, list) smap := makeSubstMap(recvTParams, list)
for i, tname := range sig.rparams { for i, tname := range sig.RParams().list() {
bound := recvTParams[i].typ.(*TypeParam).bound bound := recvTParams[i].typ.(*TypeParam).bound
// bound is (possibly) parameterized in the context of the // bound is (possibly) parameterized in the context of the
// receiver type declaration. Substitute parameters for the // receiver type declaration. Substitute parameters for the

View file

@ -26,12 +26,12 @@ func TestSizeof(t *testing.T) {
{Struct{}, 24, 48}, {Struct{}, 24, 48},
{Pointer{}, 8, 16}, {Pointer{}, 8, 16},
{Tuple{}, 12, 24}, {Tuple{}, 12, 24},
{Signature{}, 44, 88}, {Signature{}, 28, 56},
{Union{}, 12, 24}, {Union{}, 12, 24},
{Interface{}, 40, 80}, {Interface{}, 40, 80},
{Map{}, 16, 32}, {Map{}, 16, 32},
{Chan{}, 12, 24}, {Chan{}, 12, 24},
{Named{}, 88, 168}, {Named{}, 80, 152},
{TypeParam{}, 28, 48}, {TypeParam{}, 28, 48},
{term{}, 12, 24}, {term{}, 12, 24},
{top{}, 0, 0}, {top{}, 0, 0},

View file

@ -197,7 +197,7 @@ func (subst *subster) typ(typ Type) Type {
if len(t.targs) > 0 { if len(t.targs) > 0 {
// already instantiated // already instantiated
dump(">>> %s already instantiated", t) dump(">>> %s already instantiated", t)
assert(len(t.targs) == len(t.TParams())) assert(len(t.targs) == t.TParams().Len())
// 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.
@ -207,7 +207,7 @@ func (subst *subster) typ(typ Type) Type {
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 new_targs == nil { if new_targs == nil {
new_targs = make([]Type, len(t.TParams())) new_targs = make([]Type, t.TParams().Len())
copy(new_targs, t.targs) copy(new_targs, t.targs)
} }
new_targs[i] = new_targ new_targs[i] = new_targ

View file

@ -28,15 +28,19 @@ type TypeParam struct {
// Obj returns the type name for the type parameter t. // Obj returns the type name for the type parameter t.
func (t *TypeParam) Obj() *TypeName { return t.obj } func (t *TypeParam) Obj() *TypeName { return t.obj }
// NewTypeParam returns a new TypeParam. bound can be nil (and set later). // NewTypeParam returns a new TypeParam. Type parameters may be set on a Named
func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypeParam { // or Signature type by calling SetTParams. Setting a type parameter on more
// than one type will result in a panic.
//
// The bound argument can be nil, and set later via SetBound.
func (check *Checker) NewTypeParam(obj *TypeName, bound Type) *TypeParam {
// Always increment lastID, even if it is not used. // Always increment lastID, even if it is not used.
id := nextID() id := nextID()
if check != nil { if check != nil {
check.nextID++ check.nextID++
id = check.nextID id = check.nextID
} }
typ := &TypeParam{check: check, id: id, obj: obj, index: index, bound: bound} typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: bound}
if obj.typ == nil { if obj.typ == nil {
obj.typ = typ obj.typ = typ
} }
@ -88,6 +92,42 @@ func (t *TypeParam) SetBound(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) }
// TypeParams holds a list of type parameters bound to a type.
type TypeParams struct{ tparams []*TypeName }
// Len returns the number of type parameters in the list.
// It is safe to call on a nil receiver.
func (tps *TypeParams) Len() int {
return len(tps.list())
}
// At returns the i'th type parameter in the list.
// It is safe to call on a nil receiver.
func (tps *TypeParams) At(i int) *TypeName {
return tps.list()[i]
}
func (tps *TypeParams) list() []*TypeName {
if tps == nil {
return nil
}
return tps.tparams
}
func bindTParams(list []*TypeName) *TypeParams {
if len(list) == 0 {
return nil
}
for i, tp := range list {
typ := tp.Type().(*TypeParam)
if typ.index >= 0 {
panic("internal error: type parameter bound more than once")
}
typ.index = i
}
return &TypeParams{tparams: list}
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Implementation // Implementation

View file

@ -280,7 +280,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
buf.WriteByte(']') buf.WriteByte(']')
} else if t.TParams() != nil { } else if t.TParams() != nil {
// parameterized type // parameterized type
writeTParamList(buf, t.TParams(), qf, visited) writeTParamList(buf, t.TParams().list(), qf, visited)
} }
case *TypeParam: case *TypeParam:
@ -426,7 +426,7 @@ func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) { func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
if sig.tparams != nil { if sig.tparams != nil {
writeTParamList(buf, sig.tparams, qf, visited) writeTParamList(buf, sig.TParams().list(), qf, visited)
} }
writeTuple(buf, sig.params, sig.variadic, qf, visited) writeTuple(buf, sig.params, sig.variadic, qf, visited)