Detect functions that fail to return a value, without using CFG.

This check now runs at function finalization time, before constant
propagation has occurred; this affected the "DeadIfStatement" test.

Our detection isn't smart enough to realize that a loop will run zero
times, so it treats `for` and `while` loops as always running at least
once. This isn't strictly correct, but it actually mirrors how the CFG
implementation works anyway. The only downside is that we would not flag
code like `for (i=0; i<0; ++i) { return x; }` as an error.

Change-Id: I5e43a6ee3a3993045559f0fb0646d36112543a94
Bug: skia:11377
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/379056
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/tests/SkSLDSLTest.cpp b/tests/SkSLDSLTest.cpp
index e3b92f4..77844df 100644
--- a/tests/SkSLDSLTest.cpp
+++ b/tests/SkSLDSLTest.cpp
@@ -1066,8 +1066,7 @@
     }
 
     {
-        ExpectError error(r, "error: expected function to return 'float'\n"
-                             "error: function 'broken' exits without returning a value\n");
+        ExpectError error(r, "error: expected function to return 'float'\n");
         DSLWriter::ProgramElements().clear();
         DSLFunction(kFloat, "broken").define(
             Return()
@@ -1075,6 +1074,16 @@
     }
 
     {
+        ExpectError error(r, "error: function 'broken' can exit without returning a value\n");
+        DSLWriter::ProgramElements().clear();
+        Var x(kFloat, "x");
+        DSLFunction(kFloat, "broken").define(
+            Declare(x, 0),
+            If(x == 1, Return(x))
+        );
+    }
+
+    {
         ExpectError error(r, "error: may not return a value from a void function\n");
         DSLWriter::ProgramElements().clear();
         DSLFunction(kVoid, "broken").define(
@@ -1082,14 +1091,12 @@
         );
     }
 
-/* TODO: detect this case
     {
-        ExpectError error(r, "error: expected function to return 'float'\n");
+        ExpectError error(r, "error: function 'broken' can exit without returning a value\n");
         DSLWriter::ProgramElements().clear();
         DSLFunction(kFloat, "broken").define(
         );
     }
-*/
 }
 
 DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLIf, r, ctxInfo) {