]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: ensure signatures are instantiated if all type args
authorRobert Findley <rfindley@google.com>
Thu, 17 Nov 2022 01:58:58 +0000 (20:58 -0500)
committerRobert Findley <rfindley@google.com>
Fri, 18 Nov 2022 01:44:51 +0000 (01:44 +0000)
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>

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

index 5c56e2b7e9831d2cd1ac4d0bd8ae4a846d0d2245..fe84720052eecc91635ec681d1ea328fe8414904 100644 (file)
@@ -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
                }
index 5b1be07e84f3ce935dcdecc2ea82b8f35e700e9b..ffff11ea6e1c350ff0f166c1593585100e7e0f45 100644 (file)
@@ -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)
        }
index 32d6634f53da38cf86a04345b3d384b71f6a486b..98ef6c423f5c4913a49d552bba0e63bf371e89a7 100644 (file)
@@ -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
                }
index 4fb7b05519e2bddfd06f078c7f346e83004f17e9..7a9329613d44ec89804baa0e22681dc0c08b2271 100644 (file)
@@ -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)
        }