Commit 2923b14a authored by Daniel Martí's avatar Daniel Martí

cmd/compile/internal/gc: don't panic on continue in switch

Continues outside of a loop are not allowed. Most of these possibilities
were tested in label1.go, but one was missing - a plain continue in a
switch/select but no enclosing loop.

This used to error with a "continue not in loop" in 1.8, but recently
was broken by c03e75e5. In particular, innerloop does not only account
for loops, but also for switches and selects. Swap it by bools that
track whether breaks and continues should be allowed.

While at it, improve the wording of errors for breaks that are not where
they should be. Change "loop" by "loop, switch, or select" since they
can be used in any of those.

And add tests to make sure this isn't broken again. Use a separate func
since I couldn't get the compiler to crash on f() itself, possibly due
to the recursive call on itself.

Fixes #19934.

Change-Id: I8f09c6c2107fd95cac50efc2a8cb03cbc128c35e
Reviewed-on: https://go-review.googlesource.com/40357
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarJosh Bleecher Snyder <josharian@gmail.com>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent d4a623f9
...@@ -53,8 +53,10 @@ type controlflow struct { ...@@ -53,8 +53,10 @@ type controlflow struct {
// Gotos that jump forward; required for deferred checkgoto calls. // Gotos that jump forward; required for deferred checkgoto calls.
fwdGotos []*Node fwdGotos []*Node
// Unlabeled break and continue statement tracking. // Breaks are allowed in loops, switches, and selects.
innerloop *Node allowBreak bool
// Continues are allowed only in loops.
allowContinue bool
// Position stack. The current position is top of stack. // Position stack. The current position is top of stack.
pos []src.XPos pos []src.XPos
...@@ -131,8 +133,10 @@ func (c *controlflow) stmt(n *Node) { ...@@ -131,8 +133,10 @@ func (c *controlflow) stmt(n *Node) {
case OCONTINUE, OBREAK: case OCONTINUE, OBREAK:
if n.Left == nil { if n.Left == nil {
// plain break/continue // plain break/continue
if c.innerloop == nil { if n.Op == OCONTINUE && !c.allowContinue {
c.err("%v is not in a loop", n.Op) c.err("%v is not in a loop", n.Op)
} else if !c.allowBreak {
c.err("%v is not in a loop, switch, or select", n.Op)
} }
break break
} }
...@@ -166,8 +170,13 @@ func (c *controlflow) stmt(n *Node) { ...@@ -166,8 +170,13 @@ func (c *controlflow) stmt(n *Node) {
case OFOR, OFORUNTIL, OSWITCH, OSELECT: case OFOR, OFORUNTIL, OSWITCH, OSELECT:
// set up for continue/break in body // set up for continue/break in body
innerloop := c.innerloop allowBreak := c.allowBreak
c.innerloop = n allowContinue := c.allowContinue
c.allowBreak = true
switch n.Op {
case OFOR, OFORUNTIL:
c.allowContinue = true
}
lab := c.labeledNodes[n] lab := c.labeledNodes[n]
if lab != nil { if lab != nil {
// labeled for loop // labeled for loop
...@@ -179,7 +188,8 @@ func (c *controlflow) stmt(n *Node) { ...@@ -179,7 +188,8 @@ func (c *controlflow) stmt(n *Node) {
checkedNbody = true checkedNbody = true
// tear down continue/break // tear down continue/break
c.innerloop = innerloop c.allowBreak = allowBreak
c.allowContinue = allowContinue
if lab != nil { if lab != nil {
lab.ctlNode = nil lab.ctlNode = nil
} }
......
...@@ -12,7 +12,19 @@ package main ...@@ -12,7 +12,19 @@ package main
var x int var x int
func f() { func f1() {
switch x {
case 1:
continue // ERROR "continue is not in a loop$"
}
select {
default:
continue // ERROR "continue is not in a loop$"
}
}
func f2() {
L1: L1:
for { for {
if x == 0 { if x == 0 {
...@@ -31,7 +43,7 @@ L2: ...@@ -31,7 +43,7 @@ L2:
break L2 break L2
} }
if x == 1 { if x == 1 {
continue L2 // ERROR "invalid continue label .*L2|continue is not in a loop" continue L2 // ERROR "invalid continue label .*L2|continue is not in a loop$"
} }
goto L2 goto L2
} }
...@@ -49,7 +61,7 @@ L3: ...@@ -49,7 +61,7 @@ L3:
break L3 break L3
} }
if x == 12 { if x == 12 {
continue L3 // ERROR "invalid continue label .*L3|continue is not in a loop" continue L3 // ERROR "invalid continue label .*L3|continue is not in a loop$"
} }
goto L3 goto L3
} }
...@@ -60,7 +72,7 @@ L4: ...@@ -60,7 +72,7 @@ L4:
break L4 // ERROR "invalid break label .*L4" break L4 // ERROR "invalid break label .*L4"
} }
if x == 14 { if x == 14 {
continue L4 // ERROR "invalid continue label .*L4|continue is not in a loop" continue L4 // ERROR "invalid continue label .*L4|continue is not in a loop$"
} }
if x == 15 { if x == 15 {
goto L4 goto L4
...@@ -68,12 +80,12 @@ L4: ...@@ -68,12 +80,12 @@ L4:
} }
L5: L5:
f() f2()
if x == 16 { if x == 16 {
break L5 // ERROR "invalid break label .*L5" break L5 // ERROR "invalid break label .*L5"
} }
if x == 17 { if x == 17 {
continue L5 // ERROR "invalid continue label .*L5|continue is not in a loop" continue L5 // ERROR "invalid continue label .*L5|continue is not in a loop$"
} }
if x == 18 { if x == 18 {
goto L5 goto L5
...@@ -91,12 +103,12 @@ L5: ...@@ -91,12 +103,12 @@ L5:
} }
} }
continue // ERROR "continue is not in a loop" continue // ERROR "continue is not in a loop$"
for { for {
continue on // ERROR "continue label not defined: on" continue on // ERROR "continue label not defined: on"
} }
break // ERROR "break is not in a loop" break // ERROR "break is not in a loop, switch, or select"
for { for {
break dance // ERROR "break label not defined: dance" break dance // ERROR "break label not defined: dance"
} }
......
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