Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InvalidProgramException with System.Linq.Expressions and user-defined conditional operators #103197

Open
MarkMpn opened this issue Jun 8, 2024 · 6 comments

Comments

@MarkMpn
Copy link

MarkMpn commented Jun 8, 2024

Description

Using System.Linq.Expressions to build expression trees dynamically, the repro code worked as expected in .NET Framework but fails in .NET (tested .NET 6 & .NET 8)

So far I've managed to narrow this down to when I'm using a type that implements custom conditional operators (true, false, & and |) and using a TryCatch expression on the right hand side of the conditional logic operator (AndAlso or OrElse). If I reorder the arguments so the TryCatch expression is on the left hand side of the operator it works as expected.

Reproduction Steps

using System.Data.SqlTypes;
using System.Linq.Expressions;

var expr = Expression.OrElse(
    Expression.Constant(SqlBoolean.True),
    Expression.TryCatch(
        Expression.Constant(SqlBoolean.True),
        Expression.Catch(
            typeof(SqlTypeException),
            Expression.Constant(SqlBoolean.False)
        )
    )
);
var lambda = Expression.Lambda<Func<SqlBoolean>>(expr);
var func = lambda.Compile();
Console.WriteLine(func());

Expected behavior

The expression should compile and True should be printed to the console.

Actual behavior

A System.InvalidProgramException is thrown by lambda.Compile()

Regression?

Yes - this same code runs as expected in .NET Framework 4.6.2 and 4.8

Known Workarounds

No response

Configuration

.NET 6 and .NET 8
Windows 11 22631.3593
x64
Doesn't appear to be specific to this.

Other information

No response

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Jun 8, 2024
Copy link
Contributor

Tagging subscribers to this area: @cston
See info in area-owners.md if you want to be subscribed.

@EgorBo
Copy link
Member

EgorBo commented Jun 11, 2024

I presume in C# it would look like condition || try { ... . Checked JIT reports your snippet as an invalid program due to:

Evaluation stack must be empty on entry into a try block

My understanding that it is an expected behavior according to ECMA-335 I.12.4.2.8.1 Fall Through:

Entry to protected blocks can be accomplished by fall-through, at which time the evaluation
stack shall be empty.

so I am not sure why and how it used to work in .NET Framework.

so the TryCatch expression is on the left hand side of the operator it works as expected.

if it's on the left side then the evaluation stack is empty

cc @jkotas

@jkotas
Copy link
Member

jkotas commented Jun 11, 2024

I am not sure why and how it used to work in .NET Framework.

The IL produced by the expression compiler on .NET Framework has an extra spill to make the stack empty. It looks like a regression in System.Linq.Expressions to me.

@jkotas
Copy link
Member

jkotas commented Jun 11, 2024

Note that we merged a ton of optimizations in S.L.Expressions in the early .NET Core days: https://github.com/dotnet/corefx/pulls?q=label%3Aarea-System.Linq.Expressions+is%3Aclosed+optimize . It is likely one of those of changes.

@jkotas
Copy link
Member

jkotas commented Jun 26, 2024

It's the changes in LambdaCompiler.EmitMethodAndAlso and LambdaCompiler.EmitMethodOrElse in this commit: 2ecfbbd

cc @VSadov

@cston cston added this to the 9.0.0 milestone Jul 29, 2024
@cston cston removed the untriaged New issue has not been triaged by the area owner label Jul 29, 2024
@cston cston modified the milestones: 9.0.0, 10.0.0 Sep 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants
@EgorBo @jkotas @cston @MarkMpn and others