Disallow continue inside a switch.

This fails on several platforms in practice, and is of very limited
real-world utility.

Change-Id: Ib476396fc33cb51af6bbcf7fe822d30703ed995d
Bug: skia:12467
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/450993
Commit-Queue: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/sksl/ir/SkSLFunctionDefinition.cpp b/src/sksl/ir/SkSLFunctionDefinition.cpp
index b0b688d..a92aa58 100644
--- a/src/sksl/ir/SkSLFunctionDefinition.cpp
+++ b/src/sksl/ir/SkSLFunctionDefinition.cpp
@@ -12,6 +12,8 @@
 #include "src/sksl/ir/SkSLFunctionDefinition.h"
 #include "src/sksl/ir/SkSLReturnStatement.h"
 
+#include <forward_list>
+
 namespace SkSL {
 
 std::unique_ptr<FunctionDefinition> FunctionDefinition::Convert(const Context& context,
@@ -28,8 +30,8 @@
             , fReferencedIntrinsics(referencedIntrinsics) {}
 
         ~Finalizer() override {
-            SkASSERT(!fBreakableLevel);
-            SkASSERT(!fContinuableLevel);
+            SkASSERT(fBreakableLevel == 0);
+            SkASSERT(fContinuableLevel == std::forward_list<int>{0});
         }
 
         bool functionReturnsValue() const {
@@ -84,28 +86,37 @@
                 case Statement::Kind::kDo:
                 case Statement::Kind::kFor: {
                     ++fBreakableLevel;
-                    ++fContinuableLevel;
+                    ++fContinuableLevel.front();
                     bool result = INHERITED::visitStatement(stmt);
-                    --fContinuableLevel;
+                    --fContinuableLevel.front();
                     --fBreakableLevel;
                     return result;
                 }
                 case Statement::Kind::kSwitch: {
                     ++fBreakableLevel;
+                    fContinuableLevel.push_front(0);
                     bool result = INHERITED::visitStatement(stmt);
+                    fContinuableLevel.pop_front();
                     --fBreakableLevel;
                     return result;
                 }
                 case Statement::Kind::kBreak:
-                    if (!fBreakableLevel) {
+                    if (fBreakableLevel == 0) {
                         fContext.fErrors->error(stmt.fOffset,
                                                 "break statement must be inside a loop or switch");
                     }
                     break;
                 case Statement::Kind::kContinue:
-                    if (!fContinuableLevel) {
-                        fContext.fErrors->error(stmt.fOffset,
-                                                "continue statement must be inside a loop");
+                    if (fContinuableLevel.front() == 0) {
+                        if (std::any_of(fContinuableLevel.begin(),
+                                        fContinuableLevel.end(),
+                                        [](int level) { return level > 0; })) {
+                            fContext.fErrors->error(stmt.fOffset,
+                                                   "continue statement cannot be used in a switch");
+                        } else {
+                            fContext.fErrors->error(stmt.fOffset,
+                                                    "continue statement must be inside a loop");
+                        }
                     }
                     break;
                 default:
@@ -122,7 +133,8 @@
         // how deeply nested we are in breakable constructs (for, do, switch).
         int fBreakableLevel = 0;
         // how deeply nested we are in continuable constructs (for, do).
-        int fContinuableLevel = 0;
+        // We keep a stack (via a forward_list) in order to disallow continue inside of switch.
+        std::forward_list<int> fContinuableLevel{0};
 
         using INHERITED = ProgramWriter;
     };