transform: LoopToForLoop - fix bad emission

For loops only support assignments or function calls for the continuing statement.

Fixed: tint:1064
Change-Id: I07065b2119e7b9f97ca7e46b1464fd72333ca429
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/60212
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/transform/loop_to_for_loop.cc b/src/transform/loop_to_for_loop.cc
index 63c6314..85f7a84 100644
--- a/src/transform/loop_to_for_loop.cc
+++ b/src/transform/loop_to_for_loop.cc
@@ -89,7 +89,8 @@
       return nullptr;
     }
 
-    // The continuing block must be empty or contain a single statement
+    // The continuing block must be empty or contain a single, assignment or
+    // function call statement.
     ast::Statement* continuing = nullptr;
     if (auto* loop_cont = loop->continuing()) {
       if (loop_cont->statements().size() != 1) {
@@ -97,6 +98,10 @@
       }
 
       continuing = loop_cont->statements()[0];
+      if (!continuing
+               ->IsAnyOf<ast::AssignmentStatement, ast::CallStatement>()) {
+        return nullptr;
+      }
 
       // And the continuing statement must not use any of the variables declared
       // in the loop body.
diff --git a/src/transform/loop_to_for_loop_test.cc b/src/transform/loop_to_for_loop_test.cc
index 91f2ffd..402c69a 100644
--- a/src/transform/loop_to_for_loop_test.cc
+++ b/src/transform/loop_to_for_loop_test.cc
@@ -208,6 +208,32 @@
   EXPECT_EQ(expect, str(got));
 }
 
+TEST_F(LoopToForLoopTest, NoTransform_ContinuingIsCompound) {
+  auto* src = R"(
+fn f() {
+  var i : i32;
+  i = 0;
+  loop {
+    if ((i < 15)) {
+      break;
+    }
+    ignore(123);
+
+    continuing {
+      if (false) {
+      }
+    }
+  }
+}
+)";
+
+  auto* expect = src;
+
+  auto got = Run<LoopToForLoop>(src);
+
+  EXPECT_EQ(expect, str(got));
+}
+
 TEST_F(LoopToForLoopTest, NoTransform_ContinuingMultipleStmts) {
   auto* src = R"(
 fn f() {
diff --git a/test/bug/tint/1064.wgsl b/test/bug/tint/1064.wgsl
new file mode 100644
index 0000000..782a564
--- /dev/null
+++ b/test/bug/tint/1064.wgsl
@@ -0,0 +1,16 @@
+[[stage(fragment)]]
+fn main() {
+  loop {
+    if (false) {
+    } else {
+      break;
+    }
+
+    continuing {
+       if (true) {
+       } else {
+        break;
+       }
+    }
+  }
+}
diff --git a/test/bug/tint/1064.wgsl.expected.hlsl b/test/bug/tint/1064.wgsl.expected.hlsl
new file mode 100644
index 0000000..56911a4
--- /dev/null
+++ b/test/bug/tint/1064.wgsl.expected.hlsl
@@ -0,0 +1,15 @@
+void main() {
+  while (true) {
+    if (false) {
+    } else {
+      break;
+    }
+    {
+      if (true) {
+      } else {
+        break;
+      }
+    }
+  }
+  return;
+}
diff --git a/test/bug/tint/1064.wgsl.expected.msl b/test/bug/tint/1064.wgsl.expected.msl
new file mode 100644
index 0000000..f9c26cc
--- /dev/null
+++ b/test/bug/tint/1064.wgsl.expected.msl
@@ -0,0 +1,19 @@
+#include <metal_stdlib>
+
+using namespace metal;
+fragment void tint_symbol() {
+  while (true) {
+    if (false) {
+    } else {
+      break;
+    }
+    {
+      if (true) {
+      } else {
+        break;
+      }
+    }
+  }
+  return;
+}
+
diff --git a/test/bug/tint/1064.wgsl.expected.spvasm b/test/bug/tint/1064.wgsl.expected.spvasm
new file mode 100644
index 0000000..5ebd236
--- /dev/null
+++ b/test/bug/tint/1064.wgsl.expected.spvasm
@@ -0,0 +1,35 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 15
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %main "main"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+       %true = OpConstantTrue %bool
+       %main = OpFunction %void None %1
+          %4 = OpLabel
+               OpBranch %5
+          %5 = OpLabel
+               OpLoopMerge %6 %7 None
+               OpBranch %8
+          %8 = OpLabel
+               OpSelectionMerge %11 None
+               OpBranchConditional %false %12 %13
+         %12 = OpLabel
+               OpBranch %11
+         %13 = OpLabel
+               OpBranch %6
+         %11 = OpLabel
+               OpBranch %7
+          %7 = OpLabel
+               OpBranchConditional %true %5 %6
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd
diff --git a/test/bug/tint/1064.wgsl.expected.wgsl b/test/bug/tint/1064.wgsl.expected.wgsl
new file mode 100644
index 0000000..27842dc
--- /dev/null
+++ b/test/bug/tint/1064.wgsl.expected.wgsl
@@ -0,0 +1,16 @@
+[[stage(fragment)]]
+fn main() {
+  loop {
+    if (false) {
+    } else {
+      break;
+    }
+
+    continuing {
+      if (true) {
+      } else {
+        break;
+      }
+    }
+  }
+}
diff --git a/test/vk-gl-cts/graphicsfuzz/two-nested-do-whiles/0-opt.spvasm.expected.hlsl b/test/vk-gl-cts/graphicsfuzz/two-nested-do-whiles/0-opt.spvasm.expected.hlsl
index 3f53893..47412bc 100644
--- a/test/vk-gl-cts/graphicsfuzz/two-nested-do-whiles/0-opt.spvasm.expected.hlsl
+++ b/test/vk-gl-cts/graphicsfuzz/two-nested-do-whiles/0-opt.spvasm.expected.hlsl
@@ -1,77 +1,50 @@
-SKIP: FAILED
-
-
-[[block]]
-struct buf0 {
-  injectionSwitch : vec2<f32>;
+static float4 x_GLF_color = float4(0.0f, 0.0f, 0.0f, 0.0f);
+cbuffer cbuffer_x_7 : register(b0, space0) {
+  uint4 x_7[1];
 };
+static float4 gl_FragCoord = float4(0.0f, 0.0f, 0.0f, 0.0f);
 
-var<private> x_GLF_color : vec4<f32>;
-
-[[group(0), binding(0)]] var<uniform> x_7 : buf0;
-
-var<private> gl_FragCoord : vec4<f32>;
-
-fn main_1() {
-  var i : i32;
-  var i_1 : i32;
-  var i_2 : i32;
-  x_GLF_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+void main_1() {
+  int i = 0;
+  int i_1 = 0;
+  int i_2 = 0;
+  x_GLF_color = float4(1.0f, 0.0f, 0.0f, 1.0f);
   i = 0;
-  let x_35 : f32 = x_7.injectionSwitch.y;
-  if ((x_35 < 0.0)) {
+  const float x_35 = asfloat(x_7[0].y);
+  if ((x_35 < 0.0f)) {
   } else {
-    var x_42 : bool;
-    let x_41 : f32 = gl_FragCoord.y;
-    x_42 = (x_41 < -1.0);
+    bool x_42 = false;
+    const float x_41 = gl_FragCoord.y;
+    x_42 = (x_41 < -1.0f);
     if (x_42) {
     } else {
-      loop {
-        let x_50 : i32 = i;
-        if ((x_50 >= 256)) {
+      while (true) {
+        if ((i >= 256)) {
           break;
         }
-        loop {
+        while (true) {
           i_1 = 0;
-          loop {
-            let x_58 : i32 = i_1;
-            if ((x_58 < 1)) {
-            } else {
-              break;
-            }
-            if (x_42) {
-              i_2 = 0;
-              loop {
-                let x_66 : i32 = i_2;
-                if ((x_66 < 1)) {
-                } else {
-                  break;
+          {
+            for(; (i_1 < 1); i_1 = (i_1 + 1)) {
+              if (x_42) {
+                i_2 = 0;
+                {
+                  for(; (i_2 < 1); i_2 = (i_2 + 1)) {
+                  }
                 }
-
-                continuing {
-                  let x_70 : i32 = i_2;
-                  i_2 = (x_70 + 1);
-                }
+                continue;
               }
-              continue;
-            }
-            return;
-
-            continuing {
-              let x_72 : i32 = i_1;
-              i_1 = (x_72 + 1);
+              return;
             }
           }
-
-          continuing {
+          {
             if (false) {
             } else {
               break;
             }
           }
         }
-
-        continuing {
+        {
           if (false) {
           } else {
             break;
@@ -84,15 +57,20 @@
 }
 
 struct main_out {
-  [[location(0)]]
-  x_GLF_color_1 : vec4<f32>;
+  float4 x_GLF_color_1;
+};
+struct tint_symbol_1 {
+  float4 gl_FragCoord_param : SV_Position;
+};
+struct tint_symbol_2 {
+  float4 x_GLF_color_1 : SV_Target0;
 };
 
-[[stage(fragment)]]
-fn main([[builtin(position)]] gl_FragCoord_param : vec4<f32>) -> main_out {
+tint_symbol_2 main(tint_symbol_1 tint_symbol) {
+  const float4 gl_FragCoord_param = tint_symbol.gl_FragCoord_param;
   gl_FragCoord = gl_FragCoord_param;
   main_1();
-  return main_out(x_GLF_color);
+  const main_out tint_symbol_3 = {x_GLF_color};
+  const tint_symbol_2 tint_symbol_5 = {tint_symbol_3.x_GLF_color_1};
+  return tint_symbol_5;
 }
-
-Failed to generate: error: break statement must be in a loop or switch case
diff --git a/test/vk-gl-cts/graphicsfuzz/two-nested-do-whiles/0-opt.wgsl.expected.hlsl b/test/vk-gl-cts/graphicsfuzz/two-nested-do-whiles/0-opt.wgsl.expected.hlsl
index 251e7b9..47412bc 100644
--- a/test/vk-gl-cts/graphicsfuzz/two-nested-do-whiles/0-opt.wgsl.expected.hlsl
+++ b/test/vk-gl-cts/graphicsfuzz/two-nested-do-whiles/0-opt.wgsl.expected.hlsl
@@ -1,77 +1,50 @@
-SKIP: FAILED
-
-
-[[block]]
-struct buf0 {
-  injectionSwitch : vec2<f32>;
+static float4 x_GLF_color = float4(0.0f, 0.0f, 0.0f, 0.0f);
+cbuffer cbuffer_x_7 : register(b0, space0) {
+  uint4 x_7[1];
 };
+static float4 gl_FragCoord = float4(0.0f, 0.0f, 0.0f, 0.0f);
 
-var<private> x_GLF_color : vec4<f32>;
-
-[[group(0), binding(0)]] var<uniform> x_7 : buf0;
-
-var<private> gl_FragCoord : vec4<f32>;
-
-fn main_1() {
-  var i : i32;
-  var i_1 : i32;
-  var i_2 : i32;
-  x_GLF_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+void main_1() {
+  int i = 0;
+  int i_1 = 0;
+  int i_2 = 0;
+  x_GLF_color = float4(1.0f, 0.0f, 0.0f, 1.0f);
   i = 0;
-  let x_35 : f32 = x_7.injectionSwitch.y;
-  if ((x_35 < 0.0)) {
+  const float x_35 = asfloat(x_7[0].y);
+  if ((x_35 < 0.0f)) {
   } else {
-    var x_42 : bool;
-    let x_41 : f32 = gl_FragCoord.y;
-    x_42 = (x_41 < -1.0);
+    bool x_42 = false;
+    const float x_41 = gl_FragCoord.y;
+    x_42 = (x_41 < -1.0f);
     if (x_42) {
     } else {
-      loop {
-        let x_50 : i32 = i;
-        if ((x_50 >= 256)) {
+      while (true) {
+        if ((i >= 256)) {
           break;
         }
-        loop {
+        while (true) {
           i_1 = 0;
-          loop {
-            let x_58 : i32 = i_1;
-            if ((x_58 < 1)) {
-            } else {
-              break;
-            }
-            if (x_42) {
-              i_2 = 0;
-              loop {
-                let x_66 : i32 = i_2;
-                if ((x_66 < 1)) {
-                } else {
-                  break;
+          {
+            for(; (i_1 < 1); i_1 = (i_1 + 1)) {
+              if (x_42) {
+                i_2 = 0;
+                {
+                  for(; (i_2 < 1); i_2 = (i_2 + 1)) {
+                  }
                 }
-
-                continuing {
-                  let x_70 : i32 = i_2;
-                  i_2 = (x_70 + 1);
-                }
+                continue;
               }
-              continue;
-            }
-            return;
-
-            continuing {
-              let x_72 : i32 = i_1;
-              i_1 = (x_72 + 1);
+              return;
             }
           }
-
-          continuing {
+          {
             if (false) {
             } else {
               break;
             }
           }
         }
-
-        continuing {
+        {
           if (false) {
           } else {
             break;
@@ -84,18 +57,20 @@
 }
 
 struct main_out {
-  [[location(0)]]
-  x_GLF_color_1 : vec4<f32>;
+  float4 x_GLF_color_1;
+};
+struct tint_symbol_1 {
+  float4 gl_FragCoord_param : SV_Position;
+};
+struct tint_symbol_2 {
+  float4 x_GLF_color_1 : SV_Target0;
 };
 
-[[stage(fragment)]]
-fn main([[builtin(position)]] gl_FragCoord_param : vec4<f32>) -> main_out {
+tint_symbol_2 main(tint_symbol_1 tint_symbol) {
+  const float4 gl_FragCoord_param = tint_symbol.gl_FragCoord_param;
   gl_FragCoord = gl_FragCoord_param;
   main_1();
-  return main_out(x_GLF_color);
+  const main_out tint_symbol_3 = {x_GLF_color};
+  const tint_symbol_2 tint_symbol_5 = {tint_symbol_3.x_GLF_color_1};
+  return tint_symbol_5;
 }
-
-Failed to generate: vk-gl-cts/graphicsfuzz/two-nested-do-whiles/0-opt.wgsl:74:13 error: break statement must be in a loop or switch case
-            break;
-            ^^^^^
-