diff --git a/src/go/types/NOTES b/src/go/types/NOTES index b70e6e7d36..ae94ed179f 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -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()" diff --git a/src/go/types/call.go b/src/go/types/call.go index a9a371b159..d749ef1c22 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -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 = © - } - 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. diff --git a/src/go/types/subst.go b/src/go/types/subst.go index cb4995c64a..db8d520c45 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -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 = © + } + // 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 © + return &Signature{t.rparams, t.tparams, t.scope, recv, params, results, t.variadic} } case *Interface: diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index 912029fcb1..7563081b35 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -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 } -} \ No newline at end of file +var _ T(int) diff --git a/src/go/types/type.go b/src/go/types/type.go index cce68b60d5..2eb8b66bc4 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -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