Skip to content

Commit 0cbe069

Browse files
author
Maksim Volkau
committed
fixed: #495
1 parent 414210e commit 0cbe069

File tree

11 files changed

+152
-27614
lines changed

11 files changed

+152
-27614
lines changed

DebugOutputs/printcs_20260312-01.out

Lines changed: 0 additions & 9552 deletions
This file was deleted.

printcs_20260317-01.out

Lines changed: 0 additions & 8962 deletions
This file was deleted.

printcs_20260317-02.out

Lines changed: 0 additions & 9058 deletions
This file was deleted.

printcs_20260319-02.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.TestsRunner\FastExpressionCompiler.TestsRunner.csproj : warning NU1900: Error occurred while getting package vulnerability data: Unable to load the service index for source https://api.nuget.org/v3/index.json.
2+
C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.IssueTests\FastExpressionCompiler.IssueTests.csproj : warning NU1900: Error occurred while getting package vulnerability data: Unable to load the service index for source https://api.nuget.org/v3/index.json. [C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.TestsRunner\FastExpressionCompiler.TestsRunner.csproj]
3+
C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.LightExpression.IssueTests\FastExpressionCompiler.LightExpression.IssueTests.csproj : warning NU1900: Error occurred while getting package vulnerability data: Unable to load the service index for source https://api.nuget.org/v3/index.json. [C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.TestsRunner\FastExpressionCompiler.TestsRunner.csproj]
4+
C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.UnitTests\FastExpressionCompiler.UnitTests.csproj : warning NU1900: Error occurred while getting package vulnerability data: Unable to load the service index for source https://api.nuget.org/v3/index.json. [C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.TestsRunner\FastExpressionCompiler.TestsRunner.csproj]
5+
C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.LightExpression.UnitTests\FastExpressionCompiler.LightExpression.UnitTests.csproj : warning NU1900: Error occurred while getting package vulnerability data: Unable to load the service index for source https://api.nuget.org/v3/index.json. [C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.TestsRunner\FastExpressionCompiler.TestsRunner.csproj]
6+
C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.TestsRunner\FastExpressionCompiler.TestsRunner.csproj : warning NU1900: Error occurred while getting package vulnerability data: Unable to load the service index for source https://api.nuget.org/v3/index.json. [TargetFramework=net9.0]
7+
C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.LightExpression.IssueTests\FastExpressionCompiler.LightExpression.IssueTests.csproj : warning NU1900: Error occurred while getting package vulnerability data: Unable to load the service index for source https://api.nuget.org/v3/index.json. [TargetFramework=net9.0]
8+
C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.LightExpression.UnitTests\FastExpressionCompiler.LightExpression.UnitTests.csproj : warning NU1900: Error occurred while getting package vulnerability data: Unable to load the service index for source https://api.nuget.org/v3/index.json. [TargetFramework=net9.0]
9+
C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.IssueTests\FastExpressionCompiler.IssueTests.csproj : warning NU1900: Error occurred while getting package vulnerability data: Unable to load the service index for source https://api.nuget.org/v3/index.json. [TargetFramework=net9.0]
10+
C:\dev\FastExpressionCompiler\test\FastExpressionCompiler.UnitTests\FastExpressionCompiler.UnitTests.csproj : warning NU1900: Error occurred while getting package vulnerability data: Unable to load the service index for source https://api.nuget.org/v3/index.json. [TargetFramework=net9.0]
11+
CSC : error CS0016: Could not write to output file 'C:\dev\FastExpressionCompiler\src\FastExpressionCompiler\obj\Debug\net9.0\FastExpressionCompiler.sourcelink.json' -- 'Could not find file 'C:\dev\FastExpressionCompiler\src\FastExpressionCompiler\obj\Debug\net9.0\FastExpressionCompiler.sourcelink.json'.' [C:\dev\FastExpressionCompiler\src\FastExpressionCompiler\FastExpressionCompiler.csproj::TargetFramework=net9.0]
12+
CSC : error CS0016: Could not write to output file 'C:\dev\FastExpressionCompiler\src\FastExpressionCompiler.LightExpression\obj\Debug\net9.0\FastExpressionCompiler.LightExpression.sourcelink.json' -- 'Could not find file 'C:\dev\FastExpressionCompiler\src\FastExpressionCompiler.LightExpression\obj\Debug\net9.0\FastExpressionCompiler.LightExpression.sourcelink.json'.' [C:\dev\FastExpressionCompiler\src\FastExpressionCompiler.LightExpression\FastExpressionCompiler.LightExpression.csproj::TargetFramework=net9.0]

src/FastExpressionCompiler/FastExpressionCompiler.cs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,7 +2347,7 @@ public static bool TryEmit(Expression expr,
23472347
{
23482348
if ((setup & CompilerFlags.ThrowOnNotSupportedExpression) != 0)
23492349
throw new NotSupportedExpressionException(Result.NotSupported_Try_GotoReturnToTheFollowupLabel);
2350-
return false; // todo: @feature return from the TryCatch with the internal label is not supported, though it is the unlikely case
2350+
return false; // todo: @feature return from the TryCatch with the internal label is not supported, though it is an unlikely case
23512351
}
23522352

23532353
// we are generating the return value and ensuring here that it is not popped-out
@@ -2618,7 +2618,9 @@ private static bool TryEmitLabel(LabelExpression expr, IReadOnlyList<PE> paramEx
26182618
var returnVariableIndexPlusOne = labelInfo.ReturnVariableIndexPlusOneAndIsDefined >>> 1;
26192619
if (returnVariableIndexPlusOne != 0)
26202620
{
2621-
if (defaultValue != null)
2621+
// if the result value was ignored then we did not IL generate anything for the default value,
2622+
// and that in order means we do not have valid thing on stack to store here - so skip the store
2623+
if (defaultValue != null & ((parent & ParentFlags.IgnoreResult) == 0))
26222624
EmitStoreLocalVariable(il, returnVariableIndexPlusOne - 1);
26232625

26242626
il.DmarkLabel(labelInfo.ReturnLabel);
@@ -4203,7 +4205,7 @@ private static bool TryEmitArithmeticAndOrAssign(
42034205
return TryEmitAssignToParameterOrVariable((ParameterExpression)left, right,
42044206
nodeType, isPost, exprType, paramExprs, il, ref closure, setup, parent, resultVar);
42054207

4206-
// todo: @wip split for now between the Increment/Decrement and the rest
4208+
// todo: @better split for now between the Increment/Decrement and the rest
42074209
var p = (ParameterExpression)left;
42084210
#if LIGHT_EXPRESSION
42094211
var paramExprCount = paramExprs.ParameterCount;
@@ -4398,7 +4400,7 @@ private static bool TryEmitArithmeticAndOrAssign(
43984400

43994401
// required for calling the method on the value type parameter
44004402
var objType = objExpr.Type;
4401-
objVarByAddress = !closure.LastEmitIsAddress && objType.IsValueType && // todo: @wip avoid ad-hocking with parameter here
4403+
objVarByAddress = !closure.LastEmitIsAddress && objType.IsValueType && // todo: @better avoid ad-hocking with parameter here
44024404
(objExpr.NodeType != ExpressionType.Parameter || !((ParameterExpression)objExpr).IsByRef);
44034405
if (objVarByAddress)
44044406
objVar = EmitStoreAndLoadLocalVariableAddress(il, objType);
@@ -10056,17 +10058,19 @@ internal static StringBuilder ToCSharpString(this Expression e, StringBuilder sb
1005610058
case ExpressionType.Constant:
1005710059
{
1005810060
var x = (ConstantExpression)e;
10059-
if (x.Value == null)
10061+
var val = x.Value;
10062+
if (val == null)
1006010063
return x.Type == null ? sb.Append("null") : NullConstantOrDefaultToCSharpString(x.Type, sb, enclosedIn, stripNamespace, printType);
1006110064

10062-
if (x.Value is Type t)
10065+
if (val is Type t)
1006310066
return sb.AppendTypeOf(t, stripNamespace, printType);
1006410067

10065-
if (x.Value.GetType() != x.Type) // add the Type cast
10068+
var actualType = val.GetType();
10069+
if (actualType != x.Type && (actualType.IsValueType || x.Type != typeof(object))) // add the Type cast, but avoid cast to object for the reference types
1006610070
sb.Append('(').Append(x.Type.ToCode(stripNamespace, printType)).Append(')');
1006710071

1006810072
// value output may also add the cast for the primitive values
10069-
return sb.Append(x.Value.ToCode(notRecognizedToCode ?? CodePrinter.DefaultNotRecognizedToCode, stripNamespace, printType));
10073+
return sb.Append(val.ToCode(notRecognizedToCode ?? CodePrinter.DefaultNotRecognizedToCode, stripNamespace, printType));
1007010074
}
1007110075
case ExpressionType.Parameter:
1007210076
{
@@ -10513,7 +10517,10 @@ void PrintPart(Expression part, ref PrintContext ctx)
1051310517

1051410518
var hVar = h.Variable;
1051510519
if (hVar != null)
10520+
{
10521+
// todo: @wip @feat add around the ex var the "#pragma warning disable CS0168 // unused var" "#pragma warning restore CS0168", see #495, or better check inside the catch block for the usage. We need to completely remove the var, because prefix '_' does not remove the warning.
1051610522
sb.Append(' ').AppendName(hVar, hVar.Name, hVar.Type.ToCode(stripNamespace, printType), ref ctx);
10523+
}
1051710524

1051810525
sb.Append(')');
1051910526
if (h.Filter != null)
@@ -11757,7 +11764,7 @@ public static string ToArrayInitializerCode(this IEnumerable items, Type itemTyp
1175711764
/// otherwise uses passed <paramref name="notRecognizedToCode"/> or falls back to `ToString()`.
1175811765
/// </summary>
1175911766
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Trimming.Message)]
11760-
public static string ToCode(this object x,
11767+
public static string ToCode<T>(this T x,
1176111768
ObjectToCode notRecognizedToCode = null, bool stripNamespace = false, Func<Type, string, string> printType = null)
1176211769
{
1176311770
if (x == null)

src/FastExpressionCompiler/ILReader.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ namespace FastExpressionCompiler.ILDecoder;
3131
[Flags]
3232
public enum ILFormat : byte
3333
{
34-
Default,
35-
AssertOpCodes,
34+
Default = 0,
35+
AssertOpCodes = 1,
3636
/// <summary>Exclude NOP opcodes from the output</summary>
37-
SkipNop
37+
SkipNop = 1 << 1
3838
}
3939

4040
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Uses reflection on internal types and is not trim-compatible.")]

src/FastExpressionCompiler/TestTools.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ public static class TestTools
3838
static TestTools()
3939
{
4040
#if DEBUG
41-
// AllowPrintIL = true;
41+
AllowPrintIL = true;
4242
AllowPrintCS = true;
43-
// AllowPrintExpression = true;
43+
AllowPrintExpression = true;
4444
#endif
4545
}
4646

test/FastExpressionCompiler.IssueTests/Issue495_Incomplete_pattern_detection_for_NotSupported_1007_Return_goto_from_TryCatch_with_Assign_generates_invalid_IL.cs

Lines changed: 93 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public struct Issue495_Incomplete_pattern_detection_for_NotSupported_1007_Return
1515
public void Run(TestRun t)
1616
{
1717
ReturnGotoFromTryCatchWithAssign_ShouldBeDetectedAsError1007(t);
18+
ReturnGotoFromTryCatchWithAssign_ShouldBeDetectedAsError1007_null_path(t);
1819
}
1920

2021
public void ReturnGotoFromTryCatchWithAssign_ShouldBeDetectedAsError1007(TestContext t)
@@ -35,10 +36,10 @@ public void ReturnGotoFromTryCatchWithAssign_ShouldBeDetectedAsError1007(TestCon
3536
NotEqual(variable, Constant(null, typeof(object))),
3637
// FEC should detect this as error 1007 and reject it
3738
Return(returnLabel, Assign(finalResult, variable), typeof(object))
38-
// @wip other patters:
39-
// - Return(label, Block(Assign(var, value), value))
40-
// - Return(label, Call(MethodThatAssigns, ref var, value))
41-
// - Return(label, Coalesce(value, Assign(var, default)))
39+
// @wip other patters:
40+
// - Return(label, Block(Assign(var, value), value))
41+
// - Return(label, Call(MethodThatAssigns, ref var, value))
42+
// - Return(label, Coalesce(value, Assign(var, default)))
4243
),
4344
Assign(finalResult, Constant("default", typeof(object))),
4445
Label(returnLabel, Constant("fallback", typeof(object)))
@@ -51,36 +52,109 @@ public void ReturnGotoFromTryCatchWithAssign_ShouldBeDetectedAsError1007(TestCon
5152
var expr = Lambda<Func<object>>(block);
5253

5354
expr.PrintCSharp();
54-
var @cs = (Func<object>)(() => //object
55+
var _ = (Func<object>)(() => //object
5556
{
56-
object var = null;
57+
object @var = null;
5758
object finalResult = null;
5859
try
5960
{
60-
var = (object)"hello";
61-
if (var != null)
61+
@var = "hello";
62+
if (@var != null)
6263
{
63-
return finalResult = var;
64-
}; // todo: @wip remove ;
65-
finalResult = (object)"default";
66-
// return:; // todo: @wip remove or comment or rename but make it a valid c#
64+
return finalResult = @var;
65+
}
66+
finalResult = "default";
67+
@return:;
6768
}
68-
catch (Exception)//(Exception ex) // no need for ex
69+
catch (Exception
70+
#pragma warning disable CS0168 // unused var
71+
ex
72+
#pragma warning restore CS0168
73+
)
6974
{
70-
; // todo: @wip remove ;
75+
;
7176
}
7277
return finalResult;
7378
});
7479

7580
var fs = expr.CompileSys();
7681
fs.PrintIL(format: ILDecoder.ILFormat.AssertOpCodes);
77-
fs();
82+
var a = fs();
83+
t.AreEqual("hello", a);
7884

7985
// Act: CompileFast should throw NotSupportedExpressionException or return null
80-
// var ff = expr.CompileFast(ifFastFailedReturnNull: true);
81-
// ff.PrintIL(format: ILDecoder.ILFormat.AssertOpCodes);
86+
var ff = expr.CompileFast(ifFastFailedReturnNull: true);
87+
t.IsNotNull(ff);
88+
ff.PrintIL(format: ILDecoder.ILFormat.AssertOpCodes);
89+
var b = ff();
90+
t.AreEqual("hello", b);
91+
}
92+
93+
public void ReturnGotoFromTryCatchWithAssign_ShouldBeDetectedAsError1007_null_path(TestContext t)
94+
{
95+
// Arrange: Build expression with Return(label, Assign(...)) inside TryCatch
96+
var variable = Variable(typeof(object), "var");
97+
var finalResult = Variable(typeof(object), "finalResult");
98+
var returnLabel = Label(typeof(object), "return");
99+
var exceptionParam = Parameter(typeof(Exception), "ex");
100+
101+
var block = Block(
102+
new[] { variable, finalResult },
103+
TryCatch(
104+
Block(
105+
typeof(void),
106+
Assign(variable, Constant(null, typeof(object))),
107+
IfThen(
108+
NotEqual(variable, Constant(null, typeof(object))),
109+
// FEC should detect this as error 1007 and reject it
110+
Return(returnLabel, Assign(finalResult, variable), typeof(object))
111+
// @wip other patters:
112+
// - Return(label, Block(Assign(var, value), value))
113+
// - Return(label, Call(MethodThatAssigns, ref var, value))
114+
// - Return(label, Coalesce(value, Assign(var, default)))
115+
),
116+
Assign(finalResult, Constant("default", typeof(object))),
117+
Label(returnLabel, Constant("fallback", typeof(object)))
118+
),
119+
Catch(exceptionParam, Empty())
120+
),
121+
finalResult
122+
);
123+
124+
var expr = Lambda<Func<object>>(block);
125+
126+
expr.PrintCSharp();
127+
var _ = (Func<object>)(() => //object
128+
{
129+
object @var = null;
130+
object finalResult = null;
131+
try
132+
{
133+
@var = null;
134+
if (@var != null)
135+
{
136+
return finalResult = @var;
137+
}
138+
finalResult = "default";
139+
@return:;
140+
}
141+
catch (Exception)
142+
{
143+
;
144+
}
145+
return finalResult;
146+
});
82147

83-
// // Expected: compiled should be null (pattern detected as unsupported)
84-
// t.IsNull(ff);
148+
var fs = expr.CompileSys();
149+
fs.PrintIL(format: ILDecoder.ILFormat.AssertOpCodes);
150+
var a = fs();
151+
t.AreEqual("default", a);
152+
153+
// Act: CompileFast should throw NotSupportedExpressionException or return null
154+
var ff = expr.CompileFast(ifFastFailedReturnNull: true);
155+
t.IsNotNull(ff);
156+
ff.PrintIL(format: ILDecoder.ILFormat.AssertOpCodes);
157+
var b = ff();
158+
t.AreEqual("default", b);
85159
}
86160
}

test/FastExpressionCompiler.TestsRunner/Program.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ public static void Main()
1818
// ILGeneratorTools.DisableILGeneratorPooling = true;
1919
// LightExpression.ILGeneratorTools.DisableILGeneratorPooling = true;
2020

21+
// new TryCatchTests().Run();
2122
// new Issue320_Bad_label_content_in_ILGenerator_when_creating_through_DynamicModule().Run();
2223
// new Issue422_InvalidProgramException_when_having_TryCatch_Default_in_Catch().Run();
2324
// new Issue428_Expression_Switch_without_a_default_case_incorrectly_calls_first_case_for_unmatched_values().Run();
24-
2525
// new LightExpression.IssueTests.Issue365_Working_with_ref_return_values().Run();
2626
// new LightExpression.IssueTests.Issue321_Call_with_out_parameter_to_field_type_that_is_not_value_type_fails().Run();
2727
// new LightExpression.IssueTests.Issue363_ActionFunc16Generics().Run();
@@ -51,14 +51,13 @@ public static void Main()
5151

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

54+
st.Run(new Issue495_Incomplete_pattern_detection_for_NotSupported_1007_Return_goto_from_TryCatch_with_Assign_generates_invalid_IL());
55+
st.Run(new Issue480_CLR_detected_an_invalid_program_exception());
5456
#if NET8_0_OR_GREATER
5557
st.Run(new Issue487_Fix_ToCSharpString_output_for_boolean_equality_expressions());
5658
st.Run(new Issue475_Reuse_DynamicMethod_if_possible());
5759
#endif
5860

59-
st.Run(new Issue495_Incomplete_pattern_detection_for_NotSupported_1007_Return_goto_from_TryCatch_with_Assign_generates_invalid_IL());
60-
st.Run(new Issue480_CLR_detected_an_invalid_program_exception());
61-
6261
var lt = new LightExpression.TestRun(LightExpression.TestFlags.RethrowException);
6362

6463
#if NET8_0_OR_GREATER

0 commit comments

Comments
 (0)