diff --git a/src/go/types/NOTES b/src/go/types/NOTES index a0c78bd383..31015c4390 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -4,13 +4,15 @@ TODO - interface embedding doesn't take care of literal type constraints yet (need an allTypes list, like we have an allMethods list?) - type assertions on/against parameterized types +- consolidate Signature.tparams and Signature.mtparams? +- can we always set Named.targs? +- use []*TypeParam for tparams in subst OPEN ISSUES - using a contract and enumerating type arguments currently leads to an error (e.g. func f(type T C(T)) (x T) ... ) - contracts slip through in places where only types are permitted -- parameterized interface methods (of type bounds) need to be customized (subst) for context -DESIGN/IMPLEMENTATION DECISIONS +DESIGN/IMPLEMENTATION - 12/4/2019: do not allow parenthesized generic uninstantiated types (unless instantiated implicitly) In other words: generic types must always be instantiated before they can be used in any form More generally: Only permit type instantiation T(x) in type context, when the type is a named type. @@ -19,3 +21,11 @@ DESIGN/IMPLEMENTATION DECISIONS - 12/12/2019: represent type bounds always as (possibly unnamed) interfaces (contracts are user syntactic sugar) + +- 12/19/2019: Type parameters don't act like type aliases. For instance: + + func f(type T1, T2)(x T1) T2 { return x } + + is not valid, no matter how T1 and T2 are instantiated (but if T1 and T2 were type aliases with + both of them having type int, the return x would be valid). In fact, the type parameters act more + like named types. With their underlying type being the interface by which they are bound. diff --git a/src/go/types/call.go b/src/go/types/call.go index 84bf6026f5..056cef8337 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -106,7 +106,9 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind { } // instantiate function signature - x.typ = check.instantiate(x.pos(), sig, targs, poslist) + sig = check.instantiate(x.pos(), sig, targs, poslist).(*Signature) + sig.tparams = nil // signature is not generic anymore + x.typ = sig x.mode = value x.expr = e return expression @@ -260,7 +262,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, args []*oper ddd := call.Ellipsis.IsValid() // set up parameters - params := sig.params + sig_params := sig.params if sig.variadic { if ddd { // variadic_func(a, b, c...) @@ -282,7 +284,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, args []*oper for len(vars) < nargs { vars = append(vars, NewParam(last.pos, last.pkg, last.name, typ)) } - params = NewTuple(vars...) + sig_params = NewTuple(vars...) npars = nargs } else { // nargs < npars-1 @@ -312,19 +314,20 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, args []*oper if len(sig.tparams) > 0 { // TODO(gri) provide position information for targs so we can feed // it to the instantiate call for better error reporting - targs := check.infer(call.Rparen, sig.tparams, params, args) + targs := check.infer(call.Rparen, sig.tparams, sig_params, args) if targs == nil { return } rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature) - params = check.subst(call.Pos(), params, sig.tparams, targs).(*Tuple) + 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. } // check arguments for i, a := range args { - check.assignment(a, params.vars[i].typ, "argument") + check.assignment(a, sig_params.vars[i].typ, "argument") } return diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 2c11799987..7f9759c3ca 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -230,7 +230,9 @@ func (subst *subster) typ(typ Type) Type { results := subst.tuple(t.results) if recv != t.recv || params != t.params || results != t.results { copy := *t - copy.tparams = nil // TODO(gri) is this correct? (another indication that perhaps tparams belong to the function decl) + // note: we leave (copy.)tparams alone - if a caller instantiated a function + // via instantiate (calling subst) it is the caller's responsibility + // to nil out this field copy.recv = recv copy.params = params copy.results = results diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index 4ba882f949..cbb358b144 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -3,75 +3,3 @@ // license that can be found in the LICENSE file. package p - -func f(type P interface{ m() P })(x P) - -type T(type P) P - -func (_ T(P)) m() T(P) - -func _(type Q)(x Q) { - f(T(Q))(T(Q)(x)) -} - -// TODO(gri) Once the code above works, check the code below. -/* -type NumericAbs(type T) interface { - Abs() T -} - -func AbsDifference(type T NumericAbs(T))() - -type OrderedAbs(type T) T - -func (a OrderedAbs(T)) Abs() OrderedAbs(T) - -func OrderedAbsDifference(type T)() { - AbsDifference(OrderedAbs(T))() -} -*/ - -/* -package p - -type T(type _ interface { a() }, _ interface{}) struct{} - -type A(type P) struct{ x P } - -func (_ A(P)) a() {} - -var _ T(A(int), int) -*/ - -/* -type NumericAbs(type T) interface { - Abs() T -} - -func AbsDifference(type T NumericAbs(T))(x T) - -type OrderedAbs(type T) T - -func (a OrderedAbs(T)) Abs() T - -func OrderedAbsDifference(type T)(x T) { - AbsDifference(OrderedAbs(T)(x)) -} -*/ - -// TODO(gri) Once the code above works, check the code below. -/* -type NumericAbs(type T) interface { - Abs() T -} - -func AbsDifference(type T NumericAbs(T))() - -type OrderedAbs(type T) T - -func (a OrderedAbs(T)) Abs() T - -func OrderedAbsDifference(type T)() { - AbsDifference(OrderedAbs(T))() -} -*/ \ No newline at end of file diff --git a/src/go/types/testdata/typeinst2.go2 b/src/go/types/testdata/typeinst2.go2 index 367ee23d25..35f3cb25d6 100644 --- a/src/go/types/testdata/typeinst2.go2 +++ b/src/go/types/testdata/typeinst2.go2 @@ -78,3 +78,41 @@ func Values (type T) (r Receiver(T)) T { func (it Iterator(K)) Next() K { return Values(Pair(K))(it.r).key } + +// A more complex test case testing type bounds (extracted from linalg.go2 and reduced to essence) + +contract NumericAbs(T) { + T Abs() T +} + +func AbsDifference(type T NumericAbs)(x T) + +type OrderedAbs(type T) T + +func (a OrderedAbs(T)) Abs() OrderedAbs(T) + +func OrderedAbsDifference(type T)(x T) { + AbsDifference(OrderedAbs(T)(x)) +} + +// same code, reduced to essence + +func g(type P interface{ m() P })(x P) + +type T4(type P) P + +func (_ T4(P)) m() T4(P) + +func _(type Q)(x Q) { + g(T4(Q)(x)) +} + +// Another test case that caused problems in the past + +type T5(type _ interface { a() }, _ interface{}) struct{} + +type A(type P) struct{ x P } + +func (_ A(P)) a() {} + +var _ T5(A(int), int) diff --git a/src/go/types/testdata/typeparams.go2 b/src/go/types/testdata/typeparams.go2 index 83aa57c91e..bd06f1d2e4 100644 --- a/src/go/types/testdata/typeparams.go2 +++ b/src/go/types/testdata/typeparams.go2 @@ -151,6 +151,27 @@ func _(type P)() { f10(S2(P, int, P){}, S2(P, float32, bool){}) } +// corner case for type inference +// (was bug: after instanting f11, the type-checker didn't mark f11 as non-generic) + +func f11(type T)() + +func _() { + f11(int)() +} + +// the previous example was extracted from + +func f12(type T interface{m() T})() + +type A(type T) T + +func (a A(T)) m() A(T) + +func _(type T)() { + f12(A(T))() +} + // method expressions func (_ S1(P)) m()