Explicitly detect static recursion in SkSL

This relaxes our rules to allow calls to declared (but not yet defined)
functions. With that rule change, we have to specifically detect static
recursion and produce an error.

Bug: skia:12137
Change-Id: I39cc281fcd73fb30014bc7b43043552623727e03
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/431537
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/tests/SkRuntimeEffectTest.cpp b/tests/SkRuntimeEffectTest.cpp
index ec784e5..9e2ebda 100644
--- a/tests/SkRuntimeEffectTest.cpp
+++ b/tests/SkRuntimeEffectTest.cpp
@@ -52,7 +52,7 @@
 
 DEF_TEST(SkRuntimeEffectInvalid_UndefinedFunction, r) {
     test_invalid_effect(r, "half4 missing(); half4 main(float2 p) { return missing(); }",
-                           "undefined function");
+                           "function 'half4 missing()' is not defined");
 }
 
 DEF_TEST(SkRuntimeEffectInvalid_UndefinedMain, r) {
diff --git a/tests/SkSLTest.cpp b/tests/SkSLTest.cpp
index 5c32142..9d1565b 100644
--- a/tests/SkSLTest.cpp
+++ b/tests/SkSLTest.cpp
@@ -245,6 +245,7 @@
 SKSL_TEST(SkSLFunctionArgTypeMatch,            "shared/FunctionArgTypeMatch.sksl")
 SKSL_TEST(SkSLFunctionReturnTypeMatch,         "shared/FunctionReturnTypeMatch.sksl")
 SKSL_TEST(SkSLFunctions,                       "shared/Functions.sksl")
+SKSL_TEST(SkSLFunctionPrototype,               "shared/FunctionPrototype.sksl")
 SKSL_TEST(SkSLGeometricIntrinsics,             "shared/GeometricIntrinsics.sksl")
 SKSL_TEST(SkSLHelloWorld,                      "shared/HelloWorld.sksl")
 SKSL_TEST(SkSLHex,                             "shared/Hex.sksl")
@@ -292,14 +293,6 @@
 SKSL_TEST_ES3(SkSLWhileLoopControlFlow,        "shared/WhileLoopControlFlow.sksl")
 
 /*
-// Incompatible with Runtime Effects because calling a function before its definition is disallowed.
-// (This was done to prevent recursion, as required by ES2.)
-// TODO(skia:12137) Enable this test once we specifically detect recursion, rather than just
-// calling functions before definition.
-SKSL_TEST(SkSLFunctionPrototype,               "shared/FunctionPrototype.sksl")
-*/
-
-/*
 TODO(skia:11209): enable these tests when Runtime Effects have support for ES3
 
 SKSL_TEST(SkSLMatrixFoldingES3,                "folding/MatrixFoldingES3.sksl")
diff --git a/tests/sksl/runtime_errors/IllegalRecursion.skvm b/tests/sksl/runtime_errors/IllegalRecursion.skvm
deleted file mode 100644
index 69dae42..0000000
--- a/tests/sksl/runtime_errors/IllegalRecursion.skvm
+++ /dev/null
@@ -1,6 +0,0 @@
-### Compilation failed:
-
-error: 9: call to undefined function 'fibonacci'
-error: 13: call to undefined function 'is_even'
-error: 14: call to undefined function 'is_odd'
-3 errors
diff --git a/tests/sksl/runtime_errors/IllegalRecursionComplex.skvm b/tests/sksl/runtime_errors/IllegalRecursionComplex.skvm
new file mode 100644
index 0000000..ece80d3
--- /dev/null
+++ b/tests/sksl/runtime_errors/IllegalRecursionComplex.skvm
@@ -0,0 +1,8 @@
+### Compilation failed:
+
+error: 6: potential recursion (function call cycle) not allowed:
+	void f_one(int n)
+	void f_two(int n)
+	void f_three(int n)
+	void f_one(int n)
+1 error
diff --git a/tests/sksl/runtime_errors/IllegalRecursionMutual.skvm b/tests/sksl/runtime_errors/IllegalRecursionMutual.skvm
new file mode 100644
index 0000000..84a613b
--- /dev/null
+++ b/tests/sksl/runtime_errors/IllegalRecursionMutual.skvm
@@ -0,0 +1,7 @@
+### Compilation failed:
+
+error: 4: potential recursion (function call cycle) not allowed:
+	bool is_even(int n)
+	bool is_odd(int n)
+	bool is_even(int n)
+1 error
diff --git a/tests/sksl/runtime_errors/IllegalRecursionSimple.skvm b/tests/sksl/runtime_errors/IllegalRecursionSimple.skvm
new file mode 100644
index 0000000..6659d20
--- /dev/null
+++ b/tests/sksl/runtime_errors/IllegalRecursionSimple.skvm
@@ -0,0 +1,6 @@
+### Compilation failed:
+
+error: 4: potential recursion (function call cycle) not allowed:
+	int fibonacci(int n)
+	int fibonacci(int n)
+1 error