diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 72410985..8ee51c68 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1104,10 +1104,15 @@ public static Action (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) => f(c, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); } + // Only the real RuntimeType can be emitted via ldtoken; TypeDelegator/TypeBuilder + // must stay in the closure to preserve identity. + private static readonly Type RuntimeType = typeof(object).GetType(); + /// Helps to identify constants as the one to be put into the Closure public static bool IsClosureBoundConstant(object value, Type type) => value is Delegate || type.IsArray || - !type.IsPrimitive && !type.IsEnum && value is string == false && value is Type == false && value is decimal == false; + !type.IsPrimitive && !type.IsEnum && value is string == false && + (value is Type == false || type != RuntimeType) && value is decimal == false; public enum Result { diff --git a/test/FastExpressionCompiler.TestsRunner.Net472/Program.cs b/test/FastExpressionCompiler.TestsRunner.Net472/Program.cs index 123fb705..eaeefdf4 100644 --- a/test/FastExpressionCompiler.TestsRunner.Net472/Program.cs +++ b/test/FastExpressionCompiler.TestsRunner.Net472/Program.cs @@ -43,6 +43,7 @@ static void RunLightExpressionTests(object state) t.Run(new LightExpression.IssueTests.Issue476_System_ExecutionEngineException_with_nullables_on_repeated_calls_to_ConcurrentDictionary()); t.Run(new LightExpression.IssueTests.Issue498_InvalidProgramException_when_using_loop()); t.Run(new LightExpression.IssueTests.Issue499_InvalidProgramException_for_Sorting_and_comparison_function()); + t.Run(new LightExpression.UnitTests.RuntimeTypeConstantRegressionTests()); t.Run(new LightExpression.UnitTests.ParityTests()); Console.WriteLine($"Just LightExpression tests are passing in {justLightTestsStopwatch.ElapsedMilliseconds} ms."); @@ -64,6 +65,7 @@ static void RunLightExpressionTests(object state) fecTests.Run(new Issue498_InvalidProgramException_when_using_loop()); fecTests.Run(new Issue499_InvalidProgramException_for_Sorting_and_comparison_function()); fecTests.Run(new ReductionProgressRegressionTests()); + fecTests.Run(new RuntimeTypeConstantRegressionTests()); fecTests.Run(new ParityTests()); diff --git a/test/FastExpressionCompiler.TestsRunner/Program.cs b/test/FastExpressionCompiler.TestsRunner/Program.cs index 2a413436..7a867a16 100644 --- a/test/FastExpressionCompiler.TestsRunner/Program.cs +++ b/test/FastExpressionCompiler.TestsRunner/Program.cs @@ -75,6 +75,7 @@ public static void Main(string[] args) lt.Run(new LightExpression.IssueTests.Issue476_System_ExecutionEngineException_with_nullables_on_repeated_calls_to_ConcurrentDictionary()); lt.Run(new LightExpression.IssueTests.Issue499_InvalidProgramException_for_Sorting_and_comparison_function()); lt.Run(new LightExpression.IssueTests.Issue561_NonBooleanEqualityOperator()); + lt.Run(new LightExpression.UnitTests.RuntimeTypeConstantRegressionTests()); lt.Run(new LightExpression.UnitTests.ParityTests()); ltTime = ltStopwatch.ElapsedMilliseconds; }); @@ -83,6 +84,7 @@ public static void Main(string[] args) st.Run(new Issue499_InvalidProgramException_for_Sorting_and_comparison_function()); st.Run(new Issue561_NonBooleanEqualityOperator()); st.Run(new ReductionProgressRegressionTests()); + st.Run(new RuntimeTypeConstantRegressionTests()); st.Run(new ParityTests()); st.Run(new Issue498_InvalidProgramException_when_using_loop()); st.Run(new Issue495_Incomplete_pattern_detection_for_NotSupported_1007_Return_goto_from_TryCatch_with_Assign_generates_invalid_IL()); diff --git a/test/FastExpressionCompiler.UnitTests/RuntimeTypeConstantRegressionTests.cs b/test/FastExpressionCompiler.UnitTests/RuntimeTypeConstantRegressionTests.cs new file mode 100644 index 00000000..59482f1a --- /dev/null +++ b/test/FastExpressionCompiler.UnitTests/RuntimeTypeConstantRegressionTests.cs @@ -0,0 +1,34 @@ +using System; +using System.Reflection; +using System.Reflection.Emit; +#if LIGHT_EXPRESSION +using E = FastExpressionCompiler.LightExpression.Expression; +namespace FastExpressionCompiler.LightExpression.UnitTests; +#else +using E = System.Linq.Expressions.Expression; +namespace FastExpressionCompiler.UnitTests; +#endif +public struct RuntimeTypeConstantRegressionTests : ITestX +{ + public void Run(TestRun t) { Nonruntime_type_constants_preserve_identity(t); Runtime_type_constants_keep_token_semantics(t); } + public void Nonruntime_type_constants_preserve_identity(TestContext t) + { + var module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("FecTypeConstant"), AssemblyBuilderAccess.Run).DefineDynamicModule("M"); + foreach (Type value in new Type[] { new TypeDelegator(typeof(int)), module.DefineType("Unfinished") }) + { + var expression = E.Lambda>(E.Constant(value, typeof(Type))); + var fast = expression.CompileFast(true); + if (t.IsNotNull(fast)) t.AreEqual(true, ReferenceEquals(value, fast())); + var repeated = E.Lambda>(E.ReferenceEqual(E.Constant(value, typeof(Type)), E.Constant(value, typeof(Type)))).CompileFast(true); + if (t.IsNotNull(repeated)) t.AreEqual(true, repeated()); + } + } + public void Runtime_type_constants_keep_token_semantics(TestContext t) + { + foreach (var value in new Type[] { null, typeof(int), typeof(Func<>), typeof(Func<>).GetGenericArguments()[0], typeof(int).MakeByRefType(), typeof(int[,]) }) + { + var fast = E.Lambda>(E.Constant(value, typeof(Type))).CompileFast(true); + if (t.IsNotNull(fast)) t.AreEqual(true, ReferenceEquals(value, fast())); + } + } +}