]> Cypherpunks repositories - gostls13.git/commitdiff
go.types, types2: factor out checking of LHS in variable assignment
authorRobert Griesemer <gri@golang.org>
Mon, 20 Mar 2023 18:47:28 +0000 (11:47 -0700)
committerGopher Robot <gobot@golang.org>
Tue, 21 Mar 2023 21:37:14 +0000 (21:37 +0000)
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 <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
src/cmd/compile/internal/types2/assignments.go
src/go/types/assignments.go

index dd814c2e83d948c57d59e00c96a602949f784cdc..2eecce94c8e4b8887bcf9a66b82f469074717f4b 100644 (file)
@@ -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
        }
index a3b28d0e22a50137eb50bfc728467145d8731efe..2d71c8f10092380174c2a27f34fc0b9cb3d08e2c 100644 (file)
@@ -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
        }