1
0
mirror of https://github.com/golang/go synced 2024-11-17 03:04:44 -07:00

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 <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>
This commit is contained in:
Robert Griesemer 2023-03-20 11:47:28 -07:00 committed by Gopher Robot
parent c013ed5b52
commit 70d836debb
2 changed files with 78 additions and 46 deletions

View File

@ -164,26 +164,21 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
return x.typ return x.typ
} }
func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type { // lhsVar checks a lhs variable in an assignment and returns its type.
if x.mode == invalid || x.typ == Typ[Invalid] { // lhsVar takes care of not counting a lhs identifier as a "use" of
check.use(lhs) // that identifier. The result is nil if it is the blank identifier,
return nil // 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. // Determine if the lhs is a (possibly parenthesized) identifier.
ident, _ := unparen(lhs).(*syntax.Name) ident, _ := unparen(lhs).(*syntax.Name)
// Don't evaluate lhs if it is the blank identifier. // Don't evaluate lhs if it is the blank identifier.
if ident != nil && ident.Value == "_" { if ident != nil && ident.Value == "_" {
check.recordDef(ident, nil) check.recordDef(ident, nil)
check.assignment(x, nil, "assignment to _ identifier")
if x.mode == invalid {
return nil return nil
} }
return x.typ
}
// 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 // is not a 'use' of v. Remember current value of v.used and restore
// after evaluating the lhs via check.expr. // after evaluating the lhs via check.expr.
var v *Var var v *Var
@ -200,37 +195,58 @@ func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
} }
} }
var z operand var x operand
check.expr(&z, lhs) check.expr(&x, lhs)
if v != nil { if v != nil {
v.used = v_used // restore v.used v.used = v_used // restore v.used
} }
if z.mode == invalid || z.typ == Typ[Invalid] { if x.mode == invalid || x.typ == Typ[Invalid] {
return nil return Typ[Invalid]
} }
// spec: "Each left-hand side operand must be addressable, a map index // spec: "Each left-hand side operand must be addressable, a map index
// expression, or the blank identifier. Operands may be parenthesized." // expression, or the blank identifier. Operands may be parenthesized."
switch z.mode { switch x.mode {
case invalid: case invalid:
return nil return Typ[Invalid]
case variable, mapindex: case variable, mapindex:
// ok // ok
default: default:
if sel, ok := z.expr.(*syntax.SelectorExpr); ok { if sel, ok := x.expr.(*syntax.SelectorExpr); ok {
var op operand var op operand
check.expr(&op, sel.X) check.expr(&op, sel.X)
if op.mode == mapindex { if op.mode == mapindex {
check.errorf(&z, UnaddressableFieldAssign, "cannot assign to struct field %s in map", syntax.String(z.expr)) check.errorf(&x, UnaddressableFieldAssign, "cannot assign to struct field %s in map", syntax.String(x.expr))
return nil 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 return nil
} }
check.assignment(x, z.typ, "assignment") T := check.lhsVar(lhs) // nil if lhs is _
if T == Typ[Invalid] {
return nil
}
context := "assignment"
if T == nil {
context = "assignment to _ identifier"
}
check.assignment(x, T, context)
if x.mode == invalid { if x.mode == invalid {
return nil return nil
} }

View File

@ -158,26 +158,21 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
return x.typ return x.typ
} }
func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { // lhsVar checks a lhs variable in an assignment and returns its type.
if x.mode == invalid || x.typ == Typ[Invalid] { // lhsVar takes care of not counting a lhs identifier as a "use" of
check.useLHS(lhs) // that identifier. The result is nil if it is the blank identifier,
return nil // 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. // Determine if the lhs is a (possibly parenthesized) identifier.
ident, _ := unparen(lhs).(*ast.Ident) ident, _ := unparen(lhs).(*ast.Ident)
// Don't evaluate lhs if it is the blank identifier. // Don't evaluate lhs if it is the blank identifier.
if ident != nil && ident.Name == "_" { if ident != nil && ident.Name == "_" {
check.recordDef(ident, nil) check.recordDef(ident, nil)
check.assignment(x, nil, "assignment to _ identifier")
if x.mode == invalid {
return nil return nil
} }
return x.typ
}
// 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 // is not a 'use' of v. Remember current value of v.used and restore
// after evaluating the lhs via check.expr. // after evaluating the lhs via check.expr.
var v *Var var v *Var
@ -194,37 +189,58 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
} }
} }
var z operand var x operand
check.expr(&z, lhs) check.expr(&x, lhs)
if v != nil { if v != nil {
v.used = v_used // restore v.used v.used = v_used // restore v.used
} }
if z.mode == invalid || z.typ == Typ[Invalid] { if x.mode == invalid || x.typ == Typ[Invalid] {
return nil return Typ[Invalid]
} }
// spec: "Each left-hand side operand must be addressable, a map index // spec: "Each left-hand side operand must be addressable, a map index
// expression, or the blank identifier. Operands may be parenthesized." // expression, or the blank identifier. Operands may be parenthesized."
switch z.mode { switch x.mode {
case invalid: case invalid:
return nil return Typ[Invalid]
case variable, mapindex: case variable, mapindex:
// ok // ok
default: default:
if sel, ok := z.expr.(*ast.SelectorExpr); ok { if sel, ok := x.expr.(*ast.SelectorExpr); ok {
var op operand var op operand
check.expr(&op, sel.X) check.expr(&op, sel.X)
if op.mode == mapindex { if op.mode == mapindex {
check.errorf(&z, UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(z.expr)) check.errorf(&x, UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(x.expr))
return nil 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 return nil
} }
check.assignment(x, z.typ, "assignment") T := check.lhsVar(lhs) // nil if lhs is _
if T == Typ[Invalid] {
return nil
}
context := "assignment"
if T == nil {
context = "assignment to _ identifier"
}
check.assignment(x, T, context)
if x.mode == invalid { if x.mode == invalid {
return nil return nil
} }