go/types: cleanup instantiation of function signatures

Make sure that a generic signature is not generic anymore
after instantiation. Update various related comments.

Change-Id: I2ac81037e570dc3a96c138b85529f3d86030776a
This commit is contained in:
Robert Griesemer 2020-02-05 13:47:03 -08:00
parent 3ab56f5ff2
commit 99c8ae0d28
5 changed files with 22 additions and 51 deletions

View File

@ -18,8 +18,6 @@ KNOWN ISSUES
- cannot handle mutually recursive parameterized interfaces using themselves as type bounds
- contract instantiation requires the type arguments to be type parameters from the type of function
type parameter list or enclosing contract
- instantiating a parameterized function type w/o value or result parameters may have unexpected side-effects
(we don't make a copy of the signature in some cases) - investigate
- leaving away unused receiver type parameters leads to an error; e.g.: "type S(type T) struct{}; func (S) _()"
- using _ for an unused receiver type parameter leads to an error and crash; e.g.: "type T(type P) int; func (_ T(_)) m()"

View File

@ -107,15 +107,7 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
// instantiate function signature
res := check.instantiate(x.pos(), sig, targs, poslist).(*Signature)
// TODO(gri) The code below should not be necessary. The subst function
// does not correctly create a copy for signatures that have no value
// parameters but are instantiated. Documented bug.
// Look also into the situation for methods.
if res == sig {
copy := *sig
res = &copy
}
res.tparams = nil // signature is not generic anymore
assert(res.tparams == nil) // signature is not generic anymore
x.typ = res
x.mode = value
x.expr = e
@ -327,7 +319,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, args []*oper
return
}
rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
rsig.tparams = nil // signature is not generic anymore
assert(rsig.tparams == nil) // signature is not generic anymore
sig_params = check.subst(call.Pos(), sig_params, sig.tparams, targs).(*Tuple)
// TODO(gri) Optimization: We don't need to check arguments
// from which we inferred parameter types.

View File

@ -37,6 +37,19 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
tparams = t.tparams
case *Signature:
tparams = t.tparams
defer func() {
// If the signature doesn't use its type parameters, subst
// will not make a copy. In that case, make a copy now (so
// we can set tparams to nil w/o causing side-effects).
if t == res {
copy := *t
res = &copy
}
// After instantiating a generic signure, it is not generic
// anymore; we need to set tparams to nil.
res.(*Signature).tparams = nil
}()
default:
check.dump("%v: cannot instantiate %v", pos, typ)
unreachable() // only defined types and (defined) functions can be generic
@ -184,24 +197,13 @@ func (subst *subster) typ(typ Type) Type {
return subst.tuple(t)
case *Signature:
// TODO(gri) BUG: If we instantiate this signature but it has no value params, we don't get a copy!
// We need to look at the actual type parameters of the signature as well.
// TODO(gri) rethink the recv situation with respect to methods on parameterized types
// recv := subst.var_(t.recv) // TODO(gri) this causes a stack overflow - explain
recv := t.recv
params := subst.tuple(t.params)
results := subst.tuple(t.results)
if recv != t.recv || params != t.params || results != t.results {
// TODO(gri) what do we need to do with t.scope, if anything?
copy := *t
// TODO(gri) if we instantiate this signature, we need to set
// tparams to nil (the signature may be a field type of a struct)
// otherwise the signature remains parameterized which would be
// wrong. Investigate the correct approach.
copy.recv = recv
copy.params = params
copy.results = results
return &copy
return &Signature{t.rparams, t.tparams, t.scope, recv, params, results, t.variadic}
}
case *Interface:

View File

@ -4,29 +4,8 @@
package p
/*
type I interface{
m(type A)(A)
}
type S struct{}
func (S) m(type B)(B)
var _ I = S{}
type E(type T) interface {
m(type B)(B)
type T(type E) struct {
f *T(E)
}
type II interface{
(E(int))
mm() int
}
var _ I = II(nil)
*/
func _(type T interface{ type string, chan<-int })(x T) {
for i, _ := range x /* ERROR send-only channel */ { _ = i }
}
var _ T(int)

View File

@ -199,10 +199,10 @@ type Signature struct {
// 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.
// We then unpack the *Signature and use the scope for the literal body.
scope *Scope // function scope, present for package-local signatures
recv *Var // nil if not a method
rparams []*TypeName // reveiver type parameters from left to right; or nil
tparams []*TypeName // type parameters from left to right; or nil
scope *Scope // function scope, present for package-local signatures
recv *Var // nil if not a method
params *Tuple // (incoming) parameters from left to right; or nil
results *Tuple // (outgoing) results from left to right; or nil
variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
@ -222,7 +222,7 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
panic("types.NewSignature: variadic parameter must be of unnamed slice type")
}
}
return &Signature{nil, recv, nil, nil, params, results, variadic}
return &Signature{nil, nil, nil, recv, params, results, variadic}
}
// Recv returns the receiver of signature s (if a method), or nil if a