diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index a35bd364..ea4c5253 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -6100,8 +6100,7 @@ private static bool TryEmitSwitch(SwitchExpression expr, ILGenerator il, ref Com var testValExpr = testValues[v]; if (testValExpr is not ConstantExpression && testValExpr is not DefaultExpression) { - Debug.Assert(false, $"Not supported non-constant,non-default switch case value expression: `{testValExpr}`"); - return false; + goto OrderedDispatch; } var testValueLong = testValExpr is ConstantExpression constExpr ? ConvertValueObjectToLong(constExpr.Value) : 0L; @@ -6351,6 +6350,7 @@ private static bool TryEmitSwitch(SwitchExpression expr, ILGenerator il, ref Com } } + OrderedDispatch: var switchValueIsNullable = switchValueType.IsNullable(); Type switchNullableUnderlyingValueType = null; MethodInfo switchNullableHasValueMethod = null; @@ -8690,7 +8690,8 @@ public static bool TryFindSwitchBranch(SwitchExpression switchExpr, CompilerFlag var testValues = cases[i].TestValues; for (var j = 0; j < testValues.Count; j++) { - if (testValues[j] is not ConstantExpression testConst || testConst.Value == null) continue; + if (testValues[j] is not ConstantExpression testConst) return false; + if (testConst.Value == null) continue; if (switchValLong == ConvertValueObjectToLong(testConst.Value)) { matchedBody = cases[i].Body; return true; } } } diff --git a/test/FastExpressionCompiler.TestsRunner.Net472/Program.cs b/test/FastExpressionCompiler.TestsRunner.Net472/Program.cs index 1a569866..2cdb5ead 100644 --- a/test/FastExpressionCompiler.TestsRunner.Net472/Program.cs +++ b/test/FastExpressionCompiler.TestsRunner.Net472/Program.cs @@ -34,6 +34,7 @@ static void RunLightExpressionTests(object state) var justLightTestsStopwatch = Stopwatch.StartNew(); var t = (LightExpression.TestRun)state; + t.Run(new LightExpression.UnitTests.RuntimeSwitchCaseRegressionTests()); t.Run(new LightExpression.UnitTests.StaticFieldAddressRegressionTests()); t.Run(new LightExpression.UnitTests.MultidimensionalGetStorageRegressionTests()); t.Run(new LightExpression.UnitTests.AbstractConstructorRegressionTests()); @@ -78,6 +79,7 @@ static void RunLightExpressionTests(object state) var fecTestsStopwatch = Stopwatch.StartNew(); var fecTests = new TestRun(); + fecTests.Run(new RuntimeSwitchCaseRegressionTests()); fecTests.Run(new StaticFieldAddressRegressionTests()); fecTests.Run(new MultidimensionalGetStorageRegressionTests()); fecTests.Run(new AbstractConstructorRegressionTests()); diff --git a/test/FastExpressionCompiler.TestsRunner/Program.cs b/test/FastExpressionCompiler.TestsRunner/Program.cs index 1b15c8e2..b5d7a189 100644 --- a/test/FastExpressionCompiler.TestsRunner/Program.cs +++ b/test/FastExpressionCompiler.TestsRunner/Program.cs @@ -63,6 +63,7 @@ public static void Main(string[] args) var ltTask = Task.Run(() => { var ltStopwatch = Stopwatch.StartNew(); + lt.Run(new LightExpression.UnitTests.RuntimeSwitchCaseRegressionTests()); lt.Run(new LightExpression.UnitTests.StaticFieldAddressRegressionTests()); lt.Run(new LightExpression.UnitTests.MultidimensionalGetStorageRegressionTests()); lt.Run(new LightExpression.UnitTests.AbstractConstructorRegressionTests()); @@ -104,6 +105,7 @@ public static void Main(string[] args) }); var stStopwatch = Stopwatch.StartNew(); + st.Run(new RuntimeSwitchCaseRegressionTests()); st.Run(new StaticFieldAddressRegressionTests()); st.Run(new MultidimensionalGetStorageRegressionTests()); st.Run(new AbstractConstructorRegressionTests()); diff --git a/test/FastExpressionCompiler.UnitTests/RuntimeSwitchCaseRegressionTests.cs b/test/FastExpressionCompiler.UnitTests/RuntimeSwitchCaseRegressionTests.cs new file mode 100644 index 00000000..a8b3523a --- /dev/null +++ b/test/FastExpressionCompiler.UnitTests/RuntimeSwitchCaseRegressionTests.cs @@ -0,0 +1,32 @@ +using System; +using S = System.Linq.Expressions.Expression; +#if LIGHT_EXPRESSION +namespace FastExpressionCompiler.LightExpression.UnitTests; +#else +namespace FastExpressionCompiler.UnitTests; +#endif + +public struct RuntimeSwitchCaseRegressionTests : ITestX +{ + public void Run(TestRun t) { Numeric_runtime_case_values_are_supported(t); Constant_input_does_not_skip_runtime_tests(t); } + public void Numeric_runtime_case_values_are_supported(TestContext t) + { + var value = S.Parameter(typeof(int)); var test = S.Parameter(typeof(int)); + var tree = S.Lambda>(S.Switch(value, S.Constant(-1), S.SwitchCase(S.Constant(7), S.Add(test, S.Constant(1)))), value, test); + var system = tree.Compile(); var fast = tree.FromSysExpression().CompileFast(true); + if (!t.IsNotNull(fast)) return; + foreach (var x in new[] { int.MinValue, -1, 0, 42, int.MaxValue }) + foreach (var y in new[] { -1, 0, 41, int.MaxValue }) t.AreEqual(system(x, y), fast(x, y)); + } + public static int Calls; + public static int Test() { ++Calls; return 42; } + public void Constant_input_does_not_skip_runtime_tests(TestContext t) + { + var tree = S.Lambda>(S.Switch(S.Constant(42), S.Constant(-1), + S.SwitchCase(S.Constant(7), S.Call(typeof(RuntimeSwitchCaseRegressionTests).GetMethod(nameof(Test)))), + S.SwitchCase(S.Constant(9), S.Constant(42)))); + var fast = tree.FromSysExpression().CompileFast(true); + if (!t.IsNotNull(fast)) return; + Calls = 0; t.AreEqual(7, fast()); t.AreEqual(1, Calls); + } +}