dotnet / dotnet/runtime

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

Open
#131,715 24 comments 9 reactions 0 assignees View on GitHub
api-suggestion area-System.Runtime.InteropServices
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### 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

```diff
namespace System.Runtime.InteropServices;

public enum ExtendedLayoutKind
{
+ Transparent
}
```

### API Usage

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

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

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

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

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

// layouts guaranteed to match
Unsafe.BitCast, ReadOnlySpan>>(a).Contains(new EquatableWrapper(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_

Contributor guide

Open the contributing guide

Research direction

Start with the prior discussion in #100896 and the existing limited handling for CLong, CULong, and NFloat. The issue names no files or tests; the work would need to clarify Transparent layout behavior for managed and native calls, JIT treatment, and the one-field TypeLoadException rule.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend-api-design, compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.