From fe8787ce9bef710da0e5461d63a3fdb92d71d833 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Oct 2026 20:54:43 +0000 Subject: [PATCH 1/2] Initial plan From 07f0b0d1f7bb78cb19f8c483c8c83cab29af0f56 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Oct 2026 21:00:42 +0000 Subject: [PATCH 2/2] Support System lambda invocation in LightExpression Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com> --- .../Expression.cs | 2 ++ .../FastExpressionCompiler.cs | 24 +++++++++++-- .../NestedLambdaTests.cs | 34 ++++++++++++++++++- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/Expression.cs b/src/FastExpressionCompiler.LightExpression/Expression.cs index e5fb43b6e..e3a26d50b 100644 --- a/src/FastExpressionCompiler.LightExpression/Expression.cs +++ b/src/FastExpressionCompiler.LightExpression/Expression.cs @@ -1353,6 +1353,8 @@ expression is LambdaExpression lambda ? lambda.ReturnType : expression is UnaryExpression unExpr && unExpr.NodeType == ExpressionType.Quote && unExpr.Operand is LambdaExpression quotedLambda ? quotedLambda.ReturnType + : expression is ConstantExpression constantExpr && constantExpr.Value is System.Linq.Expressions.LambdaExpression sysLambda + ? sysLambda.ReturnType : expression.Type.FindDelegateInvokeMethod().ReturnType; [RequiresUnreferencedCode(Trimming.Message)] diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index ef735f597..68d32802c 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1343,7 +1343,7 @@ public static Result TryCollectInfo(ref CompilerContext context, Expression expr var invokeExpr = (InvocationExpression)expr; var invokeArgs = invokeExpr.GetArgExprs(); var invokeArgCount = invokeArgs.GetArgCount(); - var invokedExpr = invokeExpr.Expression; + var invokedExpr = GetInvokedExpression(invokeExpr, ref context); if ((context.CompilerFlags & CompilerFlags.NoInvocationLambdaInlining) == 0 && invokedExpr is LambdaExpression lambdaExpr) { var oldIndex = context.CurrentInlinedLambdaInvokeIndex; @@ -1617,6 +1617,26 @@ private static Expression CreateInlinedLambdaInvocationExpression(ArgExprs invok return inlinedBlock; } + private static Expression GetInvokedExpression(InvocationExpression invokeExpr, ref CompilerContext context) + { + var invokedExpr = invokeExpr.Expression; +#if LIGHT_EXPRESSION + if (invokedExpr is ConstantExpression constantExpr && + constantExpr.Value is System.Linq.Expressions.LambdaExpression sysLambda) + { + if ((context.CompilerFlags & CompilerFlags.NoInvocationLambdaInlining) != 0) + { + ref var cachedLambda = ref context.InlinedLambdaInvocation.Map.AddOrGetValueRef(invokeExpr, out var found); + if (!found) + cachedLambda = sysLambda.ToLightExpression(); + return cachedLambda; + } + return sysLambda.ToLightExpression(); + } +#endif + return invokedExpr; + } + private static bool PropagateNonPassedParamsToOuterLambda(ref CompilerContext context, NestedLambdaInfo lambda, ParamExprs nestedLambdaParamExprs, ref SmallList nestedNonPassedParams) { @@ -5375,7 +5395,7 @@ private static bool TryEmitInvoke(InvocationExpression expr, ILGenerator il, ref var argExprs = expr.GetArgExprs(); var argCount = argExprs.GetArgCount(); - var invokedExpr = expr.Expression; + var invokedExpr = GetInvokedExpression(expr, ref context); if ((context.CompilerFlags & CompilerFlags.NoInvocationLambdaInlining) == 0 && invokedExpr is LambdaExpression lambdaExpr) { parent |= ParentFlags.InlinedLambdaInvoke; diff --git a/test/FastExpressionCompiler.UnitTests/NestedLambdaTests.cs b/test/FastExpressionCompiler.UnitTests/NestedLambdaTests.cs index 8b452c4dc..e748bea71 100644 --- a/test/FastExpressionCompiler.UnitTests/NestedLambdaTests.cs +++ b/test/FastExpressionCompiler.UnitTests/NestedLambdaTests.cs @@ -25,6 +25,9 @@ public int Run() Given_composed_expr_with_closure_over_2_parameters_used_in_2_levels_of_nested_lambda(); Given_composed_expr_with_closure_over_2_same_parameters_used_in_2_levels_of_nested_lambda(); Two_same_nested_lambdas_should_compile_once(); +#if LIGHT_EXPRESSION + Invoke_constant_System_lambda(); +#endif Nested_Hoisted_Func_using_outer_parameter(); Nested_Hoisted_Func_using_outer_parameter_and_closed_value(); @@ -32,7 +35,7 @@ public int Run() Nested_Hoisted_lambda_using_outer_parameter_and_closed_value_deeply_nested_lambda(); Given_hoisted_expr_with_closure_over_parameters_in_nested_lambda_should_work(); - return 17; + return 18; } @@ -388,6 +391,35 @@ public void Two_same_nested_lambdas_should_compile_once() Asserts.AreEqual(50, f()); } +#if LIGHT_EXPRESSION + public void Invoke_constant_System_lambda() + { + System.Linq.Expressions.LambdaExpression lambda = + System.Linq.Expressions.Expression.Lambda>( + System.Linq.Expressions.Expression.Constant(42)); + var zeroArgExpr = Lambda>(Invoke(Constant(lambda))); + + var zeroArgFunc = zeroArgExpr.CompileFast(true); + Asserts.IsNotNull(zeroArgFunc); + Asserts.AreEqual(42, zeroArgFunc()); + + var value = System.Linq.Expressions.Expression.Parameter(typeof(int), "value"); + lambda = System.Linq.Expressions.Expression.Lambda>( + System.Linq.Expressions.Expression.Add(value, System.Linq.Expressions.Expression.Constant(2)), + value); + var arg = Parameter(typeof(int), "arg"); + var oneArgExpr = Lambda>(Invoke(Constant(lambda), arg), arg); + + var oneArgFunc = oneArgExpr.CompileFast(true); + Asserts.IsNotNull(oneArgFunc); + Asserts.AreEqual(42, oneArgFunc(40)); + + oneArgFunc = oneArgExpr.CompileFast(true, CompilerFlags.NoInvocationLambdaInlining); + Asserts.IsNotNull(oneArgFunc); + Asserts.AreEqual(42, oneArgFunc(40)); + } +#endif + public void Hmm_I_can_use_the_same_parameter_for_outer_and_nested_lambda() {