go/types: make type inference work for self-recursive function calls

Cases such as

	func f(type T)(x T) {
		f(x)
	}

can now be type-checked. For more examples see examples/functions.go2
in the go/types directory.

Change-Id: Id661c84f086cc8ee45ec372ac4af543e68bebe8a
This commit is contained in:
Robert Griesemer 2020-03-15 16:17:00 -07:00
parent 67e62d261e
commit 399d59ee46
3 changed files with 89 additions and 20 deletions

View File

@ -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)
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
}

View File

@ -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

View File

@ -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₀<T₂>) = 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)
}
*/