runtime: S.R.CS.Unsafe: Add unsafe operations for ref returns and locals

Roslyn is adding support for ref returns and locals (https://github.com/dotnet/roslyn/issues/118). S.R.CS.Unsafe should provide operations that allow taking advantage of ref returns and locals in unsafe code.

public static class Unsafe
{
    // Reinterprets the given reference as a reference to a value of type TTo
    public static ref TTo As<TFrom,TTo>(ref TFrom source);

    // Add element offset to the given reference.
    public static ref T Add<T>(ref T source, int elementOffset);

    // Subtract element offset to the given reference.
    public static ref T Subtract<T>(ref T source, int elementOffset);

    // Determines whether the specified references point to the same location.
    public static bool AreSame<T>(ref T a, ref T b);
}

Edit: Updated with the revised proposal

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 9
  • Comments: 52 (50 by maintainers)

Commits related to this issue

Most upvoted comments

Here is the updated proposal with feedback incorporated:

public static class Unsafe
{
    public static ref TTo As<TFrom,TTo>(ref TFrom source);

    public static ref T Add<T>(ref T source, int elementOffset);
    public static ref T Subtract<T>(ref T source, int elementOffset);

    public static bool AreSame<T>(ref T a, ref T b);
}
  • Keeping <TFrom,TTo> order for As as it is preferred by more people. Renaming the generic arguments for clarity.
  • int overloads for Add/Subtract are needed to make this reasonably usable today. IntPtr overloads for Add/Subtract can be added later without any harm if/once native int becomes better supported in C#.
  • Keeping Subtract(ref,int) for convenience, even though it is redundant.
  • Omitting Subtract(ref,ref) for now because of it is not very useful with byref locals and returns anyway, and there are naming and design issues around it.
  • Renaming ReferenceEquals to AreSame. I have checked about a good name with @KrzysztofCwalina and he suggested this name. It is being used for similar concepts in other places and I like it the most out of all the options discussed.

Maybe just add * and / to IntPtr? I think IntPtr should behave like any other integer type as much as possible. I saw proposed C# language changes about that on the Roslyn Github presence.

The pointer difference representation can safely be IntPtr on all platforms. The CLR can just promise to make that work. I see no issues with that.

The null refs cannot be manufactured in C# directly, but they are possible to manufacture by unsafe code. They can be checked for null using Unsafe.AsPointer(ref valueRef) == null.

Maybe we could go back to the earlier suggestion using a fluent API?

Ha ha, no thanks. I think I’ll start hating fluent APIs with a passion. They have their uses but these days they’re more like abuses.

Thank you for a great feedback! I like the suggestions.

IsOnStack - used mostly in asserts

Byrefs on stack are implicitly pinned, so it can be used to assert that it is safe to convert to raw pointer without pinning - example from CoreCLR. Unfortunately, there is no way to implement it in portable way. It would have to be runtime or platform specific that is not pretty given the current shape of library. If it keeps showing up as needed API, it should be looked into as separate issue.

RefSubtract - atomic subtraction between two refs useful to get distance between refs to elements of the same array

I agree that it is useful operation to have. BTW: It is less useful with the current byref locals and returns than one may think because of the single assignment limitations of byref locals. I have noticed in my experiments that one tends to operate on indices and then convert to byref as the last step and never go back - the style of the pointer math is different from unmanaged pointers.

It may be be also nice to have Subtract variant that takes elementOffset for convenience and symmetry with Add.

So the updated proposal is:

public static class Unsafe
{
    public static ref U As<T, U>(ref T source);
    public static ref T Add<T>(ref T source, int elementOffset);
    public static ref T Subtract<T>(ref T source, int elementOffset);
    public static int Subtract<T>(ref T a, ref T b);
    public static bool ReferenceEquals<T>(ref T a, ref T b);
}

More suggestions for refinements are welcomed.

Including @jamesqo @KrzysztofCwalina that I forgot to include yesterday.

As far as I can tell these new API additions are there for convenience only, since they can be expressed via the excisting unsafe API surface.

Nope, they cannot. The equivalents that you show are incorrect because the use of AsPointer introduces an intermediary unmanaged pointer.

public static ref U As<T, U>(ref T source);

We already have a ref returning method - AsRef. It seems to me that it would make sense that the new method is also called AsRef.

public static bool Equals<T>(ref T a, ref T b);

Seems confusing. I can imagine one asking “Does this method compare references or does it compare the referenced values?”.