[dev.typeparams] cmd/compile: use nil instead of syntax.ImplicitOne

Represent x++/-- as x +=/-= with the RHS of the assignment being nil
rather than syntax.ImplicitOne.

Dependent code already had to check for syntax.ImplicitOne, but
then shared some existing code for regular assignment operations.
Now always handle this case fully explicit, which simplifies the
code.

Change-Id: I28c7918153c27cbbf97b041d0c85ff027c58687c
Reviewed-on: https://go-review.googlesource.com/c/go/+/285172
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Robert Griesemer 2021-01-20 17:03:36 -08:00
parent 2427f6e6c0
commit 18bd7aa625
10 changed files with 48 additions and 53 deletions

View file

@ -19,10 +19,6 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node {
return nil return nil
} }
if expr == syntax.ImplicitOne {
base.Fatalf("expr of ImplicitOne")
}
if expr, ok := expr.(*syntax.Name); ok && expr.Value == "_" { if expr, ok := expr.(*syntax.Name); ok && expr.Value == "_" {
return ir.BlankNode return ir.BlankNode
} }

View file

@ -677,11 +677,7 @@ func (p *noder) expr(expr syntax.Expr) ir.Node {
case *syntax.Name: case *syntax.Name:
return p.mkname(expr) return p.mkname(expr)
case *syntax.BasicLit: case *syntax.BasicLit:
pos := base.Pos n := ir.NewBasicLit(p.pos(expr), p.basicLit(expr))
if expr != syntax.ImplicitOne { // ImplicitOne doesn't have a unique position
pos = p.pos(expr)
}
n := ir.NewBasicLit(pos, p.basicLit(expr))
if expr.Kind == syntax.RuneLit { if expr.Kind == syntax.RuneLit {
n.SetType(types.UntypedRune) n.SetType(types.UntypedRune)
} }
@ -1039,9 +1035,15 @@ func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) ir.Node {
case *syntax.DeclStmt: case *syntax.DeclStmt:
return ir.NewBlockStmt(src.NoXPos, p.decls(stmt.DeclList)) return ir.NewBlockStmt(src.NoXPos, p.decls(stmt.DeclList))
case *syntax.AssignStmt: case *syntax.AssignStmt:
if stmt.Rhs == nil {
pos := p.pos(stmt)
n := ir.NewAssignOpStmt(pos, p.binOp(stmt.Op), p.expr(stmt.Lhs), ir.NewBasicLit(pos, one))
n.IncDec = true
return n
}
if stmt.Op != 0 && stmt.Op != syntax.Def { if stmt.Op != 0 && stmt.Op != syntax.Def {
n := ir.NewAssignOpStmt(p.pos(stmt), p.binOp(stmt.Op), p.expr(stmt.Lhs), p.expr(stmt.Rhs)) n := ir.NewAssignOpStmt(p.pos(stmt), p.binOp(stmt.Op), p.expr(stmt.Lhs), p.expr(stmt.Rhs))
n.IncDec = stmt.Rhs == syntax.ImplicitOne
return n return n
} }
@ -1502,7 +1504,7 @@ func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node {
} }
func (p *noder) setlineno(n syntax.Node) { func (p *noder) setlineno(n syntax.Node) {
if n != nil && n != syntax.ImplicitOne { if n != nil {
base.Pos = p.pos(n) base.Pos = p.pos(n)
} }
} }

View file

@ -53,7 +53,7 @@ func (g *irgen) stmt0(stmt syntax.Stmt) ir.Node {
case *syntax.AssignStmt: case *syntax.AssignStmt:
if stmt.Op != 0 && stmt.Op != syntax.Def { if stmt.Op != 0 && stmt.Op != syntax.Def {
op := g.op(stmt.Op, binOps[:]) op := g.op(stmt.Op, binOps[:])
if stmt.Rhs == syntax.ImplicitOne { if stmt.Rhs == nil {
return IncDec(g.pos(stmt), op, g.expr(stmt.Lhs)) return IncDec(g.pos(stmt), op, g.expr(stmt.Lhs))
} }
return ir.NewAssignOpStmt(g.pos(stmt), op, g.expr(stmt.Lhs), g.expr(stmt.Rhs)) return ir.NewAssignOpStmt(g.pos(stmt), op, g.expr(stmt.Lhs), g.expr(stmt.Rhs))

View file

@ -367,7 +367,7 @@ type (
AssignStmt struct { AssignStmt struct {
Op Operator // 0 means no operation Op Operator // 0 means no operation
Lhs, Rhs Expr // Rhs == ImplicitOne means Lhs++ (Op == Add) or Lhs-- (Op == Sub) Lhs, Rhs Expr // Rhs == nil means Lhs++ (Op == Add) or Lhs-- (Op == Sub)
simpleStmt simpleStmt
} }

View file

@ -1874,10 +1874,6 @@ func (p *parser) badExpr() *BadExpr {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Statements // Statements
// We represent x++, x-- as assignments x += ImplicitOne, x -= ImplicitOne.
// ImplicitOne should not be used elsewhere.
var ImplicitOne = &BasicLit{Value: "1"}
// SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl . // SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
func (p *parser) simpleStmt(lhs Expr, keyword token) SimpleStmt { func (p *parser) simpleStmt(lhs Expr, keyword token) SimpleStmt {
if trace { if trace {
@ -1910,7 +1906,7 @@ func (p *parser) simpleStmt(lhs Expr, keyword token) SimpleStmt {
// lhs++ or lhs-- // lhs++ or lhs--
op := p.op op := p.op
p.next() p.next()
return p.newAssignStmt(pos, op, lhs, ImplicitOne) return p.newAssignStmt(pos, op, lhs, nil)
case _Arrow: case _Arrow:
// lhs <- rhs // lhs <- rhs

View file

@ -549,7 +549,7 @@ func (p *printer) printRawNode(n Node) {
case *AssignStmt: case *AssignStmt:
p.print(n.Lhs) p.print(n.Lhs)
if n.Rhs == ImplicitOne { if n.Rhs == nil {
// TODO(gri) This is going to break the mayCombine // TODO(gri) This is going to break the mayCombine
// check once we enable that again. // check once we enable that again.
p.print(n.Op, n.Op) // ++ or -- p.print(n.Op, n.Op) // ++ or --

View file

@ -207,7 +207,9 @@ func (w *walker) node(n Node) {
case *AssignStmt: case *AssignStmt:
w.node(n.Lhs) w.node(n.Lhs)
w.node(n.Rhs) if n.Rhs != nil {
w.node(n.Rhs)
}
case *BranchStmt: case *BranchStmt:
if n.Label != nil { if n.Label != nil {

View file

@ -889,6 +889,7 @@ var binaryOpPredicates = opPredicates{
} }
// The binary expression e may be nil. It's passed in for better error messages only. // The binary expression e may be nil. It's passed in for better error messages only.
// TODO(gri) revisit use of e and opPos
func (check *Checker) binary(x *operand, e *syntax.Operation, lhs, rhs syntax.Expr, op syntax.Operator, opPos syntax.Pos) { func (check *Checker) binary(x *operand, e *syntax.Operation, lhs, rhs syntax.Expr, op syntax.Operator, opPos syntax.Pos) {
var y operand var y operand

View file

@ -286,7 +286,7 @@ func endPos(n syntax.Node) syntax.Pos {
return n.Pos() return n.Pos()
case *syntax.AssignStmt: case *syntax.AssignStmt:
m = n.Rhs m = n.Rhs
if m == syntax.ImplicitOne { if m == nil {
p := endPos(n.Lhs) p := endPos(n.Lhs)
return syntax.MakePos(p.Base(), p.Line(), p.Col()+2) return syntax.MakePos(p.Base(), p.Line(), p.Col()+2)
} }

View file

@ -367,47 +367,45 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
case *syntax.AssignStmt: case *syntax.AssignStmt:
lhs := unpackExpr(s.Lhs) lhs := unpackExpr(s.Lhs)
rhs := unpackExpr(s.Rhs) if s.Rhs == nil {
if s.Op == 0 || s.Op == syntax.Def { // x++ or x--
// regular assignment or short variable declaration if len(lhs) != 1 {
if len(lhs) == 0 { check.invalidASTf(s, "%s%s requires one operand", s.Op, s.Op)
check.invalidASTf(s, "missing lhs in assignment")
return return
} }
if s.Op == syntax.Def {
check.shortVarDecl(s.Pos(), lhs, rhs)
} else {
// regular assignment
check.assignVars(lhs, rhs)
}
} else {
// assignment operations
if len(lhs) != 1 || len(rhs) != 1 {
check.errorf(s, "assignment operation %s requires single-valued expressions", s.Op)
return
}
// provide better error messages for x++ and x--
if rhs[0] == syntax.ImplicitOne {
var x operand
check.expr(&x, lhs[0])
if x.mode == invalid {
return
}
if !isNumeric(x.typ) {
check.invalidOpf(lhs[0], "%s%s%s (non-numeric type %s)", lhs[0], s.Op, s.Op, x.typ)
return
}
}
var x operand var x operand
check.binary(&x, nil, lhs[0], rhs[0], s.Op, rhs[0].Pos()) // TODO(gri) should have TokPos here (like in go/types) check.expr(&x, lhs[0])
if x.mode == invalid { if x.mode == invalid {
return return
} }
if !isNumeric(x.typ) {
check.invalidOpf(lhs[0], "%s%s%s (non-numeric type %s)", lhs[0], s.Op, s.Op, x.typ)
return
}
check.assignVar(lhs[0], &x) check.assignVar(lhs[0], &x)
return
} }
rhs := unpackExpr(s.Rhs)
switch s.Op {
case 0:
check.assignVars(lhs, rhs)
return
case syntax.Def:
check.shortVarDecl(s.Pos(), lhs, rhs)
return
}
// assignment operations
if len(lhs) != 1 || len(rhs) != 1 {
check.errorf(s, "assignment operation %s requires single-valued expressions", s.Op)
return
}
var x operand
check.binary(&x, nil, lhs[0], rhs[0], s.Op, s.Pos())
check.assignVar(lhs[0], &x)
// case *syntax.GoStmt: // case *syntax.GoStmt:
// check.suspendedCall("go", s.Call) // check.suspendedCall("go", s.Call)