-1

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.

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
Nick
  • 920
  • 1
  • 7
  • 21

1 Answers1

-1

Nevermind - this was a nasty bug where the assignment $num was referencing a different object but same name parameter expression which was different to the parameter expression in the block. Parameter Expressions with the same name are not necessarily the same parameter expressions!

Nick
  • 920
  • 1
  • 7
  • 21