Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Cannot use fixed local inside lambda expression"

I have an XNA 3.0 project that compiled just fine in VS2008, but that gives compile errors in VS2010 (with XNA 4.0 CTP). The error:

Cannot use fixed local 'depthPtr' inside an anonymous method, lambda expression, or query expression

depthPtr is a fixed float* into an array, that is used inside a Parallel.For lambda expression from System.Threading. As I said, this compiled and ran just fine on VS2008, but it does not on VS2010, even when targeting .NET 3.5.

Has this changed in .NET 4.0, and even so, shouldn't it still compile when I choose .NET 3.5 as the target framework? Searching for the term "Cannot use fixed local" yields exactly one (useless) result, both in Google and Bing.

If this has changed, what is the reason for this? I can imagine capturing a fixed pointer-type in a closure could get a bit weird, is that why? So I'm guessing this is bad practice? And before anyone asks: no, the use of pointers is not absolutely critical here. I would still like to know though :)

EDIT: As requested, a code sample (not from my program, obviously) that reproduces the error:

static unsafe void Main(string[] args)
{
  float[] array = new float[10];

  fixed (float* ptr = array)
  {
    Parallel.For(0, 10, i =>
    {
      ptr[i] = i;
    });
  }
}

The above compiles in VS2008 (well, aside from the reference to Parallel, but any other lambda expression will do), but does not in VS2010.

like image 994
JulianR Avatar asked May 13 '10 00:05

JulianR


4 Answers

fixed pins a pointer for the duration of the block. If you were to store the delegate to invoke later after the block had been exited the garbage collector could move the object between when the lambda is created and when the lambda is invoked. As to why targeting a different framework doesn't help, this is because this is being enforced by the language/compiler, not the runtime (if it were the runtime, it would be reported via an exception or similar at runtime, not by the compiler at compile time).

like image 188
Logan Capaldo Avatar answered Nov 10 '22 15:11

Logan Capaldo


This works. Basically we eliminated the lambda containing an unsafe pointer and replaced it with a delegate to an instance of a class declared inside the fixed block.

    static unsafe void UnsafeTest(string[] args) {
        float[] array = new float[10];

        fixed(float* ptr = array) {
            UnsafeOps ops = new UnsafeOps();
            ops.p = ptr;

            Parallel.For(0, 10, ops.Lambda);
        }
    }

    unsafe class UnsafeOps {
        public float* p;
        public unsafe void Lambda(int value) {
            p[value] = value;
        }
    }

Looks to me like .NET 4 added some half-arsed attempt at disallowing fixed memory access in the compiler. In the above code block you could define UnsafeOps outside the fixed block and access the array after the fixed block. So it's not perfect...

like image 32
Igor Zevaka Avatar answered Nov 10 '22 15:11

Igor Zevaka


The doco say you are not allowed to access unsafe code in an anonymous methods, and the same restrictions apply to lambdas, so I think that may be your problem. Do you have the actual compiler error no?

like image 42
LukeN Avatar answered Nov 10 '22 14:11

LukeN


The compiler is correct to reject that code. Fixed can only be used on local variables, and variables captured by a closure are not local variables, they are hoisted into the class used to maintain state for the closure.

like image 1
Ben Voigt Avatar answered Nov 10 '22 14:11

Ben Voigt