dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.1k stars 4.04k forks source link

Getting address to a managed `ref` field returns the value of the field, not the address #68000

Closed steveharter closed 1 year ago

steveharter commented 1 year ago

Version Used: SDK 8.0.100-preview.3.23178.7 4.6.0-2.23177.13

Steps to Reproduce: sharplab.io

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
internal ref struct Buffer
{
    public ref byte _f1;
    public int _f2;
}

public unsafe class C
{
    public static void M()
    {
        Buffer b;

        // This is null because 'ldfld' is used:
        IntPtr* p1 = (IntPtr*)&b._f1;
        // Also null:  (IntPtr*)Unsafe.AsPointer(ref b.f1;

        // This is not null because 'ldflda' is used:
        IntPtr* p2 = (IntPtr*)&b._f2;
    }
}

creates the relevant IL:

IL_0001: ldloca.s 0
> IL_0003: ldfld uint8& Buffer::_f1
IL_0008: conv.u
IL_0009: stloc.1
IL_000a: ldloca.s 0
IL_000c: ldflda int32 Buffer::_f2

Expected Behavior: The pointer to the managed field _f1 to be non-null.

Actual Behavior: The pointer to the managed field _f1 is null.

RikkiGibson commented 1 year ago

The language doesn't have a way of getting a ref to a ref variable. It makes sense to me that there also isn't a way to get a pointer to a ref variable.

I wonder if there's a pattern involving custom struct layouts and Unsafe.As which would permit getting that reference.

steveharter commented 1 year ago

I wonder if there's a pattern involving custom struct layouts and Unsafe.As which would permit getting that reference.

Yes that is the case for my scenario. I need to get the offset of ref fields in a fixed layout ref struct, and ideally without doing ref arithmetic.

jcouv commented 1 year ago

From discussion with @jarepar, this is expected and it mirrors the semantics with a ref parameter (see example below). If we wanted to get a ref or pointer to the ref variable, we'd need some kind of new syntax (such as &(ref expr)). I'll close this issue as by-design. Feel free to open a csharplang issue for language-level request (some new syntax).

Example (sharplab):

public class C 
{
    public  unsafe void M(int i, ref int j) 
    {
        var x = &i; // ldarga
        fixed (int* y = &j) { } // not ldarga
    }
}