My problem here is that I cannot get local variables to blocks in c# expression trees (e.g. System.Linq.Expressions) to work without throwing an exception despite function argument variables working perfectly fine.
By 'local variables' here, I don't mean closures. I mean variables that are specifically local to blocks in c# expression trees. Currently, I have a very bizarre case, where one expression tree works, and another throws the exception:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Core.dll
Additional information: variable 'num' of type 'System.Int32' referenced from scope '', but it is not defined
The working code is compiled from:
public static int Assign ()
{
int num;
int num2;
num = 1;
num2 = 2;
return num + num2;
}
to the expression tree (debug view):
.Lambda #Lambda1<System.Func`1[System.Int32]>() {
.Block(
System.Int32 $num,
System.Int32 $num2) {
0;
0;
$num = 1;
$num2 = 2;
.Return returnLabel { $num + $num2 };
.Label
0
.LabelTarget returnLabel:
}
}
This works normally. However, when my compiler tries to compile this:
public static int Assign ()
{
int num = 1;
int num2 = 2;
return num + num2;
}
as:
.Lambda #Lambda1<System.Func`1[System.Int32]>() {
.Block(
System.Int32 $num,
System.Int32 $num2) {
$num = 1;
$num2 = 2;
.Return returnLabel { $num + $num2 };
.Label
0
.LabelTarget returnLabel:
}
}
This block expression throws the invalid operation exception -
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Core.dll Additional information: variable 'num' of type 'System.Int32' referenced from scope '', but it is not defined
What's really confusing here is that the only difference is caused by the random {0} - System.Linq.Expressions.Expression.Constant(0) expression seemingly making the error go away (the reason these exist is
Specifically, this problem doesn't appear for parameters defined in the lambda expression, for example, the compiler deals with:
public static Crappier<int> FieldAssign(int i, Crap thing)
{
thing.field = i;
return new Crappier<int>(); ;
}
fine outputting an expression tree:
.Lambda #Lambda1<System.Func`3[System.Int32,Sulieman.Crap,Sulieman.Crappier`1[System.Int32]]>(
System.Int32 $i,
Sulieman.Crap $thing) {
.Block() {
$thing.field = $i;
.Return returnLabel { .New Sulieman.Crappier`1[System.Int32]() };
.Label
null
.LabelTarget returnLabel:
}
}
However, I specifically want variables that are bound to one block. The reason for this is that in the code:
for (int i = 0; i < 3; i++) {
Console.WriteLine("hello");
}
for (int i = 0; i < 3; i++) {
Console.WriteLine("world");
}
This is valid c# code since although i is declared twice, it is declared to it's local scope. My compiler tries to account for this by compiling to:
*.Lambda #Lambda1<System.Action>() {
.Block() {
.Block(System.Int32 $i) {
$i = 0;
.Loop .LabelTarget forContinueLabel: {
.If ($i < 3) {
.Block() {
.Block() {
.Call System.Console.WriteLine("hello")
};
$i++
}
} .Else {
.Break forBreakLabel { }
}
}
.LabelTarget forBreakLabel:
};
.Block(System.Int32 $i) {
$i = 0;
.Loop .LabelTarget forContinueLabel: {
.If ($i < 3) {
.Block() {
.Block() {
.Call System.Console.WriteLine("world")
};
$i++
}
} .Else {
.Break forBreakLabel { }
}
}
.LabelTarget forBreakLabel:
};
.Label
3
.LabelTarget returnLabel:
}
}
Any suggestions on how to deal with this? I'm fairly certain I'm handling these local variables in totally the wrong way.