aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2023-06-08 17:04:06 -0700
committerGopher Robot <gobot@golang.org>2023-06-12 17:29:51 +0000
commit3367475e83eeccd79a5c73c2cc2e91e85e482295 (patch)
tree7c90f07447e9cfe0f4b13ccb054a302dd65823d4
parenteaa4b1a6e4d9f9bec10257a548d25b2425a83ee5 (diff)
downloadgo-3367475e83eeccd79a5c73c2cc2e91e85e482295.tar.gz
go-3367475e83eeccd79a5c73c2cc2e91e85e482295.zip
go/types, types2: use correct reverse inference approach
To infer type arguments in an assignment of the form var target func(t1, t2, ...) = g where g is a generic function func g[P1, P2, ...](p1, p2, ...) the type checker used the synthetic function call g(t1, t2, ...) But because each argument (of type) t1, t2, ... is assigned to its corresponding parameter p1, p2, ..., type inference uses assignment rules ("inexact match") for unification. As a result, types such as mystring and string match even though they should not (they are not identical), yet function parameter types must be identical to match. This CL fixes this by constructing the synthetic call g'(func(t1, t2, ...)) where g' is the generic function func g'[P1, P2, ...](func(p1, p2, ...)) This mimics the function assignment directly by representing it as a single argument passing (of a function-typed argument). Function parameter types must now be identical to unify. As an added benefit, the implementation is simpler. As a consequence, when such an assignment is invalid because the function types cannot possibly match, we now correctly get an inference error. Without this change, in some cases unification would succeed, only to lead to an assignment error afterwards. While at it, update the date in the copyright notice of testdata/manual.go so we don't need to fix it each time we copy code from a test case in manual.go into a issueXXXXX.go file. Fixes #60688. Change-Id: I716247f426ef33d76c7849b0c33c59124e55b859 Reviewed-on: https://go-review.googlesource.com/c/go/+/501938 Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@google.com> Run-TryBot: Robert Griesemer <gri@google.com> Auto-Submit: Robert Griesemer <gri@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
-rw-r--r--src/cmd/compile/internal/types2/call.go63
-rw-r--r--src/cmd/compile/internal/types2/testdata/manual.go2
-rw-r--r--src/go/types/call.go66
-rw-r--r--src/go/types/testdata/manual.go2
-rw-r--r--src/internal/types/testdata/examples/inference2.go12
-rw-r--r--src/internal/types/testdata/fixedbugs/issue60688.go16
6 files changed, 61 insertions, 100 deletions
diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go
index e08e46fff4..af27339967 100644
--- a/src/cmd/compile/internal/types2/call.go
+++ b/src/cmd/compile/internal/types2/call.go
@@ -8,7 +8,6 @@ package types2
import (
"cmd/compile/internal/syntax"
- "fmt"
. "internal/types/errors"
"strings"
"unicode"
@@ -76,17 +75,19 @@ func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst
return targs, xlist
}
- // If the uninstantiated or partially instantiated function x is used in an
- // assignment (tsig != nil), use the respective function parameter and result
- // types to infer additional type arguments.
+ // If the uninstantiated or partially instantiated function x is used in
+ // an assignment (tsig != nil), infer missing type arguments by treating
+ // the assignment
+ //
+ // var tvar tsig = x
+ //
+ // like a call g(tvar) of the synthetic generic function g
+ //
+ // func g[type_parameters_of_x](func_type_of_x)
+ //
var args []*operand
var params []*Var
- if tsig != nil && sig.tparams != nil && tsig.params.Len() == sig.params.Len() && tsig.results.Len() == sig.results.Len() {
- // x is a generic function and the signature arity matches the target function.
- // To infer x's missing type arguments, treat the function assignment as a call
- // of a synthetic function f where f's parameters are the parameters and results
- // of x and where the arguments to the call of f are values of the parameter and
- // result types of x.
+ if tsig != nil && sig.tparams != nil {
if !versionErr && !check.allowVersion(check.pkg, instErrPos, go1_21) {
if inst != nil {
check.versionErrorf(instErrPos, go1_21, "partially instantiated function in assignment")
@@ -94,22 +95,13 @@ func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst
check.versionErrorf(instErrPos, go1_21, "implicitly instantiated function in assignment")
}
}
- n := tsig.params.Len()
- m := tsig.results.Len()
- args = make([]*operand, n+m)
- params = make([]*Var, n+m)
- for i := 0; i < n; i++ {
- lvar := tsig.params.At(i)
- lname := syntax.NewName(x.Pos(), paramName(lvar.name, i, "parameter"))
- args[i] = &operand{mode: value, expr: lname, typ: lvar.typ}
- params[i] = sig.params.At(i)
- }
- for i := 0; i < m; i++ {
- lvar := tsig.results.At(i)
- lname := syntax.NewName(x.Pos(), paramName(lvar.name, i, "result parameter"))
- args[n+i] = &operand{mode: value, expr: lname, typ: lvar.typ}
- params[n+i] = sig.results.At(i)
- }
+ gsig := NewSignatureType(nil, nil, nil, sig.params, sig.results, sig.variadic)
+ params = []*Var{NewVar(x.Pos(), check.pkg, "", gsig)}
+ // The type of the argument operand is tsig, which is the type of the LHS in an assignment
+ // or the result type in a return statement. Create a pseudo-expression for that operand
+ // that makes sense when reported in error messages from infer, below.
+ expr := syntax.NewName(x.Pos(), "variable in assignment")
+ args = []*operand{{mode: value, expr: expr, typ: tsig}}
}
// Rename type parameters to avoid problems with recursive instantiations.
@@ -140,25 +132,6 @@ func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst
return nil, nil
}
-func paramName(name string, i int, kind string) string {
- if name != "" {
- return name
- }
- return nth(i+1) + " " + kind
-}
-
-func nth(n int) string {
- switch n {
- case 1:
- return "1st"
- case 2:
- return "2nd"
- case 3:
- return "3rd"
- }
- return fmt.Sprintf("%dth", n)
-}
-
func (check *Checker) instantiateSignature(pos syntax.Pos, expr syntax.Expr, typ *Signature, targs []Type, xlist []syntax.Expr) (res *Signature) {
assert(check != nil)
assert(len(targs) == typ.TypeParams().Len())
diff --git a/src/cmd/compile/internal/types2/testdata/manual.go b/src/cmd/compile/internal/types2/testdata/manual.go
index 96d4ba67c2..57dcc227a5 100644
--- a/src/cmd/compile/internal/types2/testdata/manual.go
+++ b/src/cmd/compile/internal/types2/testdata/manual.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Go Authors. All rights reserved.
+// 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.
diff --git a/src/go/types/call.go b/src/go/types/call.go
index 9016f6fd50..7258ab1237 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -7,7 +7,6 @@
package types
import (
- "fmt"
"go/ast"
"go/internal/typeparams"
"go/token"
@@ -78,17 +77,19 @@ func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *t
return targs, xlist
}
- // If the uninstantiated or partially instantiated function x is used in an
- // assignment (tsig != nil), use the respective function parameter and result
- // types to infer additional type arguments.
+ // If the uninstantiated or partially instantiated function x is used in
+ // an assignment (tsig != nil), infer missing type arguments by treating
+ // the assignment
+ //
+ // var tvar tsig = x
+ //
+ // like a call g(tvar) of the synthetic generic function g
+ //
+ // func g[type_parameters_of_x](func_type_of_x)
+ //
var args []*operand
var params []*Var
- if tsig != nil && sig.tparams != nil && tsig.params.Len() == sig.params.Len() && tsig.results.Len() == sig.results.Len() {
- // x is a generic function and the signature arity matches the target function.
- // To infer x's missing type arguments, treat the function assignment as a call
- // of a synthetic function f where f's parameters are the parameters and results
- // of x and where the arguments to the call of f are values of the parameter and
- // result types of x.
+ if tsig != nil && sig.tparams != nil {
if !versionErr && !check.allowVersion(check.pkg, instErrPos, go1_21) {
if ix != nil {
check.versionErrorf(instErrPos, go1_21, "partially instantiated function in assignment")
@@ -96,24 +97,14 @@ func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *t
check.versionErrorf(instErrPos, go1_21, "implicitly instantiated function in assignment")
}
}
- n := tsig.params.Len()
- m := tsig.results.Len()
- args = make([]*operand, n+m)
- params = make([]*Var, n+m)
- for i := 0; i < n; i++ {
- lvar := tsig.params.At(i)
- lname := ast.NewIdent(paramName(lvar.name, i, "parameter"))
- lname.NamePos = x.Pos() // correct position
- args[i] = &operand{mode: value, expr: lname, typ: lvar.typ}
- params[i] = sig.params.At(i)
- }
- for i := 0; i < m; i++ {
- lvar := tsig.results.At(i)
- lname := ast.NewIdent(paramName(lvar.name, i, "result parameter"))
- lname.NamePos = x.Pos() // correct position
- args[n+i] = &operand{mode: value, expr: lname, typ: lvar.typ}
- params[n+i] = sig.results.At(i)
- }
+ gsig := NewSignatureType(nil, nil, nil, sig.params, sig.results, sig.variadic)
+ params = []*Var{NewVar(x.Pos(), check.pkg, "", gsig)}
+ // The type of the argument operand is tsig, which is the type of the LHS in an assignment
+ // or the result type in a return statement. Create a pseudo-expression for that operand
+ // that makes sense when reported in error messages from infer, below.
+ expr := ast.NewIdent("variable in assignment")
+ expr.NamePos = x.Pos() // correct position
+ args = []*operand{{mode: value, expr: expr, typ: tsig}}
}
// Rename type parameters to avoid problems with recursive instantiations.
@@ -144,25 +135,6 @@ func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *t
return nil, nil
}
-func paramName(name string, i int, kind string) string {
- if name != "" {
- return name
- }
- return nth(i+1) + " " + kind
-}
-
-func nth(n int) string {
- switch n {
- case 1:
- return "1st"
- case 2:
- return "2nd"
- case 3:
- return "3rd"
- }
- return fmt.Sprintf("%dth", n)
-}
-
func (check *Checker) instantiateSignature(pos token.Pos, expr ast.Expr, typ *Signature, targs []Type, xlist []ast.Expr) (res *Signature) {
assert(check != nil)
assert(len(targs) == typ.TypeParams().Len())
diff --git a/src/go/types/testdata/manual.go b/src/go/types/testdata/manual.go
index 96d4ba67c2..57dcc227a5 100644
--- a/src/go/types/testdata/manual.go
+++ b/src/go/types/testdata/manual.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Go Authors. All rights reserved.
+// 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.
diff --git a/src/internal/types/testdata/examples/inference2.go b/src/internal/types/testdata/examples/inference2.go
index aa2475b741..6097c2b5eb 100644
--- a/src/internal/types/testdata/examples/inference2.go
+++ b/src/internal/types/testdata/examples/inference2.go
@@ -27,9 +27,9 @@ var (
_ func(int) int = f3[int]
v6 func(int, int) = f4
- v7 func(int, string) = f4 // ERROR "type string of 2nd parameter does not match inferred type int for P"
+ v7 func(int, string) = f4 // ERROR "type func(int, string) of variable in assignment does not match inferred type func(int, int) for func(P, P)"
v8 func(int) []int = f5
- v9 func(string) []int = f5 // ERROR "type []int of 1st result parameter does not match inferred type []string for []P"
+ v9 func(string) []int = f5 // ERROR "type func(string) []int of variable in assignment does not match inferred type func(string) []string for func(P) []P"
_, _ func(int) = f1, f1
_, _ func(int) = f1, f2 // ERROR "cannot infer P"
@@ -49,9 +49,9 @@ func _() {
v5 = f3[int]
v6 = f4
- v7 = f4 // ERROR "type string of 2nd parameter does not match inferred type int for P"
+ v7 = f4 // ERROR "type func(int, string) of variable in assignment does not match inferred type func(int, int) for func(P, P)"
v8 = f5
- v9 = f5 // ERROR "type []int of 1st result parameter does not match inferred type []string for []P"
+ v9 = f5 // ERROR "type func(string) []int of variable in assignment does not match inferred type func(string) []string for func(P) []P"
}
// Return statements
@@ -62,11 +62,11 @@ func _() func(int) int { return f3[int] }
func _() func(int, int) { return f4 }
func _() func(int, string) {
- return f4 /* ERROR "type string of 2nd parameter does not match inferred type int for P" */
+ return f4 /* ERROR "type func(int, string) of variable in assignment does not match inferred type func(int, int) for func(P, P)" */
}
func _() func(int) []int { return f5 }
func _() func(string) []int {
- return f5 /* ERROR "type []int of 1st result parameter does not match inferred type []string for []P" */
+ return f5 /* ERROR "type func(string) []int of variable in assignment does not match inferred type func(string) []string for func(P) []P" */
}
func _() (_, _ func(int)) { return f1, f1 }
diff --git a/src/internal/types/testdata/fixedbugs/issue60688.go b/src/internal/types/testdata/fixedbugs/issue60688.go
new file mode 100644
index 0000000000..38d90ee8cc
--- /dev/null
+++ b/src/internal/types/testdata/fixedbugs/issue60688.go
@@ -0,0 +1,16 @@
+// 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
+
+type String string
+
+func g[P any](P, string) {}
+
+// String and string are not identical and thus must not unify
+// (they are element types of the func type and therefore must
+// be identical to match).
+// The result is an error from type inference, rather than an
+// error from an assignment mismatch.
+var f func(int, String) = g // ERROR "type func(int, String) of variable in assignment does not match inferred type func(int, string) for func(P, string)"