1
0
mirror of https://github.com/golang/go synced 2024-09-29 06:34:28 -06: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
}
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
}
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 {
return nil
}

View 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
}
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 {
return nil
}