Question

Expression trees - invoke lambda during loop leads to variable leaking

I have found little issue with expression trees and would like to know whether such is a bug or a feature. I have this code. The first function assigns one to a variable and returns it.

static class Functions 
{
    static public Expression<Func<int>> GetOne()
    {
        //Returns something like this () => {int x; return x +1;}
        var variable = Expression.Variable(typeof(int));
        var f = Expression.Lambda<Func<int>>(
                            Expression.Block(
                            new[] { variable },
                            Expression.Assign(variable, Expression.Add(variable, Expression.Constant(1)))
        ));
        return f;
    }
    
    static public Expression<Func<int>> ApplyTenTimes()
    {
        var i = Expression.Variable(typeof(int));
        var breakLabel = Expression.Label();
        var f = GetOne();
        var loop = Expression.Lambda<Func<int>>(
        Expression.Block(
            new[] { i },
            Expression.Block(
            new Expression[] {
            Expression.Loop(Expression.Block(
                        Expression.IfThen(Expression.Equal(i, Expression.Constant(10)),          Expression.Break(breakLabel)),
                Expression.PostIncrementAssign(i),
                Expression.Call(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),   Expression.Call( Expression.Invoke(f),typeof(int).GetMethod("ToString", new Type[0]))
                    ))),
            Expression.Label(breakLabel),
            Expression.Invoke(f)
            })));
    
        return loop;
    }
}
f = Functions.GetOne().Compile();
IEnumerable<int> a = Enumerable.Range(0, 9).Select(_ => f()).ToList();
//Prints 1
Console.WriteLine(f());

f = Functions.ApplyTenTimes().Compile();
//Prints 1, ..., 10, 1
Console.WriteLine(f());

I expected the f always prints 1 as 0 is a default value for int.

 3  100  3
1 Jan 1970

Solution

 3

If you try to get an interpreted delegate when calling Compile:

var f = Functions.ApplyTenTimes().Compile(preferInterpretation: true);
Console.WriteLine(f());

Then it behaves as you'd expect - "1" is printed eleven times.

Based on this fact, this is highly likely to be a bug in the expression compiler when compiling a loop expression, since the delegate should have the same behaviour regardless of whether it is interpreted or compiled.

2024-07-08
Sweeper

Solution

 0

The problem here is that you don't initialize local variables. Disassembled code looks like this:

public class baz
{
  public static int go()
  {
    int num1;
    while (num1 != 10)
    {
      ++num1;
      int num2;
      Console.WriteLine(++num2);
    }
    int num3;
    return ++num3;
  }
}

As you can see, num2 is declared inside loop, but not initialized. AFAIK local variables have to be assigned before they can be used and only class fields get their default value. If you try to compile this code you will get errors like this: Error CS0165 : Use of unassigned local variable 'num1'.

You should zero vars first. For example:

Expression.Block(
    new[] { variable },
    Expression.Assign(variable, Expression.Constant(0)),
    Expression.Assign(variable, Expression.Add(variable, Expression.Constant(1)))
)
2024-07-18
Krzysztof