diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go index c139771730..9fc3dd2778 100644 --- a/src/cmd/compile/internal/gc/escape.go +++ b/src/cmd/compile/internal/gc/escape.go @@ -302,7 +302,7 @@ func (e *Escape) stmt(n ir.Node) { default: base.Fatalf("unexpected stmt: %v", n) - case ir.ODCLCONST, ir.ODCLTYPE, ir.OEMPTY, ir.OFALL, ir.OINLMARK: + case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL, ir.OINLMARK: // nop case ir.OBREAK, ir.OCONTINUE, ir.OGOTO: diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index 7b21efb8c2..85518bc939 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -1048,6 +1048,17 @@ func (w *exportWriter) stmt(n ir.Node) { } switch op := n.Op(); op { + case ir.OBLOCK: + // No OBLOCK in export data. + // Inline content into this statement list, + // like the init list above. + // (At the moment neither the parser nor the typechecker + // generate OBLOCK nodes except to denote an empty + // function body, although that may change.) + for _, n := range n.List().Slice() { + w.stmt(n) + } + case ir.ODCL: w.op(ir.ODCL) w.pos(n.Left().Pos()) @@ -1129,9 +1140,6 @@ func (w *exportWriter) stmt(n ir.Node) { w.op(ir.OFALL) w.pos(n.Pos()) - case ir.OEMPTY: - // nothing to emit - case ir.OBREAK, ir.OCONTINUE, ir.OGOTO, ir.OLABEL: w.op(op) w.pos(n.Pos()) diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go index 419db285b5..1d9baed5ad 100644 --- a/src/cmd/compile/internal/gc/iimport.go +++ b/src/cmd/compile/internal/gc/iimport.go @@ -742,7 +742,9 @@ func (r *importReader) stmtList() []ir.Node { if n == nil { break } - // OBLOCK nodes may be created when importing ODCL nodes - unpack them + // OBLOCK nodes are not written to the import data directly, + // but the handling of ODCL calls liststmt, which creates one. + // Inline them into the statement list. if n.Op() == ir.OBLOCK { list = append(list, n.List().Slice()...) } else { diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go index e67a032c5d..dc825b2421 100644 --- a/src/cmd/compile/internal/gc/init.go +++ b/src/cmd/compile/internal/gc/init.go @@ -88,8 +88,10 @@ func fninit(n []ir.Node) { s := lookupN("init.", i) fn := ir.AsNode(s.Def).Name().Defn // Skip init functions with empty bodies. - if fn.Body().Len() == 1 && fn.Body().First().Op() == ir.OEMPTY { - continue + if fn.Body().Len() == 1 { + if stmt := fn.Body().First(); stmt.Op() == ir.OBLOCK && stmt.List().Len() == 0 { + continue + } } fns = append(fns, s.Linksym()) } diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index b36a01e389..89c9873c1d 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -396,7 +396,7 @@ func (v *hairyVisitor) visit(n ir.Node) bool { case ir.OAPPEND: v.budget -= inlineExtraAppendCost - case ir.ODCLCONST, ir.OEMPTY, ir.OFALL: + case ir.ODCLCONST, ir.OFALL: // These nodes don't produce code; omit from inlining budget. return false @@ -425,6 +425,11 @@ func (v *hairyVisitor) visit(n ir.Node) bool { v.usedLocals[n] = true } + case ir.OBLOCK: + // The only OBLOCK we should see at this point is an empty one. + // In any event, let the visitList(n.List()) below take care of the statements, + // and don't charge for the OBLOCK itself. The ++ undoes the -- below. + v.budget++ } v.budget-- diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 4c81657628..9352463f18 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -164,7 +164,7 @@ func (p *noder) funcBody(fn *ir.Func, block *syntax.BlockStmt) { if block != nil { body := p.stmts(block.List) if body == nil { - body = []ir.Node{ir.Nod(ir.OEMPTY, nil, nil)} + body = []ir.Node{ir.Nod(ir.OBLOCK, nil, nil)} } fn.PtrBody().Set(body) @@ -967,7 +967,9 @@ func (p *noder) stmtsFall(stmts []syntax.Stmt, fallOK bool) []ir.Node { for i, stmt := range stmts { s := p.stmtFall(stmt, fallOK && i+1 == len(stmts)) if s == nil { - } else if s.Op() == ir.OBLOCK && s.Init().Len() == 0 { + } else if s.Op() == ir.OBLOCK && s.List().Len() > 0 { + // Inline non-empty block. + // Empty blocks must be preserved for checkreturn. nodes = append(nodes, s.List().Slice()...) } else { nodes = append(nodes, s) @@ -991,7 +993,7 @@ func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) ir.Node { l := p.blockStmt(stmt) if len(l) == 0 { // TODO(mdempsky): Line number? - return ir.Nod(ir.OEMPTY, nil, nil) + return ir.Nod(ir.OBLOCK, nil, nil) } return liststmt(l) case *syntax.ExprStmt: @@ -1166,7 +1168,7 @@ func (p *noder) ifStmt(stmt *syntax.IfStmt) ir.Node { n.PtrBody().Set(p.blockStmt(stmt.Then)) if stmt.Else != nil { e := p.stmt(stmt.Else) - if e.Op() == ir.OBLOCK && e.Init().Len() == 0 { + if e.Op() == ir.OBLOCK { n.PtrRlist().Set(e.List().Slice()) } else { n.PtrRlist().Set1(e) @@ -1319,7 +1321,7 @@ func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) ir.Node { l := []ir.Node{lhs} if ls != nil { - if ls.Op() == ir.OBLOCK && ls.Init().Len() == 0 { + if ls.Op() == ir.OBLOCK { l = append(l, ls.List().Slice()...) } else { l = append(l, ls) diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index c2e236537f..352e9c473b 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -676,7 +676,7 @@ func (o *Order) stmt(n ir.Node) { o.cleanTemp(t) // Special: does not save n onto out. - case ir.OBLOCK, ir.OEMPTY: + case ir.OBLOCK: o.stmtList(n.List()) // Special: n->left is not an expression; save as is. diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 60e65e4b11..7c74054b60 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -1071,7 +1071,7 @@ func (s *state) stmt(n ir.Node) { s.stmtList(n.List()) // No-ops - case ir.OEMPTY, ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL: + case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL: // Expression statements case ir.OCALLFUNC: diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 2a0caad469..5a073ac324 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -1950,13 +1950,16 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { case ir.OBREAK, ir.OCONTINUE, ir.ODCL, - ir.OEMPTY, ir.OGOTO, ir.OFALL, ir.OVARKILL, ir.OVARLIVE: ok |= ctxStmt + case ir.OBLOCK: + ok |= ctxStmt + typecheckslice(n.List().Slice(), ctxStmt) + case ir.OLABEL: ok |= ctxStmt decldepth++ @@ -1964,7 +1967,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // Empty identifier is valid but useless. // Eliminate now to simplify life later. // See issues 7538, 11589, 11593. - n = ir.NodAt(n.Pos(), ir.OEMPTY, nil, nil) + n = ir.NodAt(n.Pos(), ir.OBLOCK, nil, nil) } case ir.ODEFER: @@ -3808,7 +3811,7 @@ func deadcode(fn *ir.Func) { } } - fn.PtrBody().Set([]ir.Node{ir.Nod(ir.OEMPTY, nil, nil)}) + fn.PtrBody().Set([]ir.Node{ir.Nod(ir.OBLOCK, nil, nil)}) } func deadcodeslice(nn *ir.Nodes) { diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 183a7acc1b..7e8ae22e4e 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -142,7 +142,6 @@ func walkstmt(n ir.Node) ir.Node { ir.OPRINT, ir.OPRINTN, ir.OPANIC, - ir.OEMPTY, ir.ORECOVER, ir.OGETG: if n.Typecheck() == 0 { @@ -155,7 +154,7 @@ func walkstmt(n ir.Node) ir.Node { if wascopy && n.Op() == ir.ONAME { // copy rewrote to a statement list and a temp for the length. // Throw away the temp to avoid plain values as statements. - n = ir.NodAt(n.Pos(), ir.OEMPTY, nil, nil) + n = ir.NodAt(n.Pos(), ir.OBLOCK, nil, nil) } n = addinit(n, init.Slice()) @@ -470,7 +469,7 @@ opswitch: ir.Dump("walk", n) base.Fatalf("walkexpr: switch 1 unknown op %+S", n) - case ir.ONONAME, ir.OEMPTY, ir.OGETG, ir.ONEWOBJ, ir.OMETHEXPR: + case ir.ONONAME, ir.OGETG, ir.ONEWOBJ, ir.OMETHEXPR: case ir.OTYPE, ir.ONAME, ir.OLITERAL, ir.ONIL: // TODO(mdempsky): Just return n; see discussion on CL 38655. @@ -609,7 +608,7 @@ opswitch: } if oaslit(n, init) { - n = ir.NodAt(n.Pos(), ir.OEMPTY, nil, nil) + n = ir.NodAt(n.Pos(), ir.OBLOCK, nil, nil) break } @@ -2032,10 +2031,10 @@ func walkprint(nn ir.Node, init *ir.Nodes) ir.Node { typecheckslice(calls, ctxStmt) walkexprlist(calls, init) - r := ir.Nod(ir.OEMPTY, nil, nil) + r := ir.Nod(ir.OBLOCK, nil, nil) r = typecheck(r, ctxStmt) - r = walkexpr(r, init) - r.PtrInit().Set(calls) + r = walkstmt(r) + r.PtrList().Set(calls) return r } diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go index 5bb1ed857c..9486d8b021 100644 --- a/src/cmd/compile/internal/ir/fmt.go +++ b/src/cmd/compile/internal/ir/fmt.go @@ -942,6 +942,11 @@ func stmtFmt(n Node, s fmt.State, mode FmtMode) { mode.Fprintf(s, "%.v = %.v", n.List(), n.Rlist()) } + case OBLOCK: + if n.List().Len() != 0 { + mode.Fprintf(s, "%v", n.List()) + } + case ORETURN: mode.Fprintf(s, "return %.v", n.List()) @@ -1044,9 +1049,6 @@ func stmtFmt(n Node, s fmt.State, mode FmtMode) { mode.Fprintf(s, "%#v", n.Op()) } - case OEMPTY: - break - case OLABEL: mode.Fprintf(s, "%v: ", n.Sym()) } @@ -1155,12 +1157,12 @@ var OpPrec = []int{ OAS2MAPR: -1, OAS2RECV: -1, OASOP: -1, + OBLOCK: -1, OBREAK: -1, OCASE: -1, OCONTINUE: -1, ODCL: -1, ODEFER: -1, - OEMPTY: -1, OFALL: -1, OFOR: -1, OFORUNTIL: -1, diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go index d121cc19d4..06bc48e9ca 100644 --- a/src/cmd/compile/internal/ir/node.go +++ b/src/cmd/compile/internal/ir/node.go @@ -301,7 +301,7 @@ const ( OCASE OCONTINUE // continue [Sym] ODEFER // defer Left (Left must be call) - OEMPTY // no-op (empty statement) + OEMPTY // TODO(rsc): Delete. (Use OBLOCK instead.) OFALL // fallthrough OFOR // for Ninit; Left; Right { Nbody } // OFORUNTIL is like OFOR, but the test (Left) is applied after the body: @@ -781,8 +781,6 @@ func NodAt(pos src.XPos, op Op, nleft, nright Node) Node { n := NewTypeAssertExpr(pos, nleft, typ) n.SetOp(op) return n - case OEMPTY: - return NewEmptyStmt(pos) case OFOR: return NewForStmt(pos, nil, nleft, nright, nil) case OGO: diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go index 91714e38e3..a6bbab4889 100644 --- a/src/cmd/compile/internal/ir/stmt.go +++ b/src/cmd/compile/internal/ir/stmt.go @@ -266,22 +266,6 @@ func (n *DeferStmt) rawCopy() Node { c := *n; return &c } func (n *DeferStmt) Left() Node { return n.Call } func (n *DeferStmt) SetLeft(x Node) { n.Call = x } -// An EmptyStmt is an empty statement -type EmptyStmt struct { - miniStmt -} - -func NewEmptyStmt(pos src.XPos) *EmptyStmt { - n := &EmptyStmt{} - n.pos = pos - n.op = OEMPTY - return n -} - -func (n *EmptyStmt) String() string { return fmt.Sprint(n) } -func (n *EmptyStmt) Format(s fmt.State, verb rune) { FmtNode(n, s, verb) } -func (n *EmptyStmt) rawCopy() Node { c := *n; return &c } - // A ForStmt is a non-range for loop: for Init; Cond; Post { Body } // Op can be OFOR or OFORUNTIL (!Cond). type ForStmt struct {