Description
Background
Recent efforts have improved the predictability of layout in managed memory (for example, non-blittable bool
/ char
fields no longer break sequential layout on the managed side). This enables high-performance serialization and interop without runtime marshalling or explicit layout attributes.
Motivation
I'm building an API that that takes blittable types:
void Blit(ReadOnlySpan<T> items) { ... }
"Blittable" in this case means unmanaged
or nullable unmanaged
. Yes, Nullable<T> where T : unmanaged
is in fact blittable, let's move on. :)
Because Nullable<T>
doesn't satisfy the struct
/ unmanaged
constraints, you end up defining multiple overloads:
void Blit<T>(in T value) where T : unmanaged { ... }
void Blit<T>(in T? value) where T : unmanaged { ... }
This becomes exponentially worse the more blittable parameters there are:
void Blit<T1, T2>(in T1 a, in T2 b) where T1 : unmanaged where T2 : unmanaged { ... }
void Blit<T1, T2>(in T1 a, in T2? b) where T1 : unmanaged where T2 : unmanaged { ... }
void Blit<T1, T2>(in T1? a, in T2 b) where T1 : unmanaged where T2 : unmanaged { ... }
void Blit<T1, T2>(in T1? a, in T2? b) where T1 : unmanaged where T2 : unmanaged { ... }
So I decided to leave T
unconstrained and instead throw an exception when the RuntimeHelpers.IsReferenceOrContainsReferences<T>()
intrinsic returns false. This approach detects issues at runtime with zero overhead, but I'd like to add an analyzer error in case of misuse.
void Blit<[Blittable] T>(in T value) { ... }
...
Blit("oops"); // Error: Type 'string' is not Blittable
Proposed API
A couple of additions to the Roslyn API would help:
-
ITypeSymbol.IsReferenceOrContainsReferences
: Equivalent toRuntimeHelpers.IsReferenceOrContainsReferences
. Can already be done by checking all fields in a struct recursively, but should be faster through a dedicated API. -
INamedTypeSymbol.LayoutKind
: So the analyzer can warn against auto layout, which is currently tricky to infer (e.g. can't assume sequential layout for a struct lackingStructLayoutAttribute
because it might be a forwarded type likeValueTuple
and it has aTypeForwardedFromAttribute
instead of the original attributes).
namespace Microsoft.CodeAnalysis
{
public interface INamedTypeSymbol : ITypeSymbol
{
+ System.Runtime.InteropServices.LayoutKind LayoutKind { get; }
}
}
Risks
Can't see any.