Commit 4347baac authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: eliminate OXFALL

Previously, we used OXFALL vs OFALL to distinguish fallthrough
statements that had been validated. Because in the Node AST we flatten
statement blocks, OXCASE and OXFALL needed to keep track of their
block scopes for this purpose.

Now that we have an AST that keeps these separate, we can just perform
the validation earlier.

Passes toolstash-check.

Fixes #14540.

Change-Id: I8421eaba16c2b3b72c9c5483b5cf20b14261385e
Reviewed-on: https://go-review.googlesource.com/61130
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent c100a0f6
...@@ -1486,8 +1486,8 @@ func (p *exporter) stmt(n *Node) { ...@@ -1486,8 +1486,8 @@ func (p *exporter) stmt(n *Node) {
p.stmtList(n.List) p.stmtList(n.List)
p.stmtList(n.Nbody) p.stmtList(n.Nbody)
case OFALL, OXFALL: case OFALL:
p.op(OXFALL) p.op(OFALL)
p.pos(n) p.pos(n)
case OBREAK, OCONTINUE: case OBREAK, OCONTINUE:
......
...@@ -1176,7 +1176,6 @@ func (p *importer) node() *Node { ...@@ -1176,7 +1176,6 @@ func (p *importer) node() *Node {
case OXCASE: case OXCASE:
types.Markdcl() types.Markdcl()
n := nodl(p.pos(), OXCASE, nil, nil) n := nodl(p.pos(), OXCASE, nil, nil)
n.Xoffset = int64(types.Block)
n.List.Set(p.exprList()) n.List.Set(p.exprList())
// TODO(gri) eventually we must declare variables for type switch // TODO(gri) eventually we must declare variables for type switch
// statements (type switch statements are not yet exported) // statements (type switch statements are not yet exported)
...@@ -1187,9 +1186,8 @@ func (p *importer) node() *Node { ...@@ -1187,9 +1186,8 @@ func (p *importer) node() *Node {
// case OFALL: // case OFALL:
// unreachable - mapped to OXFALL case below by exporter // unreachable - mapped to OXFALL case below by exporter
case OXFALL: case OFALL:
n := nodl(p.pos(), OXFALL, nil, nil) n := nodl(p.pos(), OFALL, nil, nil)
n.Xoffset = int64(types.Block)
return n return n
case OBREAK, OCONTINUE: case OBREAK, OCONTINUE:
......
...@@ -204,7 +204,6 @@ var goopnames = []string{ ...@@ -204,7 +204,6 @@ var goopnames = []string{
OSUB: "-", OSUB: "-",
OSWITCH: "switch", OSWITCH: "switch",
OXOR: "^", OXOR: "^",
OXFALL: "fallthrough",
} }
func (o Op) String() string { func (o Op) String() string {
...@@ -1080,11 +1079,7 @@ func (n *Node) stmtfmt(s fmt.State, mode fmtMode) { ...@@ -1080,11 +1079,7 @@ func (n *Node) stmtfmt(s fmt.State, mode fmtMode) {
} }
mode.Fprintf(s, ": %v", n.Nbody) mode.Fprintf(s, ": %v", n.Nbody)
case OBREAK, case OBREAK, OCONTINUE, OGOTO, OFALL:
OCONTINUE,
OGOTO,
OFALL,
OXFALL:
if n.Left != nil { if n.Left != nil {
mode.Fprintf(s, "%#v %v", n.Op, n.Left) mode.Fprintf(s, "%#v %v", n.Op, n.Left)
} else { } else {
...@@ -1219,7 +1214,6 @@ var opprec = []int{ ...@@ -1219,7 +1214,6 @@ var opprec = []int{
OSELECT: -1, OSELECT: -1,
OSWITCH: -1, OSWITCH: -1,
OXCASE: -1, OXCASE: -1,
OXFALL: -1,
OEND: 0, OEND: 0,
} }
......
...@@ -710,9 +710,13 @@ func (p *noder) embedded(typ syntax.Expr) *Node { ...@@ -710,9 +710,13 @@ func (p *noder) embedded(typ syntax.Expr) *Node {
} }
func (p *noder) stmts(stmts []syntax.Stmt) []*Node { func (p *noder) stmts(stmts []syntax.Stmt) []*Node {
return p.stmtsFall(stmts, false)
}
func (p *noder) stmtsFall(stmts []syntax.Stmt, fallOK bool) []*Node {
var nodes []*Node var nodes []*Node
for _, stmt := range stmts { for i, stmt := range stmts {
s := p.stmt(stmt) s := p.stmtFall(stmt, fallOK && i+1 == len(stmts))
if s == nil { if s == nil {
} else if s.Op == OBLOCK && s.Ninit.Len() == 0 { } else if s.Op == OBLOCK && s.Ninit.Len() == 0 {
nodes = append(nodes, s.List.Slice()...) nodes = append(nodes, s.List.Slice()...)
...@@ -724,12 +728,16 @@ func (p *noder) stmts(stmts []syntax.Stmt) []*Node { ...@@ -724,12 +728,16 @@ func (p *noder) stmts(stmts []syntax.Stmt) []*Node {
} }
func (p *noder) stmt(stmt syntax.Stmt) *Node { func (p *noder) stmt(stmt syntax.Stmt) *Node {
return p.stmtFall(stmt, false)
}
func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) *Node {
p.lineno(stmt) p.lineno(stmt)
switch stmt := stmt.(type) { switch stmt := stmt.(type) {
case *syntax.EmptyStmt: case *syntax.EmptyStmt:
return nil return nil
case *syntax.LabeledStmt: case *syntax.LabeledStmt:
return p.labeledStmt(stmt) return p.labeledStmt(stmt, fallOK)
case *syntax.BlockStmt: case *syntax.BlockStmt:
l := p.blockStmt(stmt) l := p.blockStmt(stmt)
if len(l) == 0 { if len(l) == 0 {
...@@ -780,7 +788,10 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node { ...@@ -780,7 +788,10 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node {
case syntax.Continue: case syntax.Continue:
op = OCONTINUE op = OCONTINUE
case syntax.Fallthrough: case syntax.Fallthrough:
op = OXFALL if !fallOK {
yyerror("fallthrough statement out of place")
}
op = OFALL
case syntax.Goto: case syntax.Goto:
op = OGOTO op = OGOTO
default: default:
...@@ -790,9 +801,6 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node { ...@@ -790,9 +801,6 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node {
if stmt.Label != nil { if stmt.Label != nil {
n.Left = p.newname(stmt.Label) n.Left = p.newname(stmt.Label)
} }
if op == OXFALL {
n.Xoffset = int64(types.Block)
}
return n return n
case *syntax.CallStmt: case *syntax.CallStmt:
var op Op var op Op
...@@ -912,7 +920,7 @@ func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node { ...@@ -912,7 +920,7 @@ func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node {
} }
tswitch := n.Left tswitch := n.Left
if tswitch != nil && (tswitch.Op != OTYPESW || tswitch.Left == nil) { if tswitch != nil && tswitch.Op != OTYPESW {
tswitch = nil tswitch = nil
} }
n.List.Set(p.caseClauses(stmt.Body, tswitch, stmt.Rbrace)) n.List.Set(p.caseClauses(stmt.Body, tswitch, stmt.Rbrace))
...@@ -934,15 +942,35 @@ func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node, rbrace ...@@ -934,15 +942,35 @@ func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node, rbrace
if clause.Cases != nil { if clause.Cases != nil {
n.List.Set(p.exprList(clause.Cases)) n.List.Set(p.exprList(clause.Cases))
} }
if tswitch != nil { if tswitch != nil && tswitch.Left != nil {
nn := newname(tswitch.Left.Sym) nn := newname(tswitch.Left.Sym)
declare(nn, dclcontext) declare(nn, dclcontext)
n.Rlist.Set1(nn) n.Rlist.Set1(nn)
// keep track of the instances for reporting unused // keep track of the instances for reporting unused
nn.Name.Defn = tswitch nn.Name.Defn = tswitch
} }
n.Xoffset = int64(types.Block)
n.Nbody.Set(p.stmts(clause.Body)) // Trim trailing empty statements. We omit them from
// the Node AST anyway, and it's easier to identify
// out-of-place fallthrough statements without them.
body := clause.Body
for len(body) > 0 {
if _, ok := body[len(body)-1].(*syntax.EmptyStmt); !ok {
break
}
body = body[:len(body)-1]
}
n.Nbody.Set(p.stmtsFall(body, true))
if l := n.Nbody.Len(); l > 0 && n.Nbody.Index(l-1).Op == OFALL {
if tswitch != nil {
yyerror("cannot fallthrough in type switch")
}
if i+1 == len(clauses) {
yyerror("cannot fallthrough final case in switch")
}
}
nodes = append(nodes, n) nodes = append(nodes, n)
} }
if len(clauses) > 0 { if len(clauses) > 0 {
...@@ -980,12 +1008,12 @@ func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace src.Pos) []*Nod ...@@ -980,12 +1008,12 @@ func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace src.Pos) []*Nod
return nodes return nodes
} }
func (p *noder) labeledStmt(label *syntax.LabeledStmt) *Node { func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) *Node {
lhs := p.nod(label, OLABEL, p.newname(label.Label), nil) lhs := p.nod(label, OLABEL, p.newname(label.Label), nil)
var ls *Node var ls *Node
if label.Stmt != nil { // TODO(mdempsky): Should always be present. if label.Stmt != nil { // TODO(mdempsky): Should always be present.
ls = p.stmt(label.Stmt) ls = p.stmtFall(label.Stmt, fallOK)
} }
lhs.Name.Defn = ls lhs.Name.Defn = ls
......
...@@ -122,7 +122,6 @@ var opnames = []string{ ...@@ -122,7 +122,6 @@ var opnames = []string{
ODEFER: "DEFER", ODEFER: "DEFER",
OEMPTY: "EMPTY", OEMPTY: "EMPTY",
OFALL: "FALL", OFALL: "FALL",
OXFALL: "XFALL",
OFOR: "FOR", OFOR: "FOR",
OFORUNTIL: "FORUNTIL", OFORUNTIL: "FORUNTIL",
OGOTO: "GOTO", OGOTO: "GOTO",
......
...@@ -620,7 +620,6 @@ func orderstmt(n *Node, order *Order) { ...@@ -620,7 +620,6 @@ func orderstmt(n *Node, order *Order) {
ODCLCONST, ODCLCONST,
ODCLTYPE, ODCLTYPE,
OFALL, OFALL,
OXFALL,
OGOTO, OGOTO,
OLABEL, OLABEL,
ORETJMP: ORETJMP:
......
...@@ -332,7 +332,6 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) { ...@@ -332,7 +332,6 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
OCLOSE, OCLOSE,
ONEW, ONEW,
OXCASE, OXCASE,
OXFALL,
OCASE, OCASE,
OPANIC, OPANIC,
ORECOVER, ORECOVER,
......
...@@ -380,7 +380,7 @@ func casebody(sw *Node, typeswvar *Node) { ...@@ -380,7 +380,7 @@ func casebody(sw *Node, typeswvar *Node) {
var def *Node // defaults var def *Node // defaults
br := nod(OBREAK, nil, nil) br := nod(OBREAK, nil, nil)
for i, n := range sw.List.Slice() { for _, n := range sw.List.Slice() {
setlineno(n) setlineno(n)
if n.Op != OXCASE { if n.Op != OXCASE {
Fatalf("casebody %v", n.Op) Fatalf("casebody %v", n.Op)
...@@ -474,21 +474,7 @@ func casebody(sw *Node, typeswvar *Node) { ...@@ -474,21 +474,7 @@ func casebody(sw *Node, typeswvar *Node) {
fallIndex-- fallIndex--
} }
last := stat[fallIndex] last := stat[fallIndex]
if last.Op != OFALL {
// botch - shouldn't fall through declaration
if last.Xoffset == n.Xoffset && last.Op == OXFALL {
if typeswvar != nil {
setlineno(last)
yyerror("cannot fallthrough in type switch")
}
if i+1 >= sw.List.Len() {
setlineno(last)
yyerror("cannot fallthrough final case in switch")
}
last.Op = OFALL
} else {
stat = append(stat, br) stat = append(stat, br)
} }
} }
......
...@@ -45,7 +45,6 @@ type Node struct { ...@@ -45,7 +45,6 @@ type Node struct {
// - ONAME nodes that refer to local variables use it to identify their stack frame position. // - ONAME nodes that refer to local variables use it to identify their stack frame position.
// - ODOT, ODOTPTR, and OINDREGSP use it to indicate offset relative to their base address. // - ODOT, ODOTPTR, and OINDREGSP use it to indicate offset relative to their base address.
// - OSTRUCTKEY uses it to store the named field's offset. // - OSTRUCTKEY uses it to store the named field's offset.
// - OXCASE and OXFALL use it to validate the use of fallthrough.
// - Named OLITERALs use it to to store their ambient iota value. // - Named OLITERALs use it to to store their ambient iota value.
// Possibly still more uses. If you find any, document them. // Possibly still more uses. If you find any, document them.
Xoffset int64 Xoffset int64
...@@ -564,8 +563,8 @@ const ( ...@@ -564,8 +563,8 @@ const (
OCONTINUE // continue OCONTINUE // continue
ODEFER // defer Left (Left must be call) ODEFER // defer Left (Left must be call)
OEMPTY // no-op (empty statement) OEMPTY // no-op (empty statement)
OFALL // fallthrough (after processing) _ // placeholder to appease toolstash
OXFALL // fallthrough (before processing) OFALL // fallthrough
OFOR // for Ninit; Left; Right { Nbody } OFOR // for Ninit; Left; Right { Nbody }
OFORUNTIL // for Ninit; Left; Right { Nbody } ; test applied after executing body, not before OFORUNTIL // for Ninit; Left; Right { Nbody } ; test applied after executing body, not before
OGOTO // goto Left OGOTO // goto Left
......
...@@ -2012,7 +2012,7 @@ func typecheck1(n *Node, top int) *Node { ...@@ -2012,7 +2012,7 @@ func typecheck1(n *Node, top int) *Node {
ODCL, ODCL,
OEMPTY, OEMPTY,
OGOTO, OGOTO,
OXFALL, OFALL,
OVARKILL, OVARKILL,
OVARLIVE: OVARLIVE:
ok |= Etop ok |= Etop
...@@ -3898,7 +3898,7 @@ func (n *Node) isterminating() bool { ...@@ -3898,7 +3898,7 @@ func (n *Node) isterminating() bool {
case OBLOCK: case OBLOCK:
return n.List.isterminating() return n.List.isterminating()
case OGOTO, ORETURN, ORETJMP, OPANIC, OXFALL: case OGOTO, ORETURN, ORETJMP, OPANIC, OFALL:
return true return true
case OFOR, OFORUNTIL: case OFOR, OFORUNTIL:
......
...@@ -348,10 +348,6 @@ func walkstmt(n *Node) *Node { ...@@ -348,10 +348,6 @@ func walkstmt(n *Node) *Node {
case ORANGE: case ORANGE:
n = walkrange(n) n = walkrange(n)
case OXFALL:
yyerror("fallthrough statement out of place")
n.Op = OFALL
} }
if n.Op == ONAME { if n.Op == ONAME {
......
// errorcheck
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
func f(x int) {
switch x {
case 0:
fallthrough
; // ok
case 1:
fallthrough // ERROR "fallthrough statement out of place"
{}
case 2:
fallthrough // ERROR "cannot fallthrough"
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment