mirror of
https://github.com/golang/go
synced 2024-11-26 17:56:55 -07:00
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>
This commit is contained in:
parent
eaa4b1a6e4
commit
3367475e83
@ -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())
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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())
|
||||
|
2
src/go/types/testdata/manual.go
vendored
2
src/go/types/testdata/manual.go
vendored
@ -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.
|
||||
|
||||
|
@ -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 }
|
||||
|
16
src/internal/types/testdata/fixedbugs/issue60688.go
vendored
Normal file
16
src/internal/types/testdata/fixedbugs/issue60688.go
vendored
Normal file
@ -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)"
|
Loading…
Reference in New Issue
Block a user