Skip to content

[API Proposal]: Transparent layout support for value types. #131715

Description

@MichalPetryka

Background and motivation

Wrapper structs with a single field are popular in many codebases, for SIMD, generics and interop purposes. A common issue with them is that such wrapper types either cause less optimal calling conventions in the 1st case, have issues around wrapping managed types in 2nd, or are not guaranteed to match ABI wise for 3rd.

As such, I'd like to propose exposing support for user defineable value types that guarantee to match the ABI properties of the type they wrap.

This would be enforced both in managed and native calls and would potentially also impact how the JIT treats them for promotion and such.

The runtime would throw TypeLoadException if such type contains more than 1 field.

This was previously discussed in #100896 and was left for future discussion AFAIU.

This is already internally implemented in a limited capacity for CLong`CULong\NFloat`, with public API those could be switched to it from special handling.

For prior art on this, Rust provides this with #[repr(transparent)].

cc @tannergooding @jkoritzinsky @jkotas as you were all involved in previous discussions on this.

API Proposal

namespace System.Runtime.InteropServices;

public enum ExtendedLayoutKind
{
+    Transparent
}

API Usage

// non interop
[ExtendedLayout(ExtendedLayoutKind.Transparent)]
public readonly struct CustomVector
{
    public readonly Vector128<float> Vector;
}

public static void Method(CustomVector v)
{
    // passed via SIMD reg, not stack like normal struct.
}

[ExtendedLayout(ExtendedLayoutKind.Transparent)]
public readonly struct EquatableWrapper<T> : IEquatable<EquatableWrapper<T>>
{
    public readonly T Value;

    public EquatableWrapper(T value) => Value = value;

    public bool Equals(EquatableWrapper<T> other) => EqualityComparer<T>.Default.Equals(Value, other.Value);
    // rest of IEquatable
}

// layouts guaranteed to match
Unsafe.BitCast<ReadOnlySpan<object>, ReadOnlySpan<EquatableWrapper<object>>>(a).Contains(new EquatableWrapper<object>(o))

// interop
[ExtendedLayout(ExtendedLayoutKind.Transparent)]
public readonly struct Handle
{
    public readonly void* Value;
}

// guaranteed to be safe
[DllImport("Kernel32")]
public static extern int CloseHandle(Handle handle);

Alternative Designs

For interop purposes this can be solved with custom LibraryImport marshallers but that doesn't help with blittable PInvokes, function pointers and non interop uses.

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status
    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions