mirror of https://github.com/golang/go.git
go/types: fix bug with generic function instantiation
Once a generic function is instantiated, it's not generic anymore. Also: Added various additional test cases. Change-Id: Ic2304b6c252cfdf41e526825dda64b8a77023d47
This commit is contained in:
parent
edb963e7e9
commit
ac0c4f1846
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))()
|
||||
}
|
||||
*/
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue