mirror of https://github.com/golang/go.git
go/types, types2: ensure signatures are instantiated if all type args
are provided Improve the accuracy of recorded types and instances for function calls, by instantiating their signature before checking arguments if all type arguments are provided. This avoids a problem where fully instantiated function signatures are are not recorded as such following an error checking their arguments. Fixes golang/go#51803 Change-Id: Iec4cbd219a2cd19bb1bcf2a5c4019f556e4304b1 Reviewed-on: https://go-review.googlesource.com/c/go/+/451436 Reviewed-by: Robert Griesemer <gri@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
7f75b72904
commit
0789ca4951
|
|
@ -536,19 +536,22 @@ type T[P any] []P
|
|||
{`T`, []string{`string`}, `[]string`},
|
||||
},
|
||||
},
|
||||
{`package issue51803; func foo[T any](T) {}; func _() { foo[int]( /* leave arg away on purpose */ ) }`,
|
||||
[]testInst{{`foo`, []string{`int`}, `func(int)`}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
imports := make(testImporter)
|
||||
conf := Config{Importer: imports}
|
||||
conf := Config{
|
||||
Importer: imports,
|
||||
Error: func(error) {}, // ignore errors
|
||||
}
|
||||
instMap := make(map[*syntax.Name]Instance)
|
||||
useMap := make(map[*syntax.Name]Object)
|
||||
makePkg := func(src string) *Package {
|
||||
f := mustParse("p.go", src)
|
||||
pkg, err := conf.Check("", []*syntax.File{f}, &Info{Instances: instMap, Uses: useMap})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pkg, _ := conf.Check("", []*syntax.File{f}, &Info{Instances: instMap, Uses: useMap})
|
||||
imports[pkg.Name()] = pkg
|
||||
return pkg
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,10 +52,10 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
|
|||
assert(got == want)
|
||||
|
||||
// instantiate function signature
|
||||
res := check.instantiateSignature(x.Pos(), sig, targs, xlist)
|
||||
assert(res.TypeParams().Len() == 0) // signature is not generic anymore
|
||||
check.recordInstance(inst.X, targs, res)
|
||||
x.typ = res
|
||||
sig = check.instantiateSignature(x.Pos(), sig, targs, xlist)
|
||||
assert(sig.TypeParams().Len() == 0) // signature is not generic anymore
|
||||
check.recordInstance(inst.X, targs, sig)
|
||||
x.typ = sig
|
||||
x.mode = value
|
||||
x.expr = inst
|
||||
}
|
||||
|
|
@ -177,6 +177,9 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
|
|||
return statement
|
||||
}
|
||||
|
||||
// Capture wasGeneric before sig is potentially instantiated below.
|
||||
wasGeneric := sig.TypeParams().Len() > 0
|
||||
|
||||
// evaluate type arguments, if any
|
||||
var xlist []syntax.Expr
|
||||
var targs []Type
|
||||
|
|
@ -200,14 +203,33 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
|
|||
x.expr = call
|
||||
return statement
|
||||
}
|
||||
|
||||
// If sig is generic and all type arguments are provided, preempt function
|
||||
// argument type inference by explicitly instantiating the signature. This
|
||||
// ensures that we record accurate type information for sig, even if there
|
||||
// is an error checking its arguments (for example, if an incorrect number
|
||||
// of arguments is supplied).
|
||||
if got == want && want > 0 {
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
check.versionErrorf(inst.Pos(), "go1.18", "function instantiation")
|
||||
}
|
||||
|
||||
sig = check.instantiateSignature(inst.Pos(), sig, targs, xlist)
|
||||
assert(sig.TypeParams().Len() == 0) // signature is not generic anymore
|
||||
check.recordInstance(inst, targs, sig)
|
||||
|
||||
// targs have been consumed; proceed with checking arguments of the
|
||||
// non-generic signature.
|
||||
targs = nil
|
||||
xlist = nil
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate arguments
|
||||
args, _ := check.exprList(call.ArgList, false)
|
||||
isGeneric := sig.TypeParams().Len() > 0
|
||||
sig = check.arguments(call, sig, targs, args, xlist)
|
||||
|
||||
if isGeneric && sig.TypeParams().Len() == 0 {
|
||||
if wasGeneric && sig.TypeParams().Len() == 0 {
|
||||
// update the recorded type of call.Fun to its instantiated type
|
||||
check.recordTypeAndValue(call.Fun, value, sig, nil)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -535,19 +535,22 @@ type T[P any] []P
|
|||
{`T`, []string{`string`}, `[]string`},
|
||||
},
|
||||
},
|
||||
{`package issue51803; func foo[T any](T) {}; func _() { foo[int]( /* leave arg away on purpose */ ) }`,
|
||||
[]testInst{{`foo`, []string{`int`}, `func(int)`}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
imports := make(testImporter)
|
||||
conf := Config{Importer: imports}
|
||||
conf := Config{
|
||||
Importer: imports,
|
||||
Error: func(error) {}, // ignore errors
|
||||
}
|
||||
instMap := make(map[*ast.Ident]Instance)
|
||||
useMap := make(map[*ast.Ident]Object)
|
||||
makePkg := func(src string) *Package {
|
||||
f := mustParse(fset, "p.go", src)
|
||||
pkg, err := conf.Check("", fset, []*ast.File{f}, &Info{Instances: instMap, Uses: useMap})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pkg, _ := conf.Check("", fset, []*ast.File{f}, &Info{Instances: instMap, Uses: useMap})
|
||||
imports[pkg.Name()] = pkg
|
||||
return pkg
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@ func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) {
|
|||
assert(got == want)
|
||||
|
||||
// instantiate function signature
|
||||
res := check.instantiateSignature(x.Pos(), sig, targs, ix.Indices)
|
||||
assert(res.TypeParams().Len() == 0) // signature is not generic anymore
|
||||
check.recordInstance(ix.Orig, targs, res)
|
||||
x.typ = res
|
||||
sig = check.instantiateSignature(x.Pos(), sig, targs, ix.Indices)
|
||||
assert(sig.TypeParams().Len() == 0) // signature is not generic anymore
|
||||
check.recordInstance(ix.Orig, targs, sig)
|
||||
x.typ = sig
|
||||
x.mode = value
|
||||
x.expr = ix.Orig
|
||||
}
|
||||
|
|
@ -108,7 +108,6 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
|||
}
|
||||
x.expr = call.Fun
|
||||
check.record(x)
|
||||
|
||||
} else {
|
||||
check.exprOrType(x, call.Fun, true)
|
||||
}
|
||||
|
|
@ -180,6 +179,9 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
|||
return statement
|
||||
}
|
||||
|
||||
// Capture wasGeneric before sig is potentially instantiated below.
|
||||
wasGeneric := sig.TypeParams().Len() > 0
|
||||
|
||||
// evaluate type arguments, if any
|
||||
var xlist []ast.Expr
|
||||
var targs []Type
|
||||
|
|
@ -203,14 +205,33 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
|||
x.expr = call
|
||||
return statement
|
||||
}
|
||||
|
||||
// If sig is generic and all type arguments are provided, preempt function
|
||||
// argument type inference by explicitly instantiating the signature. This
|
||||
// ensures that we record accurate type information for sig, even if there
|
||||
// is an error checking its arguments (for example, if an incorrect number
|
||||
// of arguments is supplied).
|
||||
if got == want && want > 0 {
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
check.softErrorf(inNode(call.Fun, ix.Lbrack), UnsupportedFeature, "function instantiation requires go1.18 or later")
|
||||
}
|
||||
|
||||
sig = check.instantiateSignature(ix.Pos(), sig, targs, xlist)
|
||||
assert(sig.TypeParams().Len() == 0) // signature is not generic anymore
|
||||
check.recordInstance(ix.Orig, targs, sig)
|
||||
|
||||
// targs have been consumed; proceed with checking arguments of the
|
||||
// non-generic signature.
|
||||
targs = nil
|
||||
xlist = nil
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate arguments
|
||||
args, _ := check.exprList(call.Args, false)
|
||||
isGeneric := sig.TypeParams().Len() > 0
|
||||
sig = check.arguments(call, sig, targs, args, xlist)
|
||||
|
||||
if isGeneric && sig.TypeParams().Len() == 0 {
|
||||
if wasGeneric && sig.TypeParams().Len() == 0 {
|
||||
// Update the recorded type of call.Fun to its instantiated type.
|
||||
check.recordTypeAndValue(call.Fun, value, sig, nil)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue