From: Robert Griesemer Date: Mon, 11 Dec 2023 21:28:25 +0000 (-0800) Subject: go/types, types2: better error message for cases of reverse type inference X-Git-Tag: go1.22rc1~30 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=2643a591728b0fd2bd1e007443442254d1200201;p=gostls13.git go/types, types2: better error message for cases of reverse type inference Introduce a new type "target" to pass around target types together with a suitable description (typically a variable name) for a better error message. As a side effect, using a specific type (target), rather than just Type avoids accidental confusion with other types. Use the target type description for a better error message in some cases. The error message can be further improved by flipping the order of the sentence (for another CL to keep this one small and simple). Also, and unrelated to this fix, remove the first argument to errorf in infer.go: the argument is always "type" (there's only one call). For #60747. Change-Id: I2118d0fe9e2b4aac959371941064e0e9ca7b3b6e Reviewed-on: https://go-review.googlesource.com/c/go/+/548995 Auto-Submit: Robert Griesemer Run-TryBot: Robert Griesemer Reviewed-by: Robert Griesemer Reviewed-by: Robert Findley TryBot-Result: Gopher Robot --- diff --git a/src/cmd/compile/internal/types2/assignments.go b/src/cmd/compile/internal/types2/assignments.go index 079802b0b0..338a114ff9 100644 --- a/src/cmd/compile/internal/types2/assignments.go +++ b/src/cmd/compile/internal/types2/assignments.go @@ -244,8 +244,15 @@ func (check *Checker) assignVar(lhs, rhs syntax.Expr, x *operand) { } if x == nil { + var target *target + // avoid calling syntax.String if not needed + if T != nil { + if _, ok := under(T).(*Signature); ok { + target = newTarget(T, syntax.String(lhs)) + } + } x = new(operand) - check.expr(T, x, rhs) + check.expr(target, x, rhs) } context := "assignment" @@ -369,7 +376,11 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnStmt sy if l == r && !isCall { var x operand for i, lhs := range lhs { - check.expr(lhs.typ, &x, orig_rhs[i]) + desc := lhs.name + if returnStmt != nil && desc == "" { + desc = "result variable" + } + check.expr(newTarget(lhs.typ, desc), &x, orig_rhs[i]) check.initVar(lhs, &x, context) } return diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index 439f515265..7d9b80f661 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -16,8 +16,8 @@ import ( // funcInst type-checks a function instantiation. // The incoming x must be a generic function. // If inst != nil, it provides some or all of the type arguments (inst.Index). -// If target type tsig != nil, the signature may be used to infer missing type -// arguments of x, if any. At least one of tsig or inst must be provided. +// If target != nil, it may be used to infer missing type arguments of x, if any. +// At least one of T or inst must be provided. // // There are two modes of operation: // @@ -32,8 +32,8 @@ import ( // // If an error (other than a version error) occurs in any case, it is reported // and x.mode is set to invalid. -func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst *syntax.IndexExpr, infer bool) ([]Type, []syntax.Expr) { - assert(tsig != nil || inst != nil) +func (check *Checker) funcInst(T *target, pos syntax.Pos, x *operand, inst *syntax.IndexExpr, infer bool) ([]Type, []syntax.Expr) { + assert(T != nil || inst != nil) var instErrPos poser if inst != nil { @@ -87,7 +87,7 @@ func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst // var args []*operand var params []*Var - if tsig != nil && sig.tparams != nil { + if T != 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") @@ -100,8 +100,8 @@ func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst // 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}} + expr := syntax.NewName(x.Pos(), T.desc) + args = []*operand{{mode: value, expr: expr, typ: T.sig}} } // Rename type parameters to avoid problems with recursive instantiations. diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index 3ffa9431e4..f3e3418f4f 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -449,7 +449,7 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init syntax.Expr) { if lhs == nil || len(lhs) == 1 { assert(lhs == nil || lhs[0] == obj) var x operand - check.expr(obj.typ, &x, init) + check.expr(newTarget(obj.typ, obj.name), &x, init) check.initVar(obj, &x, "variable declaration") return } diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index e1c2c8ff2a..124d9701d6 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -956,18 +956,32 @@ const ( statement ) -// TODO(gri) In rawExpr below, consider using T instead of hint and -// some sort of "operation mode" instead of allowGeneric. -// May be clearer and less error-prone. +// target represent the (signature) type and description of the LHS +// variable of an assignment, or of a function result variable. +type target struct { + sig *Signature + desc string +} + +// newTarget creates a new target for the given type and description. +// The result is nil if typ is not a signature. +func newTarget(typ Type, desc string) *target { + if typ != nil { + if sig, _ := under(typ).(*Signature); sig != nil { + return &target{sig, desc} + } + } + return nil +} // rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. -// If a non-nil target type T is given and e is a generic function -// or function call, T is used to infer the type arguments for e. +// If a non-nil target T is given and e is a generic function, +// T is used to infer the type arguments for e. // If hint != nil, it is the type of a composite literal element. // If allowGeneric is set, the operand type may be an uninstantiated // parameterized type or function value. -func (check *Checker) rawExpr(T Type, x *operand, e syntax.Expr, hint Type, allowGeneric bool) exprKind { +func (check *Checker) rawExpr(T *target, x *operand, e syntax.Expr, hint Type, allowGeneric bool) exprKind { if check.conf.Trace { check.trace(e.Pos(), "-- expr %s", e) check.indent++ @@ -989,9 +1003,9 @@ func (check *Checker) rawExpr(T Type, x *operand, e syntax.Expr, hint Type, allo } // If x is a generic type, or a generic function whose type arguments cannot be inferred -// from a non-nil target type T, nonGeneric reports an error and invalidates x.mode and x.typ. +// from a non-nil target T, nonGeneric reports an error and invalidates x.mode and x.typ. // Otherwise it leaves x alone. -func (check *Checker) nonGeneric(T Type, x *operand) { +func (check *Checker) nonGeneric(T *target, x *operand) { if x.mode == invalid || x.mode == novalue { return } @@ -1004,10 +1018,8 @@ func (check *Checker) nonGeneric(T Type, x *operand) { case *Signature: if t.tparams != nil { if enableReverseTypeInference && T != nil { - if tsig, _ := under(T).(*Signature); tsig != nil { - check.funcInst(tsig, x.Pos(), x, nil, true) - return - } + check.funcInst(T, x.Pos(), x, nil, true) + return } what = "function" } @@ -1022,7 +1034,7 @@ func (check *Checker) nonGeneric(T Type, x *operand) { // exprInternal contains the core of type checking of expressions. // Must only be called by rawExpr. // (See rawExpr for an explanation of the parameters.) -func (check *Checker) exprInternal(T Type, x *operand, e syntax.Expr, hint Type) exprKind { +func (check *Checker) exprInternal(T *target, x *operand, e syntax.Expr, hint Type) exprKind { // make sure x has a valid state in case of bailout // (was go.dev/issue/5770) x.mode = invalid @@ -1332,11 +1344,10 @@ func (check *Checker) exprInternal(T Type, x *operand, e syntax.Expr, hint Type) case *syntax.IndexExpr: if check.indexExpr(x, e) { - var tsig *Signature - if enableReverseTypeInference && T != nil { - tsig, _ = under(T).(*Signature) + if !enableReverseTypeInference { + T = nil } - check.funcInst(tsig, e.Pos(), x, e, true) + check.funcInst(T, e.Pos(), x, e, true) } if x.mode == invalid { goto Error @@ -1547,11 +1558,11 @@ func (check *Checker) typeAssertion(e syntax.Expr, x *operand, T Type, typeSwitc } // expr typechecks expression e and initializes x with the expression value. -// If a non-nil target type T is given and e is a generic function -// or function call, T is used to infer the type arguments for e. +// If a non-nil target T is given and e is a generic function or +// a function call, T is used to infer the type arguments for e. // The result must be a single value. // If an error occurred, x.mode is set to invalid. -func (check *Checker) expr(T Type, x *operand, e syntax.Expr) { +func (check *Checker) expr(T *target, x *operand, e syntax.Expr) { check.rawExpr(T, x, e, nil, false) check.exclude(x, 1<