[tint] Emit `unreachable` as `return` for non-void functions

The `PreventInfiniteLoops` transforms changes the control flow such
that some `unreachable` instructions become statically reachable. This
may upset some downstream compilers that want to ensure that non-void
functions always return.

There are various places that unreachable instructions show up, so
just always emit them as `return` statements in the textual backends
instead of only handling this in the infinite loop transform.

Fixed: 381541325
Change-Id: I92b5063361b179f0618e0f5130a29dfae98ea735
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/217894
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/glsl/writer/if_test.cc b/src/tint/lang/glsl/writer/if_test.cc
index aeae183..08ff85a 100644
--- a/src/tint/lang/glsl/writer/if_test.cc
+++ b/src/tint/lang/glsl/writer/if_test.cc
@@ -128,6 +128,32 @@
 )");
 }
 
+TEST_F(GlslWriterTest, IfBothBranchesReturn_NonVoidFunction) {
+    auto* func = b.Function("foo", ty.u32());
+    b.Append(func->Block(), [&] {
+        auto* if_ = b.If(true);
+        b.Append(if_->True(), [&] { b.Return(func, 0_u); });
+        b.Append(if_->False(), [&] { b.Return(func, 1_u); });
+        b.Unreachable();
+    });
+
+    ASSERT_TRUE(Generate()) << err_ << output_.glsl;
+    EXPECT_EQ(output_.glsl, GlslHeader() + R"(
+uint foo() {
+  if (true) {
+    return 0u;
+  } else {
+    return 1u;
+  }
+  /* unreachable */
+  return 0u;
+}
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+}
+)");
+}
+
 TEST_F(GlslWriterTest, IfWithSinglePhi) {
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
diff --git a/src/tint/lang/glsl/writer/printer/printer.cc b/src/tint/lang/glsl/writer/printer/printer.cc
index 0477b23..19938ca 100644
--- a/src/tint/lang/glsl/writer/printer/printer.cc
+++ b/src/tint/lang/glsl/writer/printer/printer.cc
@@ -1724,7 +1724,17 @@
     }
 
     /// Emit an unreachable instruction
-    void EmitUnreachable() { Line() << "/* unreachable */"; }
+    void EmitUnreachable() {
+        Line() << "/* unreachable */";
+        if (!current_function_->ReturnType()->Is<core::type::Void>()) {
+            // If this is inside a non-void function, emit a return statement to avoid potential
+            // errors due to missing return statements.
+            auto out = Line();
+            out << "return ";
+            EmitZeroValue(out, current_function_->ReturnType());
+            out << ";";
+        }
+    }
 };
 
 }  // namespace
diff --git a/src/tint/lang/hlsl/writer/if_test.cc b/src/tint/lang/hlsl/writer/if_test.cc
index 524a1c4..c45e6e6 100644
--- a/src/tint/lang/hlsl/writer/if_test.cc
+++ b/src/tint/lang/hlsl/writer/if_test.cc
@@ -124,6 +124,34 @@
 )");
 }
 
+TEST_F(HlslWriterTest, IfBothBranchesReturn_NonVoidFunction) {
+    auto* func = b.Function("foo", ty.u32());
+    b.Append(func->Block(), [&] {
+        auto* if_ = b.If(true);
+        b.Append(if_->True(), [&] { b.Return(func, 0_u); });
+        b.Append(if_->False(), [&] { b.Return(func, 1_u); });
+        b.Unreachable();
+    });
+
+    ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
+    EXPECT_EQ(output_.hlsl, R"(
+uint foo() {
+  if (true) {
+    return 0u;
+  } else {
+    return 1u;
+  }
+  /* unreachable */
+  return 0u;
+}
+
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+}
+
+)");
+}
+
 TEST_F(HlslWriterTest, IfWithSinglePhi) {
     auto* func = b.ComputeFunction("foo");
     b.Append(func->Block(), [&] {
diff --git a/src/tint/lang/hlsl/writer/printer/printer.cc b/src/tint/lang/hlsl/writer/printer/printer.cc
index 80b9f98..2bffce7 100644
--- a/src/tint/lang/hlsl/writer/printer/printer.cc
+++ b/src/tint/lang/hlsl/writer/printer/printer.cc
@@ -434,16 +434,13 @@
     /// Emit an unreachable instruction
     void EmitUnreachable() {
         Line() << "/* unreachable */";
-        if (current_block_ == current_function_->Block() &&
-            !current_function_->ReturnType()->Is<core::type::Void>()) {
-            // If this is the end of a non-void function, emit a return statement to avoid DXC
-            // errors due to -Wreturn-type + -Werror (see crbug.com/378517038).
-            // TODO(381541325): Remove this once these reachable-unreachables are replaced.
+        if (!current_function_->ReturnType()->Is<core::type::Void>()) {
+            // If this is inside a non-void function, emit a return statement to avoid DXC errors
+            // due to -Wreturn-type + -Werror (see crbug.com/378517038).
             auto out = Line();
             out << "return ";
             EmitZeroValue(out, current_function_->ReturnType());
             out << ";";
-            return;
         }
     }
 
diff --git a/src/tint/lang/msl/writer/if_test.cc b/src/tint/lang/msl/writer/if_test.cc
index 8338165..a10591a 100644
--- a/src/tint/lang/msl/writer/if_test.cc
+++ b/src/tint/lang/msl/writer/if_test.cc
@@ -116,6 +116,29 @@
 )");
 }
 
+TEST_F(MslWriterTest, IfBothBranchesReturn_NonVoidFunction) {
+    auto* func = b.Function("foo", ty.u32());
+    b.Append(func->Block(), [&] {
+        auto* if_ = b.If(true);
+        b.Append(if_->True(), [&] { b.Return(func, 0_u); });
+        b.Append(if_->False(), [&] { b.Return(func, 1_u); });
+        b.Unreachable();
+    });
+
+    ASSERT_TRUE(Generate()) << err_ << output_.msl;
+    EXPECT_EQ(output_.msl, MetalHeader() + R"(
+uint foo() {
+  if (true) {
+    return 0u;
+  } else {
+    return 1u;
+  }
+  /* unreachable */
+  return 0u;
+}
+)");
+}
+
 TEST_F(MslWriterTest, IfWithSinglePhi) {
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
diff --git a/src/tint/lang/msl/writer/printer/printer.cc b/src/tint/lang/msl/writer/printer/printer.cc
index f493452..ab1dd56 100644
--- a/src/tint/lang/msl/writer/printer/printer.cc
+++ b/src/tint/lang/msl/writer/printer/printer.cc
@@ -795,7 +795,17 @@
     }
 
     /// Emit an unreachable instruction
-    void EmitUnreachable() { Line() << "/* unreachable */"; }
+    void EmitUnreachable() {
+        Line() << "/* unreachable */";
+        if (!current_function_->ReturnType()->Is<core::type::Void>()) {
+            // If this is inside a non-void function, emit a return statement to avoid potential
+            // errors due to missing return statements.
+            auto out = Line();
+            out << "return ";
+            EmitZeroValue(out, current_function_->ReturnType());
+            out << ";";
+        }
+    }
 
     /// Emit a discard instruction
     void EmitDiscard() { Line() << "discard_fragment();"; }
diff --git a/test/tint/loops/loop.wgsl.expected.glsl b/test/tint/loops/loop.wgsl.expected.glsl
index 01d1505..a729ae7 100644
--- a/test/tint/loops/loop.wgsl.expected.glsl
+++ b/test/tint/loops/loop.wgsl.expected.glsl
@@ -22,6 +22,7 @@
     }
   }
   /* unreachable */
+  return 0;
 }
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void main() {
diff --git a/test/tint/loops/loop.wgsl.expected.ir.msl b/test/tint/loops/loop.wgsl.expected.ir.msl
index 8a55eb3..8abcec5 100644
--- a/test/tint/loops/loop.wgsl.expected.ir.msl
+++ b/test/tint/loops/loop.wgsl.expected.ir.msl
@@ -23,4 +23,5 @@
     }
   }
   /* unreachable */
+  return 0;
 }
diff --git a/test/tint/loops/loop_robustness.wgsl.expected.glsl b/test/tint/loops/loop_robustness.wgsl.expected.glsl
index 01d1505..a729ae7 100644
--- a/test/tint/loops/loop_robustness.wgsl.expected.glsl
+++ b/test/tint/loops/loop_robustness.wgsl.expected.glsl
@@ -22,6 +22,7 @@
     }
   }
   /* unreachable */
+  return 0;
 }
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void main() {
diff --git a/test/tint/loops/loop_robustness.wgsl.expected.ir.msl b/test/tint/loops/loop_robustness.wgsl.expected.ir.msl
index 8a55eb3..8abcec5 100644
--- a/test/tint/loops/loop_robustness.wgsl.expected.ir.msl
+++ b/test/tint/loops/loop_robustness.wgsl.expected.ir.msl
@@ -23,4 +23,5 @@
     }
   }
   /* unreachable */
+  return 0;
 }
diff --git a/test/tint/loops/loop_with_continuing.wgsl.expected.glsl b/test/tint/loops/loop_with_continuing.wgsl.expected.glsl
index ba98f1c..cabda69 100644
--- a/test/tint/loops/loop_with_continuing.wgsl.expected.glsl
+++ b/test/tint/loops/loop_with_continuing.wgsl.expected.glsl
@@ -22,6 +22,7 @@
     }
   }
   /* unreachable */
+  return 0;
 }
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void main() {
diff --git a/test/tint/loops/loop_with_continuing.wgsl.expected.ir.msl b/test/tint/loops/loop_with_continuing.wgsl.expected.ir.msl
index 9e61e13..653b197 100644
--- a/test/tint/loops/loop_with_continuing.wgsl.expected.ir.msl
+++ b/test/tint/loops/loop_with_continuing.wgsl.expected.ir.msl
@@ -23,4 +23,5 @@
     }
   }
   /* unreachable */
+  return 0;
 }
diff --git a/test/tint/loops/loop_with_continuing_robustness.wgsl.expected.glsl b/test/tint/loops/loop_with_continuing_robustness.wgsl.expected.glsl
index ba98f1c..cabda69 100644
--- a/test/tint/loops/loop_with_continuing_robustness.wgsl.expected.glsl
+++ b/test/tint/loops/loop_with_continuing_robustness.wgsl.expected.glsl
@@ -22,6 +22,7 @@
     }
   }
   /* unreachable */
+  return 0;
 }
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void main() {
diff --git a/test/tint/loops/loop_with_continuing_robustness.wgsl.expected.ir.msl b/test/tint/loops/loop_with_continuing_robustness.wgsl.expected.ir.msl
index 9e61e13..653b197 100644
--- a/test/tint/loops/loop_with_continuing_robustness.wgsl.expected.ir.msl
+++ b/test/tint/loops/loop_with_continuing_robustness.wgsl.expected.ir.msl
@@ -23,4 +23,5 @@
     }
   }
   /* unreachable */
+  return 0;
 }
diff --git a/test/tint/loops/nested_loops.wgsl.expected.glsl b/test/tint/loops/nested_loops.wgsl.expected.glsl
index a28aa87..6780ab0 100644
--- a/test/tint/loops/nested_loops.wgsl.expected.glsl
+++ b/test/tint/loops/nested_loops.wgsl.expected.glsl
@@ -33,9 +33,11 @@
         }
       }
       /* unreachable */
+      return 0;
     }
   }
   /* unreachable */
+  return 0;
 }
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void main() {
diff --git a/test/tint/loops/nested_loops.wgsl.expected.ir.dxc.hlsl b/test/tint/loops/nested_loops.wgsl.expected.ir.dxc.hlsl
index 408d692..052ea2c 100644
--- a/test/tint/loops/nested_loops.wgsl.expected.ir.dxc.hlsl
+++ b/test/tint/loops/nested_loops.wgsl.expected.ir.dxc.hlsl
@@ -32,6 +32,7 @@
         }
       }
       /* unreachable */
+      return int(0);
     }
   }
   /* unreachable */
diff --git a/test/tint/loops/nested_loops.wgsl.expected.ir.fxc.hlsl b/test/tint/loops/nested_loops.wgsl.expected.ir.fxc.hlsl
index 408d692..052ea2c 100644
--- a/test/tint/loops/nested_loops.wgsl.expected.ir.fxc.hlsl
+++ b/test/tint/loops/nested_loops.wgsl.expected.ir.fxc.hlsl
@@ -32,6 +32,7 @@
         }
       }
       /* unreachable */
+      return int(0);
     }
   }
   /* unreachable */
diff --git a/test/tint/loops/nested_loops.wgsl.expected.ir.msl b/test/tint/loops/nested_loops.wgsl.expected.ir.msl
index 5041162..f1447d7 100644
--- a/test/tint/loops/nested_loops.wgsl.expected.ir.msl
+++ b/test/tint/loops/nested_loops.wgsl.expected.ir.msl
@@ -34,7 +34,9 @@
         }
       }
       /* unreachable */
+      return 0;
     }
   }
   /* unreachable */
+  return 0;
 }
diff --git a/test/tint/loops/nested_loops_robustness.wgsl.expected.glsl b/test/tint/loops/nested_loops_robustness.wgsl.expected.glsl
index a28aa87..6780ab0 100644
--- a/test/tint/loops/nested_loops_robustness.wgsl.expected.glsl
+++ b/test/tint/loops/nested_loops_robustness.wgsl.expected.glsl
@@ -33,9 +33,11 @@
         }
       }
       /* unreachable */
+      return 0;
     }
   }
   /* unreachable */
+  return 0;
 }
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void main() {
diff --git a/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.dxc.hlsl b/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.dxc.hlsl
index 408d692..052ea2c 100644
--- a/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.dxc.hlsl
+++ b/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.dxc.hlsl
@@ -32,6 +32,7 @@
         }
       }
       /* unreachable */
+      return int(0);
     }
   }
   /* unreachable */
diff --git a/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.fxc.hlsl b/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.fxc.hlsl
index 408d692..052ea2c 100644
--- a/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.fxc.hlsl
+++ b/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.fxc.hlsl
@@ -32,6 +32,7 @@
         }
       }
       /* unreachable */
+      return int(0);
     }
   }
   /* unreachable */
diff --git a/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.msl b/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.msl
index 5041162..f1447d7 100644
--- a/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.msl
+++ b/test/tint/loops/nested_loops_robustness.wgsl.expected.ir.msl
@@ -34,7 +34,9 @@
         }
       }
       /* unreachable */
+      return 0;
     }
   }
   /* unreachable */
+  return 0;
 }
diff --git a/test/tint/loops/nested_loops_with_continuing.wgsl.expected.glsl b/test/tint/loops/nested_loops_with_continuing.wgsl.expected.glsl
index c94016d..9133f30 100644
--- a/test/tint/loops/nested_loops_with_continuing.wgsl.expected.glsl
+++ b/test/tint/loops/nested_loops_with_continuing.wgsl.expected.glsl
@@ -32,9 +32,11 @@
         }
       }
       /* unreachable */
+      return 0;
     }
   }
   /* unreachable */
+  return 0;
 }
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void main() {
diff --git a/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.dxc.hlsl b/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.dxc.hlsl
index 75b60e6..65a61dd 100644
--- a/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.dxc.hlsl
+++ b/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.dxc.hlsl
@@ -31,6 +31,7 @@
         }
       }
       /* unreachable */
+      return int(0);
     }
   }
   /* unreachable */
diff --git a/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.fxc.hlsl b/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.fxc.hlsl
index 75b60e6..65a61dd 100644
--- a/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.fxc.hlsl
+++ b/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.fxc.hlsl
@@ -31,6 +31,7 @@
         }
       }
       /* unreachable */
+      return int(0);
     }
   }
   /* unreachable */
diff --git a/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.msl b/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.msl
index 2b4726c..d0e6af1 100644
--- a/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.msl
+++ b/test/tint/loops/nested_loops_with_continuing.wgsl.expected.ir.msl
@@ -33,7 +33,9 @@
         }
       }
       /* unreachable */
+      return 0;
     }
   }
   /* unreachable */
+  return 0;
 }
diff --git a/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.glsl b/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.glsl
index c94016d..9133f30 100644
--- a/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.glsl
+++ b/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.glsl
@@ -32,9 +32,11 @@
         }
       }
       /* unreachable */
+      return 0;
     }
   }
   /* unreachable */
+  return 0;
 }
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void main() {
diff --git a/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.dxc.hlsl b/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.dxc.hlsl
index 75b60e6..65a61dd 100644
--- a/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.dxc.hlsl
+++ b/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.dxc.hlsl
@@ -31,6 +31,7 @@
         }
       }
       /* unreachable */
+      return int(0);
     }
   }
   /* unreachable */
diff --git a/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.fxc.hlsl b/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.fxc.hlsl
index 75b60e6..65a61dd 100644
--- a/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.fxc.hlsl
+++ b/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.fxc.hlsl
@@ -31,6 +31,7 @@
         }
       }
       /* unreachable */
+      return int(0);
     }
   }
   /* unreachable */
diff --git a/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.msl b/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.msl
index 2b4726c..d0e6af1 100644
--- a/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.msl
+++ b/test/tint/loops/nested_loops_with_continuing_robustness.wgsl.expected.ir.msl
@@ -33,7 +33,9 @@
         }
       }
       /* unreachable */
+      return 0;
     }
   }
   /* unreachable */
+  return 0;
 }