From 2923b14a7be6b9d6daa2e140c2e444aa3eade398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 11 Apr 2017 20:31:41 +0100 Subject: [PATCH] cmd/compile/internal/gc: don't panic on continue in switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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í TryBot-Result: Gobot Gobot Reviewed-by: Josh Bleecher Snyder Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/gc/checkcfg.go | 22 +++++++++++++------ test/label1.go | 28 ++++++++++++++++++------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/cmd/compile/internal/gc/checkcfg.go b/src/cmd/compile/internal/gc/checkcfg.go index 1b2fa65645..d55d91ffd2 100644 --- a/src/cmd/compile/internal/gc/checkcfg.go +++ b/src/cmd/compile/internal/gc/checkcfg.go @@ -53,8 +53,10 @@ type controlflow struct { // Gotos that jump forward; required for deferred checkgoto calls. fwdGotos []*Node - // Unlabeled break and continue statement tracking. - innerloop *Node + // Breaks are allowed in loops, switches, and selects. + allowBreak bool + // Continues are allowed only in loops. + allowContinue bool // Position stack. The current position is top of stack. pos []src.XPos @@ -131,8 +133,10 @@ func (c *controlflow) stmt(n *Node) { case OCONTINUE, OBREAK: if n.Left == nil { // plain break/continue - if c.innerloop == nil { + if n.Op == OCONTINUE && !c.allowContinue { 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 } @@ -166,8 +170,13 @@ func (c *controlflow) stmt(n *Node) { case OFOR, OFORUNTIL, OSWITCH, OSELECT: // set up for continue/break in body - innerloop := c.innerloop - c.innerloop = n + allowBreak := c.allowBreak + allowContinue := c.allowContinue + c.allowBreak = true + switch n.Op { + case OFOR, OFORUNTIL: + c.allowContinue = true + } lab := c.labeledNodes[n] if lab != nil { // labeled for loop @@ -179,7 +188,8 @@ func (c *controlflow) stmt(n *Node) { checkedNbody = true // tear down continue/break - c.innerloop = innerloop + c.allowBreak = allowBreak + c.allowContinue = allowContinue if lab != nil { lab.ctlNode = nil } diff --git a/test/label1.go b/test/label1.go index bdd489f177..b2e0ef09b8 100644 --- a/test/label1.go +++ b/test/label1.go @@ -12,7 +12,19 @@ package main 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: for { if x == 0 { @@ -31,7 +43,7 @@ L2: break L2 } 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 } @@ -49,7 +61,7 @@ L3: break L3 } 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 } @@ -60,7 +72,7 @@ L4: break L4 // ERROR "invalid break label .*L4" } 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 { goto L4 @@ -68,12 +80,12 @@ L4: } L5: - f() + f2() if x == 16 { break L5 // ERROR "invalid break label .*L5" } 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 { goto L5 @@ -91,12 +103,12 @@ L5: } } - continue // ERROR "continue is not in a loop" + continue // ERROR "continue is not in a loop$" for { 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 { break dance // ERROR "break label not defined: dance" }