]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: use correct reverse inference approach
authorRobert Griesemer <gri@golang.org>
Fri, 9 Jun 2023 00:04:06 +0000 (17:04 -0700)
committerGopher Robot <gobot@golang.org>
Mon, 12 Jun 2023 17:29:51 +0000 (17:29 +0000)
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>

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

index e08e46fff44881b316fb1b1f344b53a6f732d87f..af273399672a8e905a33adf79361e748b25c2cb7 100644 (file)
@@ -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())
index 96d4ba67c225ffc0eea310a70f8c98edb6304f74..57dcc227a5980279cc165f7154b3f81cfde1cfd5 100644 (file)
@@ -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.
 
index 9016f6fd5085b828a87e7bcf1e0ec2d81e0af7a7..7258ab1237cb7bbbed673cd3739fb2e04caef888 100644 (file)
@@ -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())
index 96d4ba67c225ffc0eea310a70f8c98edb6304f74..57dcc227a5980279cc165f7154b3f81cfde1cfd5 100644 (file)
@@ -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.
 
index aa2475b74182bf5b710a5861fb26673119008637..6097c2b5ebf5de7e62a8f0e0324a513f38a06a59 100644 (file)
@@ -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 (file)
index 0000000..38d90ee
--- /dev/null
@@ -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)"