]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: better error msg when type inference fails in a call
authorRobert Griesemer <gri@golang.org>
Tue, 23 Jan 2024 21:21:19 +0000 (13:21 -0800)
committerGopher Robot <gobot@golang.org>
Wed, 24 Jan 2024 21:28:05 +0000 (21:28 +0000)
In Checker.infer, report an error through an (incoming) *error_
so that the error can be reported as desired where infer is called.
Checker.infer is now a pure function.

Fixes #60543.

At call sites of Checker.infer, pass in an *error_ and use it to
report inference errors, together with additional information as
desired.

Fixes #60542.

In go/types, in error_.errorf, pass in a positioner rather than
a token.Pos. Also, introduce noposn, the positioner equivalent
for nopos. Adjust call sites as needed.

Change-Id: I462a7899a77a8bee2a21ba88299df237d74e0672
Reviewed-on: https://go-review.googlesource.com/c/go/+/558035
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/call.go
src/cmd/compile/internal/types2/infer.go
src/go/types/assignments.go
src/go/types/call.go
src/go/types/check.go
src/go/types/errors.go
src/go/types/errors_test.go
src/go/types/infer.go
src/internal/types/testdata/fixedbugs/issue60542.go

index 2e8531b07af657a38a4cda100f7c6e4332e7f29f..32cd80f74fb13e2496160f3fd8a92d39c81888b9 100644 (file)
@@ -109,9 +109,13 @@ func (check *Checker) funcInst(T *target, pos syntax.Pos, x *operand, inst *synt
                // Note that NewTuple(params...) below is (*Tuple)(nil) if len(params) == 0, as desired.
                tparams, params2 := check.renameTParams(pos, sig.TypeParams().list(), NewTuple(params...))
 
-               targs = check.infer(pos, tparams, targs, params2.(*Tuple), args, reverse)
+               var err error_
+               targs = check.infer(pos, tparams, targs, params2.(*Tuple), args, reverse, &err)
                if targs == nil {
-                       // error was already reported
+                       if !err.empty() {
+                               err.code = CannotInferTypeArgs
+                               check.report(&err)
+                       }
                        x.mode = invalid
                        return nil, nil
                }
@@ -603,13 +607,17 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
 
        // infer missing type arguments of callee and function arguments
        if len(tparams) > 0 {
-               targs = check.infer(call.Pos(), tparams, targs, sigParams, args, false)
+               var err error_
+               targs = check.infer(call.Pos(), tparams, targs, sigParams, args, false, &err)
                if targs == nil {
                        // TODO(gri) If infer inferred the first targs[:n], consider instantiating
                        //           the call signature for better error messages/gopls behavior.
                        //           Perhaps instantiate as much as we can, also for arguments.
                        //           This will require changes to how infer returns its results.
-                       return // error already reported
+                       if !err.empty() {
+                               check.errorf(err.pos(), CannotInferTypeArgs, "in call to %s, %s", call.Fun, err.msg(check.qualifier))
+                       }
+                       return
                }
 
                // update result signature: instantiate if needed
index a520f7025343ba6744de7f98b510ac5000993aa7..071d11aafab7a3015c46db0cf19005dac5e7d97a 100644 (file)
@@ -9,7 +9,6 @@ package types2
 import (
        "cmd/compile/internal/syntax"
        "fmt"
-       . "internal/types/errors"
        "strings"
 )
 
@@ -27,8 +26,9 @@ const enableReverseTypeInference = true // disable for debugging
 // If reverse is set, an error message's contents are reversed for a better error message for some
 // errors related to reverse type inference (where the function call is synthetic).
 // If successful, infer returns the complete list of given and inferred type arguments, one for each
-// type parameter. Otherwise the result is nil and appropriate errors will be reported.
-func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand, reverse bool) (inferred []Type) {
+// type parameter. Otherwise the result is nil. Errors are reported through the err parameter.
+// Note: infer may fail (return nil) due to invalid args operands without reporting additional errors.
+func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand, reverse bool, err *error_) (inferred []Type) {
        // Don't verify result conditions if there's no error handler installed:
        // in that case, an error leads to an exit panic and the result value may
        // be incorrect. But in that case it doesn't matter because callers won't
@@ -127,7 +127,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
                                }
                        }
                        if allFailed {
-                               check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match %s (cannot infer %s)", targ, arg.expr, tpar, typeParamsString(tparams))
+                               err.errorf(arg, "type %s of %s does not match %s (cannot infer %s)", targ, arg.expr, tpar, typeParamsString(tparams))
                                return
                        }
                }
@@ -140,12 +140,12 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
                // the more general CannotInferTypeArgs.
                if inferred != tpar {
                        if reverse {
-                               check.errorf(arg, CannotInferTypeArgs, "inferred type %s for %s does not match type %s of %s", inferred, tpar, targ, arg.expr)
+                               err.errorf(arg, "inferred type %s for %s does not match type %s of %s", inferred, tpar, targ, arg.expr)
                        } else {
-                               check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match inferred type %s for %s", targ, arg.expr, inferred, tpar)
+                               err.errorf(arg, "type %s of %s does not match inferred type %s for %s", targ, arg.expr, inferred, tpar)
                        }
                } else {
-                       check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match %s", targ, arg.expr, tpar)
+                       err.errorf(arg, "type %s of %s does not match %s", targ, arg.expr, tpar)
                }
        }
 
@@ -252,7 +252,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
                                                // TODO(gri) Type parameters that appear in the constraint and
                                                //           for which we have type arguments inferred should
                                                //           use those type arguments for a better error message.
-                                               check.errorf(pos, CannotInferTypeArgs, "%s (type %s) does not satisfy %s", tpar, tx, tpar.Constraint())
+                                               err.errorf(pos, "%s (type %s) does not satisfy %s", tpar, tx, tpar.Constraint())
                                                return nil
                                        }
                                case single && !core.tilde:
@@ -277,7 +277,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
                                        constraint := tpar.iface()
                                        if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause); m != nil {
                                                // TODO(gri) better error message (see TODO above)
-                                               check.errorf(pos, CannotInferTypeArgs, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
+                                               err.errorf(pos, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
                                                return nil
                                        }
                                }
@@ -318,7 +318,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
                        } else {
                                m := maxType(max, arg.typ)
                                if m == nil {
-                                       check.errorf(arg, CannotInferTypeArgs, "mismatched types %s and %s (cannot infer %s)", max, arg.typ, tpar)
+                                       err.errorf(arg, "mismatched types %s and %s (cannot infer %s)", max, arg.typ, tpar)
                                        return nil
                                }
                                max = m
@@ -427,7 +427,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
        for i, typ := range inferred {
                if typ == nil || isParameterized(tparams, typ) {
                        obj := tparams[i].obj
-                       check.errorf(pos, CannotInferTypeArgs, "cannot infer %s (%s)", obj.name, obj.pos)
+                       err.errorf(pos, "cannot infer %s (%s)", obj.name, obj.pos)
                        return nil
                }
        }
index ac9e7bda312d38b03d7ee7fcaed4e30f59b42f43..e69f943619d08287a904c2dce0763a526f3879b3 100644 (file)
@@ -344,9 +344,9 @@ func (check *Checker) returnError(at positioner, lhs []*Var, rhs []*operand) {
        }
        var err error_
        err.code = WrongResultCount
-       err.errorf(at.Pos(), "%s return values", qualifier)
-       err.errorf(nopos, "have %s", check.typesSummary(operandTypes(rhs), false))
-       err.errorf(nopos, "want %s", check.typesSummary(varTypes(lhs), false))
+       err.errorf(at, "%s return values", qualifier)
+       err.errorf(noposn, "have %s", check.typesSummary(operandTypes(rhs), false))
+       err.errorf(noposn, "want %s", check.typesSummary(varTypes(lhs), false))
        check.report(&err)
 }
 
index b48eb82b664f637636edef76336406a8d35dac76..79852d45238e423f68d48d07de951c12d59d9f5d 100644 (file)
@@ -112,9 +112,13 @@ func (check *Checker) funcInst(T *target, pos token.Pos, x *operand, ix *typepar
                // Note that NewTuple(params...) below is (*Tuple)(nil) if len(params) == 0, as desired.
                tparams, params2 := check.renameTParams(pos, sig.TypeParams().list(), NewTuple(params...))
 
-               targs = check.infer(atPos(pos), tparams, targs, params2.(*Tuple), args, reverse)
+               var err error_
+               targs = check.infer(atPos(pos), tparams, targs, params2.(*Tuple), args, reverse, &err)
                if targs == nil {
-                       // error was already reported
+                       if !err.empty() {
+                               err.code = CannotInferTypeArgs
+                               check.report(&err)
+                       }
                        x.mode = invalid
                        return nil, nil
                }
@@ -527,8 +531,8 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
                        params = sig.params.vars
                }
                err := newErrorf(at, WrongArgCount, "%s arguments in call to %s", qualifier, call.Fun)
-               err.errorf(nopos, "have %s", check.typesSummary(operandTypes(args), false))
-               err.errorf(nopos, "want %s", check.typesSummary(varTypes(params), sig.variadic))
+               err.errorf(noposn, "have %s", check.typesSummary(operandTypes(args), false))
+               err.errorf(noposn, "want %s", check.typesSummary(varTypes(params), sig.variadic))
                check.report(err)
                return
        }
@@ -604,13 +608,17 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
 
        // infer missing type arguments of callee and function arguments
        if len(tparams) > 0 {
-               targs = check.infer(call, tparams, targs, sigParams, args, false)
+               var err error_
+               targs = check.infer(call, tparams, targs, sigParams, args, false, &err)
                if targs == nil {
                        // TODO(gri) If infer inferred the first targs[:n], consider instantiating
                        //           the call signature for better error messages/gopls behavior.
                        //           Perhaps instantiate as much as we can, also for arguments.
                        //           This will require changes to how infer returns its results.
-                       return // error already reported
+                       if !err.empty() {
+                               check.errorf(err.posn(), CannotInferTypeArgs, "in call to %s, %s", call.Fun, err.msg(check.fset, check.qualifier))
+                       }
+                       return
                }
 
                // update result signature: instantiate if needed
index 85fd0744d234f293f1884d45fbce35b9ec2b5123..1e2ca59392d5e05037fb95f7da30159526d4db5b 100644 (file)
@@ -17,8 +17,9 @@ import (
        "strings"
 )
 
-// nopos indicates an unknown position
+// nopos, noposn indicate an unknown position
 var nopos token.Pos
+var noposn = atPos(nopos)
 
 // debugging/development support
 const debug = false // leave on during development
index 63b0d9db8f22f4c591d3d349a603d57aa8c1085d..0345cf78443de8629b1ff213b1c93de116be0205 100644 (file)
@@ -52,11 +52,11 @@ func (err *error_) empty() bool {
        return err.desc == nil
 }
 
-func (err *error_) pos() token.Pos {
+func (err *error_) posn() positioner {
        if err.empty() {
-               return nopos
+               return noposn
        }
-       return err.desc[0].posn.Pos()
+       return err.desc[0].posn
 }
 
 func (err *error_) msg(fset *token.FileSet, qf Qualifier) string {
@@ -82,13 +82,13 @@ func (err *error_) String() string {
        if err.empty() {
                return "no error"
        }
-       return fmt.Sprintf("%d: %s", err.pos(), err.msg(nil, nil))
+       return fmt.Sprintf("%d: %s", err.posn().Pos(), err.msg(nil, nil))
 }
 
 // errorf adds formatted error information to err.
 // It may be called multiple times to provide additional information.
-func (err *error_) errorf(at token.Pos, format string, args ...interface{}) {
-       err.desc = append(err.desc, errorDesc{atPos(at), format, args})
+func (err *error_) errorf(at positioner, format string, args ...interface{}) {
+       err.desc = append(err.desc, errorDesc{at, format, args})
 }
 
 func (check *Checker) qualifier(pkg *Package) string {
index 3fb9c55ac527e8caa4a87dc1d2d77f433218083c..0b81730adf7c3dd0e34b40fbef80e51ab7712fd6 100644 (file)
@@ -16,13 +16,13 @@ func TestError(t *testing.T) {
        }
 
        want = "0: foo 42"
-       err.errorf(nopos, "foo %d", 42)
+       err.errorf(noposn, "foo %d", 42)
        if got := err.String(); got != want {
                t.Errorf("simple error: got %q, want %q", got, want)
        }
 
        want = "0: foo 42\n\tbar 43"
-       err.errorf(nopos, "bar %d", 43)
+       err.errorf(noposn, "bar %d", 43)
        if got := err.String(); got != want {
                t.Errorf("simple error: got %q, want %q", got, want)
        }
index 889de000b0ca8fe36920259e5a77345e760623f0..8261ae7eb295f1d5886d93857b26fc7583e62876 100644 (file)
@@ -11,7 +11,6 @@ package types
 import (
        "fmt"
        "go/token"
-       . "internal/types/errors"
        "strings"
 )
 
@@ -29,8 +28,9 @@ const enableReverseTypeInference = true // disable for debugging
 // If reverse is set, an error message's contents are reversed for a better error message for some
 // errors related to reverse type inference (where the function call is synthetic).
 // If successful, infer returns the complete list of given and inferred type arguments, one for each
-// type parameter. Otherwise the result is nil and appropriate errors will be reported.
-func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand, reverse bool) (inferred []Type) {
+// type parameter. Otherwise the result is nil. Errors are reported through the err parameter.
+// Note: infer may fail (return nil) due to invalid args operands without reporting additional errors.
+func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand, reverse bool, err *error_) (inferred []Type) {
        // Don't verify result conditions if there's no error handler installed:
        // in that case, an error leads to an exit panic and the result value may
        // be incorrect. But in that case it doesn't matter because callers won't
@@ -129,7 +129,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
                                }
                        }
                        if allFailed {
-                               check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match %s (cannot infer %s)", targ, arg.expr, tpar, typeParamsString(tparams))
+                               err.errorf(arg, "type %s of %s does not match %s (cannot infer %s)", targ, arg.expr, tpar, typeParamsString(tparams))
                                return
                        }
                }
@@ -142,12 +142,12 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
                // the more general CannotInferTypeArgs.
                if inferred != tpar {
                        if reverse {
-                               check.errorf(arg, CannotInferTypeArgs, "inferred type %s for %s does not match type %s of %s", inferred, tpar, targ, arg.expr)
+                               err.errorf(arg, "inferred type %s for %s does not match type %s of %s", inferred, tpar, targ, arg.expr)
                        } else {
-                               check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match inferred type %s for %s", targ, arg.expr, inferred, tpar)
+                               err.errorf(arg, "type %s of %s does not match inferred type %s for %s", targ, arg.expr, inferred, tpar)
                        }
                } else {
-                       check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match %s", targ, arg.expr, tpar)
+                       err.errorf(arg, "type %s of %s does not match %s", targ, arg.expr, tpar)
                }
        }
 
@@ -254,7 +254,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
                                                // TODO(gri) Type parameters that appear in the constraint and
                                                //           for which we have type arguments inferred should
                                                //           use those type arguments for a better error message.
-                                               check.errorf(posn, CannotInferTypeArgs, "%s (type %s) does not satisfy %s", tpar, tx, tpar.Constraint())
+                                               err.errorf(posn, "%s (type %s) does not satisfy %s", tpar, tx, tpar.Constraint())
                                                return nil
                                        }
                                case single && !core.tilde:
@@ -279,7 +279,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
                                        constraint := tpar.iface()
                                        if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause); m != nil {
                                                // TODO(gri) better error message (see TODO above)
-                                               check.errorf(posn, CannotInferTypeArgs, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
+                                               err.errorf(posn, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
                                                return nil
                                        }
                                }
@@ -320,7 +320,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
                        } else {
                                m := maxType(max, arg.typ)
                                if m == nil {
-                                       check.errorf(arg, CannotInferTypeArgs, "mismatched types %s and %s (cannot infer %s)", max, arg.typ, tpar)
+                                       err.errorf(arg, "mismatched types %s and %s (cannot infer %s)", max, arg.typ, tpar)
                                        return nil
                                }
                                max = m
@@ -429,7 +429,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
        for i, typ := range inferred {
                if typ == nil || isParameterized(tparams, typ) {
                        obj := tparams[i].obj
-                       check.errorf(posn, CannotInferTypeArgs, "cannot infer %s (%s)", obj.name, obj.pos)
+                       err.errorf(posn, "cannot infer %s (%s)", obj.name, obj.pos)
                        return nil
                }
        }
index b536ddb198b19d133c48e586ad3a952dd9d309fd..b617c2b57e46d43367be32a2c5f387be0daef0eb 100644 (file)
@@ -9,4 +9,4 @@ func Clip[S ~[]E, E any](s S) S {
 }
 
 var versions func()
-var _ = Clip /* ERROR "S (type func()) does not satisfy ~[]E" */ (versions)
+var _ = Clip /* ERROR "in call to Clip, S (type func()) does not satisfy ~[]E" */ (versions)