Skip to content

Commit e41b6f4

Browse files
author
Maksim Volkau
committed
fixed: #498
1 parent 781e1fc commit e41b6f4

4 files changed

Lines changed: 114 additions & 1 deletion

File tree

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"Maksim",
3939
"NETCOREAPP",
4040
"NETFRAMEWORK",
41+
"NETSTANDARD",
4142
"Newarr",
4243
"Newobj",
4344
"outparameter",

src/FastExpressionCompiler/FastExpressionCompiler.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2222,7 +2222,7 @@ public static bool TryEmit(Expression expr,
22222222
}
22232223
case ExpressionType.Not:
22242224
{
2225-
if (!exprType.IsPrimitive) // todo: @feat #496
2225+
if (!exprType.IsPrimitive) // todo: @feat #496
22262226
{
22272227
Debug.WriteLine("Unsupported: Nullable<bool> in !x (is invalid C# but valid expression) is not supported yet, see #480: " + expr);
22282228
return false;
@@ -2531,6 +2531,10 @@ private static bool TryEmitLoop(LoopExpression loopExpr, IReadOnlyList<PE> param
25312531
CompilerFlags setup, ParentFlags parent)
25322532
#endif
25332533
{
2534+
// Loop expression itself does not leave a value on stack.
2535+
// If its body produces a value (e.g. a nested typed break/goto path), we need to ignore it before branching back to the loop head, see #498.
2536+
parent |= ParentFlags.IgnoreResult;
2537+
25342538
// Mark the start of the loop body:
25352539
var loopBodyLabel = il.DefineLabel();
25362540
il.DmarkLabel(loopBodyLabel);
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
using System;
2+
using System.Reflection.Emit;
3+
4+
#if LIGHT_EXPRESSION
5+
using FastExpressionCompiler.LightExpression.ImTools;
6+
using static FastExpressionCompiler.LightExpression.Expression;
7+
namespace FastExpressionCompiler.LightExpression.IssueTests;
8+
#else
9+
using System.Linq.Expressions;
10+
using static System.Linq.Expressions.Expression;
11+
namespace FastExpressionCompiler.IssueTests;
12+
#endif
13+
14+
public struct Issue498_InvalidProgramException_when_using_loop : ITestX
15+
{
16+
public void Run(TestRun t)
17+
{
18+
Original_test(t);
19+
}
20+
21+
public void Original_test(TestContext t)
22+
{
23+
// Creating a label that represents the return value
24+
var result = Variable(typeof(int), "result");
25+
var x = Variable(typeof(int), "x");
26+
27+
var endMain = Label(typeof(int), "endMain");
28+
var endSub1 = Label(typeof(void), "endSub1");
29+
var continue1 = Label(typeof(void), "continue1");
30+
31+
var loopExpression = Loop(
32+
Block(
33+
Loop(
34+
Block(
35+
IfThen(GreaterThanOrEqual(x, Constant(10)),
36+
Break(endMain, result)),
37+
AddAssign(result, Constant(1)),
38+
AddAssign(x, Constant(1))
39+
),
40+
endSub1,
41+
continue1
42+
)
43+
),
44+
endMain
45+
);
46+
47+
var body = Block([result, x], loopExpression);
48+
var expr = Lambda<Func<int>>(body);
49+
expr.PrintCSharp();
50+
51+
var _ = (Func<int>)(() => //int
52+
{
53+
int result = default;
54+
int x = default;
55+
while (true)
56+
{
57+
while (true)
58+
{
59+
continue1:;
60+
if (x >= 10)
61+
{
62+
return result;
63+
}
64+
result += 1;
65+
x += 1;
66+
}
67+
endSub1:;
68+
}
69+
endMain:;
70+
});
71+
72+
var fs = expr.CompileSys();
73+
fs.PrintIL(format: ILDecoder.ILFormat.AssertOpCodes);
74+
var calResult = fs();
75+
t.AreEqual(10, calResult);
76+
77+
// Act: CompileFast should throw NotSupportedExpressionException or return null
78+
var ff = expr.CompileFast(ifFastFailedReturnNull: true);
79+
t.IsNotNull(ff);
80+
ff.PrintIL(format: ILDecoder.ILFormat.AssertOpCodes);
81+
82+
ff.AssertOpCodes(
83+
OpCodes.Ldloc_1, // at IL_0000
84+
OpCodes.Ldc_I4_S, // 10 at IL_0001
85+
OpCodes.Clt, // at IL_0003
86+
OpCodes.Ldc_I4_0, // at IL_0005
87+
OpCodes.Ceq, // at IL_0006
88+
OpCodes.Brfalse, // IL_0019at IL_0008
89+
OpCodes.Ldloc_0, // at IL_0013
90+
OpCodes.Br, // IL_0037 at IL_0014
91+
OpCodes.Ldloc_0, // at IL_0019
92+
OpCodes.Ldc_I4_1, // at IL_0020
93+
OpCodes.Add, // at IL_0021
94+
OpCodes.Stloc_0, // at IL_0022
95+
OpCodes.Ldloc_1, // at IL_0023
96+
OpCodes.Ldc_I4_1, // at IL_0024
97+
OpCodes.Add, // at IL_0025
98+
OpCodes.Stloc_1, // at IL_0026
99+
OpCodes.Br, // IL_0000 at IL_0027
100+
OpCodes.Br, // IL_0000 at IL_0032
101+
OpCodes.Ret // at IL_0037
102+
);
103+
104+
calResult = ff();
105+
t.AreEqual(10, calResult);
106+
}
107+
}

test/FastExpressionCompiler.TestsRunner/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public static void Main()
5151

5252
var st = new TestRun(TestFlags.RethrowException);
5353

54+
st.Run(new Issue498_InvalidProgramException_when_using_loop());
5455
st.Run(new Issue495_Incomplete_pattern_detection_for_NotSupported_1007_Return_goto_from_TryCatch_with_Assign_generates_invalid_IL());
5556
st.Run(new Issue480_CLR_detected_an_invalid_program_exception());
5657
#if NET8_0_OR_GREATER

0 commit comments

Comments
 (0)