case OXCASE:
types.Markdcl()
n := nodl(p.pos(), OXCASE, nil, nil)
- n.Xoffset = int64(types.Block)
n.List.Set(p.exprList())
// TODO(gri) eventually we must declare variables for type switch
// statements (type switch statements are not yet exported)
// case OFALL:
// unreachable - mapped to OXFALL case below by exporter
- case OXFALL:
- n := nodl(p.pos(), OXFALL, nil, nil)
- n.Xoffset = int64(types.Block)
+ case OFALL:
+ n := nodl(p.pos(), OFALL, nil, nil)
return n
case OBREAK, OCONTINUE:
}
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
- for _, stmt := range stmts {
- s := p.stmt(stmt)
+ for i, stmt := range stmts {
+ s := p.stmtFall(stmt, fallOK && i+1 == len(stmts))
if s == nil {
} else if s.Op == OBLOCK && s.Ninit.Len() == 0 {
nodes = append(nodes, s.List.Slice()...)
}
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)
switch stmt := stmt.(type) {
case *syntax.EmptyStmt:
return nil
case *syntax.LabeledStmt:
- return p.labeledStmt(stmt)
+ return p.labeledStmt(stmt, fallOK)
case *syntax.BlockStmt:
l := p.blockStmt(stmt)
if len(l) == 0 {
case syntax.Continue:
op = OCONTINUE
case syntax.Fallthrough:
- op = OXFALL
+ if !fallOK {
+ yyerror("fallthrough statement out of place")
+ }
+ op = OFALL
case syntax.Goto:
op = OGOTO
default:
if stmt.Label != nil {
n.Left = p.newname(stmt.Label)
}
- if op == OXFALL {
- n.Xoffset = int64(types.Block)
- }
return n
case *syntax.CallStmt:
var op Op
}
tswitch := n.Left
- if tswitch != nil && (tswitch.Op != OTYPESW || tswitch.Left == nil) {
+ if tswitch != nil && tswitch.Op != OTYPESW {
tswitch = nil
}
n.List.Set(p.caseClauses(stmt.Body, tswitch, stmt.Rbrace))
if clause.Cases != nil {
n.List.Set(p.exprList(clause.Cases))
}
- if tswitch != nil {
+ if tswitch != nil && tswitch.Left != nil {
nn := newname(tswitch.Left.Sym)
declare(nn, dclcontext)
n.Rlist.Set1(nn)
// keep track of the instances for reporting unused
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)
}
if len(clauses) > 0 {
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)
var ls *Node
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
var def *Node // defaults
br := nod(OBREAK, nil, nil)
- for i, n := range sw.List.Slice() {
+ for _, n := range sw.List.Slice() {
setlineno(n)
if n.Op != OXCASE {
Fatalf("casebody %v", n.Op)
fallIndex--
}
last := stat[fallIndex]
-
- // 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 {
+ if last.Op != OFALL {
stat = append(stat, br)
}
}
// - 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.
// - 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.
// Possibly still more uses. If you find any, document them.
Xoffset int64
OCONTINUE // continue
ODEFER // defer Left (Left must be call)
OEMPTY // no-op (empty statement)
- OFALL // fallthrough (after processing)
- OXFALL // fallthrough (before processing)
+ _ // placeholder to appease toolstash
+ OFALL // fallthrough
OFOR // for Ninit; Left; Right { Nbody }
OFORUNTIL // for Ninit; Left; Right { Nbody } ; test applied after executing body, not before
OGOTO // goto Left