]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: rename generic function arguments
authorRobert Griesemer <gri@golang.org>
Thu, 4 May 2023 00:47:29 +0000 (17:47 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 4 May 2023 17:01:49 +0000 (17:01 +0000)
For correct inference, if the same generic function is provided
more than once as an argument to another function, the argument
function's type parameters must be unique for each argument so
that the type parameters can be correctly inferred.

Example:

func f(func(int), func(string)) {}

func g[P any](P) {}

func _() {
f(g, g)
}

Here the type parameter P for the first g argument resolves to int
and the type parameter P for the second g argument resolves to string.

Fixes #59956.
For #59338.

Change-Id: I10ce0ea08c2033722dd7c7c976b2a5448b2ee2d8
Reviewed-on: https://go-review.googlesource.com/c/go/+/492516
Reviewed-by: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>

src/cmd/compile/internal/types2/api_test.go
src/cmd/compile/internal/types2/call.go
src/go/types/api_test.go
src/go/types/call.go
src/internal/types/testdata/examples/inference2.go
src/internal/types/testdata/fixedbugs/issue59956.go [new file with mode: 0644]

index ae253623e6436ada2c9533129ff8d29ccdd8d5f4..8a11dd9a49177054aac22c33ee157b79eb2589cf 100644 (file)
@@ -583,6 +583,13 @@ type T[P any] []P
                                {`h`, []string{`int`}, `func([]int, *float32)`},
                        },
                },
+               {`package issue59956; func f(func(int), func(string), func(bool)) {}; func g[P any](P) {}; func _() { f(g, g, g) }`,
+                       []testInst{
+                               {`g`, []string{`int`}, `func(int)`},
+                               {`g`, []string{`string`}, `func(string)`},
+                               {`g`, []string{`bool`}, `func(bool)`},
+                       },
+               },
        }
 
        for _, test := range tests {
index c9a6e2f46ed87ccbff857262bc7467d861998011..bd8ca953ef47421e7c191dbe39deda6c2182310a 100644 (file)
@@ -500,8 +500,15 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
                for i, arg := range args {
                        // generic arguments cannot have a defined (*Named) type - no need for underlying type below
                        if asig, _ := arg.typ.(*Signature); asig != nil && asig.TypeParams().Len() > 0 {
-                               // TODO(gri) need to also rename type parameters for cases like f(g, g)
-                               tparams = append(tparams, asig.TypeParams().list()...)
+                               // Rename type parameters for cases like f(g, g); this gives each
+                               // generic function argument a unique type identity (go.dev/issues/59956).
+                               // TODO(gri) Consider only doing this if a function argument appears
+                               //           multiple times, which is rare (possible optimization).
+                               atparams, tmp := check.renameTParams(call.Pos(), asig.TypeParams().list(), asig)
+                               asig = tmp.(*Signature)
+                               asig.tparams = &TypeParamList{atparams} // renameTParams doesn't touch associated type parameters
+                               arg.typ = asig                          // new type identity for the function argument
+                               tparams = append(tparams, atparams...)
                                genericArgs = append(genericArgs, i)
                        }
                }
index 02e26c3f025d1283bf31f8f9013f53eaafa1613f..8f9ef023892679f86a5d43b929a6d1fad0666043 100644 (file)
@@ -583,6 +583,13 @@ type T[P any] []P
                                {`h`, []string{`int`}, `func([]int, *float32)`},
                        },
                },
+               {`package issue59956; func f(func(int), func(string), func(bool)) {}; func g[P any](P) {}; func _() { f(g, g, g) }`,
+                       []testInst{
+                               {`g`, []string{`int`}, `func(int)`},
+                               {`g`, []string{`string`}, `func(string)`},
+                               {`g`, []string{`bool`}, `func(bool)`},
+                       },
+               },
        }
 
        for _, test := range tests {
index 86c2da052255d90a6f8807f209e341aad8661fdd..6e94156d3e04d23dd3c4d7772d5ff4bfac0ee3ec 100644 (file)
@@ -503,8 +503,15 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
                for i, arg := range args {
                        // generic arguments cannot have a defined (*Named) type - no need for underlying type below
                        if asig, _ := arg.typ.(*Signature); asig != nil && asig.TypeParams().Len() > 0 {
-                               // TODO(gri) need to also rename type parameters for cases like f(g, g)
-                               tparams = append(tparams, asig.TypeParams().list()...)
+                               // Rename type parameters for cases like f(g, g); this gives each
+                               // generic function argument a unique type identity (go.dev/issues/59956).
+                               // TODO(gri) Consider only doing this if a function argument appears
+                               //           multiple times, which is rare (possible optimization).
+                               atparams, tmp := check.renameTParams(call.Pos(), asig.TypeParams().list(), asig)
+                               asig = tmp.(*Signature)
+                               asig.tparams = &TypeParamList{atparams} // renameTParams doesn't touch associated type parameters
+                               arg.typ = asig                          // new type identity for the function argument
+                               tparams = append(tparams, atparams...)
                                genericArgs = append(genericArgs, i)
                        }
                }
index 80acc828ddd6c43426f959dab6e9c5f26e769be7..7b86266d5ee03e5b1f028baed29fa108ac6dbd00 100644 (file)
@@ -88,7 +88,5 @@ func _() {
        g2(f4)
        g4(f6)
        g5(f6, f7)
-
-       // TODO(gri) this should work (requires type parameter renaming for f1)
-       g6(f1, f1 /* ERROR "type func[P any](P) of f1 does not match func(string)" */)
+       g6(f1, f1)
 }
diff --git a/src/internal/types/testdata/fixedbugs/issue59956.go b/src/internal/types/testdata/fixedbugs/issue59956.go
new file mode 100644 (file)
index 0000000..33b05d7
--- /dev/null
@@ -0,0 +1,49 @@
+// -reverseTypeInference
+
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func f1(func(int))
+func f2(func(int), func(string))
+func f3(func(int), func(string), func(float32))
+
+func g1[P any](P) {}
+
+func _() {
+       f1(g1)
+       f2(g1, g1)
+       f3(g1, g1, g1)
+}
+
+// More complex examples
+
+func g2[P any](P, P)                                         {}
+func h3[P any](func(P), func(P), func() P)                   {}
+func h4[P, Q any](func(P), func(P, Q), func() Q, func(P, Q)) {}
+
+func r1() int { return 0 }
+
+func _() {
+       h3(g1, g1, r1)
+       h4(g1, g2, r1, g2)
+}
+
+// Variadic cases
+
+func f(func(int))
+func g[P any](P) {}
+
+func d[P any](...func(P)) {}
+
+func _() {
+       d /* ERROR "cannot infer P" */ ()
+       d(f)
+       d(f, g)
+       d(f, g, g)
+       d /* ERROR "cannot infer P" */ (g, g, g)
+       d(g, g, f)
+       d(g, f, g, f)
+}