From: Robert Griesemer Date: Tue, 23 Jan 2024 21:21:19 +0000 (-0800) Subject: go/types, types2: better error msg when type inference fails in a call X-Git-Tag: go1.23rc1~1391 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=e85968670e35fc24987944c56277d80d7884e9cc;p=gostls13.git go/types, types2: better error msg when type inference fails in a call 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 LUCI-TryBot-Result: Go LUCI Auto-Submit: Robert Griesemer Reviewed-by: Robert Findley --- diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index 2e8531b07a..32cd80f74f 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -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 diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index a520f70253..071d11aafa 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -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 } } diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index ac9e7bda31..e69f943619 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -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) } diff --git a/src/go/types/call.go b/src/go/types/call.go index b48eb82b66..79852d4523 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -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 diff --git a/src/go/types/check.go b/src/go/types/check.go index 85fd0744d2..1e2ca59392 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -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 diff --git a/src/go/types/errors.go b/src/go/types/errors.go index 63b0d9db8f..0345cf7844 100644 --- a/src/go/types/errors.go +++ b/src/go/types/errors.go @@ -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 { diff --git a/src/go/types/errors_test.go b/src/go/types/errors_test.go index 3fb9c55ac5..0b81730adf 100644 --- a/src/go/types/errors_test.go +++ b/src/go/types/errors_test.go @@ -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) } diff --git a/src/go/types/infer.go b/src/go/types/infer.go index 889de000b0..8261ae7eb2 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -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 } } diff --git a/src/internal/types/testdata/fixedbugs/issue60542.go b/src/internal/types/testdata/fixedbugs/issue60542.go index b536ddb198..b617c2b57e 100644 --- a/src/internal/types/testdata/fixedbugs/issue60542.go +++ b/src/internal/types/testdata/fixedbugs/issue60542.go @@ -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)