From 70d836debbaf660d41604761a839e28fa349a8f9 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 20 Mar 2023 11:47:28 -0700 Subject: [PATCH] go.types, types2: factor out checking of LHS in variable assignment Step towards disentangling assignment checking functionality. In preparation for reverse inference of function type arguments, but independently helpful in better separating concerns in the code. Change-Id: I9bac9d8005090c00d9ae6c5cfa13765aacce6b12 Reviewed-on: https://go-review.googlesource.com/c/go/+/477855 Auto-Submit: Robert Griesemer Reviewed-by: Robert Findley TryBot-Result: Gopher Robot Run-TryBot: Robert Griesemer Reviewed-by: Robert Griesemer --- .../compile/internal/types2/assignments.go | 62 ++++++++++++------- src/go/types/assignments.go | 62 ++++++++++++------- 2 files changed, 78 insertions(+), 46 deletions(-) diff --git a/src/cmd/compile/internal/types2/assignments.go b/src/cmd/compile/internal/types2/assignments.go index dd814c2e83..2eecce94c8 100644 --- a/src/cmd/compile/internal/types2/assignments.go +++ b/src/cmd/compile/internal/types2/assignments.go @@ -164,26 +164,21 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type { return x.typ } -func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type { - if x.mode == invalid || x.typ == Typ[Invalid] { - check.use(lhs) - return nil - } - +// lhsVar checks a lhs variable in an assignment and returns its type. +// lhsVar takes care of not counting a lhs identifier as a "use" of +// that identifier. The result is nil if it is the blank identifier, +// and Typ[Invalid] if it is an invalid lhs expression. +func (check *Checker) lhsVar(lhs syntax.Expr) Type { // Determine if the lhs is a (possibly parenthesized) identifier. ident, _ := unparen(lhs).(*syntax.Name) // Don't evaluate lhs if it is the blank identifier. if ident != nil && ident.Value == "_" { check.recordDef(ident, nil) - check.assignment(x, nil, "assignment to _ identifier") - if x.mode == invalid { - return nil - } - return x.typ + return nil } - // If the lhs is an identifier denoting a variable v, this assignment + // If the lhs is an identifier denoting a variable v, this reference // is not a 'use' of v. Remember current value of v.used and restore // after evaluating the lhs via check.expr. var v *Var @@ -200,37 +195,58 @@ func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type { } } - var z operand - check.expr(&z, lhs) + var x operand + check.expr(&x, lhs) + if v != nil { v.used = v_used // restore v.used } - if z.mode == invalid || z.typ == Typ[Invalid] { - return nil + if x.mode == invalid || x.typ == Typ[Invalid] { + return Typ[Invalid] } // spec: "Each left-hand side operand must be addressable, a map index // expression, or the blank identifier. Operands may be parenthesized." - switch z.mode { + switch x.mode { case invalid: - return nil + return Typ[Invalid] case variable, mapindex: // ok default: - if sel, ok := z.expr.(*syntax.SelectorExpr); ok { + if sel, ok := x.expr.(*syntax.SelectorExpr); ok { var op operand check.expr(&op, sel.X) if op.mode == mapindex { - check.errorf(&z, UnaddressableFieldAssign, "cannot assign to struct field %s in map", syntax.String(z.expr)) - return nil + check.errorf(&x, UnaddressableFieldAssign, "cannot assign to struct field %s in map", syntax.String(x.expr)) + return Typ[Invalid] } } - check.errorf(&z, UnassignableOperand, "cannot assign to %s", &z) + check.errorf(&x, UnassignableOperand, "cannot assign to %s", &x) + return Typ[Invalid] + } + + return x.typ +} + +// assignVar checks the assignment lhs = x and returns the type of x. +// If the assignment is invalid, the result is nil. +func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type { + if x.mode == invalid || x.typ == Typ[Invalid] { + check.use(lhs) + return nil + } + + T := check.lhsVar(lhs) // nil if lhs is _ + if T == Typ[Invalid] { return nil } - check.assignment(x, z.typ, "assignment") + context := "assignment" + if T == nil { + context = "assignment to _ identifier" + } + check.assignment(x, T, context) if x.mode == invalid { return nil } diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index a3b28d0e22..2d71c8f100 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -158,26 +158,21 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type { return x.typ } -func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { - if x.mode == invalid || x.typ == Typ[Invalid] { - check.useLHS(lhs) - return nil - } - +// lhsVar checks a lhs variable in an assignment and returns its type. +// lhsVar takes care of not counting a lhs identifier as a "use" of +// that identifier. The result is nil if it is the blank identifier, +// and Typ[Invalid] if it is an invalid lhs expression. +func (check *Checker) lhsVar(lhs ast.Expr) Type { // Determine if the lhs is a (possibly parenthesized) identifier. ident, _ := unparen(lhs).(*ast.Ident) // Don't evaluate lhs if it is the blank identifier. if ident != nil && ident.Name == "_" { check.recordDef(ident, nil) - check.assignment(x, nil, "assignment to _ identifier") - if x.mode == invalid { - return nil - } - return x.typ + return nil } - // If the lhs is an identifier denoting a variable v, this assignment + // If the lhs is an identifier denoting a variable v, this reference // is not a 'use' of v. Remember current value of v.used and restore // after evaluating the lhs via check.expr. var v *Var @@ -194,37 +189,58 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { } } - var z operand - check.expr(&z, lhs) + var x operand + check.expr(&x, lhs) + if v != nil { v.used = v_used // restore v.used } - if z.mode == invalid || z.typ == Typ[Invalid] { - return nil + if x.mode == invalid || x.typ == Typ[Invalid] { + return Typ[Invalid] } // spec: "Each left-hand side operand must be addressable, a map index // expression, or the blank identifier. Operands may be parenthesized." - switch z.mode { + switch x.mode { case invalid: - return nil + return Typ[Invalid] case variable, mapindex: // ok default: - if sel, ok := z.expr.(*ast.SelectorExpr); ok { + if sel, ok := x.expr.(*ast.SelectorExpr); ok { var op operand check.expr(&op, sel.X) if op.mode == mapindex { - check.errorf(&z, UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(z.expr)) - return nil + check.errorf(&x, UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(x.expr)) + return Typ[Invalid] } } - check.errorf(&z, UnassignableOperand, "cannot assign to %s", &z) + check.errorf(&x, UnassignableOperand, "cannot assign to %s", &x) + return Typ[Invalid] + } + + return x.typ +} + +// assignVar checks the assignment lhs = x and returns the type of x. +// If the assignment is invalid, the result is nil. +func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { + if x.mode == invalid || x.typ == Typ[Invalid] { + check.useLHS(lhs) + return nil + } + + T := check.lhsVar(lhs) // nil if lhs is _ + if T == Typ[Invalid] { return nil } - check.assignment(x, z.typ, "assignment") + context := "assignment" + if T == nil { + context = "assignment to _ identifier" + } + check.assignment(x, T, context) if x.mode == invalid { return nil } -- 2.50.0