diff --git a/src/go/types/examples/functions.go2 b/src/go/types/examples/functions.go2 index 21777e3432..3cd0a0c43f 100644 --- a/src/go/types/examples/functions.go2 +++ b/src/go/types/examples/functions.go2 @@ -71,4 +71,43 @@ func variadic(type A, B)(A, B, ...B) int { panic("unimplemented") } // var _ = variadic(1) // ERROR not enough arguments var _ = variadic(1, 2.3) var _ = variadic(1, 2.3, 3.4, 4.5) -var _ = variadic(int, float64)(1, 2.3, 3.4, 4) \ No newline at end of file +var _ = variadic(int, float64)(1, 2.3, 3.4, 4) + +// Type inference also works in recursive function calls where +// the inferred type is the type parameter of the caller. +func f1(type T)(x T) { + f1(x) +} + +func f2a(type T)(x, y T) { + f2a(x, y) +} + +func f2b(type T)(x, y T) { + f2b(y, x) +} + +func g2a(type P, Q)(x P, y Q) { + g2a(x, y) +} + +func g2b(type P, Q)(x P, y Q) { + g2b(y, x) +} + +// Here's an example of a recursive function call with variadic +// arguments and type inference inferring the type parameter of +// the caller (i.e., itself). +func max(type T interface{ type int })(x ...T) T { + var x0 T + if len(x) > 0 { + x0 = x[0] + } + if len(x) > 1 { + x1 := max(x[1:]...) + if x1 > x0 { + return x1 + } + } + return x0 +} diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 6ce06dc384..3c47ae8d0c 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -129,7 +129,15 @@ func (p *ifacePair) identical(q *ifacePair) bool { // If a non-nil tparams is provided, type inference is done for type parameters in x. func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair, tparams []Type) bool { - if x == y { + // If we want type inference, do not shortcut for equal types. Instead + // keep comparing them element-wise so we can infer the matching (and + // equal type parameter types). A simple test case where this matters + // is: func f(type T)(x T) { f(x) } . + // (If we know that the types are equal, we could optimize this case by + // simply extracting the type parameters used and then populate tparams + // accordingly. Not clear it's worthwhile the parallel, if slightly more + // efficient code structure.) + if tparams == nil && x == y { return true } @@ -324,7 +332,15 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair, tparams if tparams == nil { return false // x and y being equal is caught in the very beginning of this function } + // tparams != nil if x := tparams[x.index]; x != nil { + // If we have inferred a type x and it matches y, we're + // done. check.identical0 won't do this check if we run + // in inference mode (tparams != nil), so do it here to + // avoid endless recursion. + if x == y { + return true + } return check.identical0(x, y, cmpTags, p, tparams) } tparams[x.index] = y // infer type from y diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index 19f25850d1..ff68694fd7 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -4,31 +4,45 @@ package main -contract C(T) { - T m() T +func f(type T)(x T) { + f(x) } -// contract C(T₁) {T₁ C₀(type T₁ any) = interface{m() T₁}} - -contract W(T) { - C(T) +func g(type T)(x, y T) { + g(x, y) } -// contract W(T₂) {T₂ W₀(type T₂ main.C₀) = interface{interface{m() T₂}}} - -func _(type T W)(x T) { - var _ T = x.m() +func f2(type T1, T2)(x1 T1, x2 T2) { + f2(x1, x2) } -contract compareTwo(A, B) { - comparable(A) - comparable(B) +func f2p(type T1, T2)(x1 T1, x2 T2) { + f2p(x2, x1) } -func _(type T1, T2 compareTwo)(x1, y1 T1) bool { - return x1 == y1 -} +/* +func f(func(int)) +func g(type T)(T) -func _(type T comparable)(x, y T) bool { - return x == y || x != y +func _() { + f(g) } +*/ + +/* +func f(type T)(T, func(type T)(T)) +func g(type T)(T) +func _() { + var x int + f(x, g) +} +*/ + +/* +func f(type T)(T, func(T)) +func g(type T)(T) +func _() { + var x int + f(x, g) +} +*/ \ No newline at end of file