Skip to content

Commit cdd7566

Browse files
authored
Specially consider CLong, CULong and Guid strictly blittable (#88213)
1 parent 2f20812 commit cdd7566

File tree

4 files changed

+64
-24
lines changed

4 files changed

+64
-24
lines changed

docs/design/libraries/LibraryImportGenerator/Compatibility.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@ Support for `MarshalAs(UnmanagedType.Interface)` is added to the interop source
1414

1515
The `ComInterfaceMarshaller<T>` type has the following general behavior: An unmanaged pointer is marshalled to a managed object through `GetOrCreateObjectForComInstance` on a shared `StrategyBasedComWrappers` instance. A managed object is marshalled to an unmanaged pointer through that same shared instance with the `GetOrCreateComInterfaceForObject` method and then calling `QueryInterface` on the returned `IUnknown*` to get the pointer for the unmanaged interface with the IID from the managed type as defined by our default interface details strategy (or the IID of `IUnknown` if the managed type has no IID).
1616

17+
### Strict Blittability
18+
19+
Strict blittability checks have been slightly relaxed. A few select types are now considered strictly blittable that previously were not:
20+
21+
- `System.Runtime.InteropServices.CLong`
22+
- `System.Runtime.InteropServices.CULong`
23+
- `System.Guid`
24+
25+
The first two types are interop intrinsics that were specifically designed to be used at the unmanaged API layer, so they should be considered blittable by all interop systems. `System.Guid` is extremely commonly used in COM-based APIs, and with the move to supporting COM interop in source-generation, this type shows up in signatures quite a bit. As .NET has always maintained that `Guid` is a blittable representation of `GUID` and it is marked as `NonVersionable` (so we have already committed to maintain the shape between multiple versions of the runtime), we have decided to add it to the list of strictly blittable types.
26+
27+
We strive to keep this list of "exceptions" to our strict blittability rules small, but we will continue to evaluate the list of types in the future as we explore new interop scenarios. We will follow these rules to determine if we will consider encoding the type as strictly blittable in the future:
28+
29+
- The type is defined in the same assembly as `System.Object` (the core assembly) and is marked either as `NonVersionable` or `Intrinsic` in CoreCLR's System.Private.CoreLib.
30+
- The type is an primitive ABI type defined by the interop team.
31+
32+
Types that meet one of these two criterion will have stable shapes and will not accidentally introduce non-blittable fields like `bool` and `char`, so we can consider adding them to the exception list. We do not guarantee that we will add any more types as exceptions in the future, but we will consider it if we find a compelling reason to do so.
33+
1734
## Version 2 (.NET 7 Release)
1835

1936
The focus of version 2 is to support all repos that make up the .NET Product, including ASP.NET Core and Windows Forms, as well as all packages in dotnet/runtime.

src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,9 @@ public static string MarshalEx(InteropGenerationOptions options)
156156
public const string System_Runtime_InteropServices_Marshalling_ComObject = "System.Runtime.InteropServices.Marshalling.ComObject";
157157

158158
public const string System_Runtime_InteropServices_BestFitMappingAttribute = "System.Runtime.InteropServices.BestFitMappingAttribute";
159+
160+
public const string System_Runtime_InteropServices_CLong = "System.Runtime.InteropServices.CLong";
161+
162+
public const string System_Runtime_InteropServices_CULong = "System.Runtime.InteropServices.CULong";
159163
}
160164
}

src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,20 @@ static unsafe bool IsStrictlyBlittableWorker(ITypeSymbol t, ImmutableHashSet<ITy
7272
if (t.ContainingAssembly is not ISourceAssemblySymbol sourceAssembly
7373
|| sourceAssembly.Compilation != compilation)
7474
{
75+
// We have a few exceptions to this rule. We allow a select number of types that we know are unmanaged and will always be unmanaged.
76+
if (t.ToDisplayString() is TypeNames.System_Runtime_InteropServices_CLong // CLong is an interop intrinsic type for the C long type
77+
or TypeNames.System_Runtime_InteropServices_CULong)// CULong is an interop intrinsic type for the C ulong type
78+
{
79+
return true;
80+
}
81+
82+
if (t.ContainingAssembly.Equals(compilation.GetSpecialType(SpecialType.System_Object).ContainingAssembly, SymbolEqualityComparer.Default))
83+
{
84+
if (t.ToDisplayString() == TypeNames.System_Guid) // .NET has established that Guid is blittable and matches the shape of the Win32 GUID type exactly and always will.
85+
{
86+
return true;
87+
}
88+
}
7589
return false;
7690
}
7791

src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,35 @@ private static string ID(
3333

3434
public static IEnumerable<object[]> CodeSnippetsToCompile()
3535
{
36-
yield return new[] { ID(), CodeSnippets.TrivialClassDeclarations };
37-
yield return new[] { ID(), CodeSnippets.TrivialStructDeclarations };
38-
yield return new[] { ID(), CodeSnippets.MultipleAttributes };
39-
yield return new[] { ID(), CodeSnippets.NestedNamespace };
40-
yield return new[] { ID(), CodeSnippets.NestedTypes };
41-
yield return new[] { ID(), CodeSnippets.UnsafeContext };
42-
yield return new[] { ID(), CodeSnippets.UserDefinedEntryPoint };
43-
yield return new[] { ID(), CodeSnippets.AllLibraryImportNamedArguments };
44-
yield return new[] { ID(), CodeSnippets.DefaultParameters };
45-
yield return new[] { ID(), CodeSnippets.UseCSharpFeaturesForConstants };
46-
47-
// Parameter / return types
48-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<byte>() };
49-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<sbyte>() };
50-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<short>() };
51-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<ushort>() };
52-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<int>() };
53-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<uint>() };
54-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<long>() };
55-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<ulong>() };
56-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<float>() };
57-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<double>() };
58-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<IntPtr>() };
59-
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<UIntPtr>() };
36+
//yield return new[] { ID(), CodeSnippets.TrivialClassDeclarations };
37+
//yield return new[] { ID(), CodeSnippets.TrivialStructDeclarations };
38+
//yield return new[] { ID(), CodeSnippets.MultipleAttributes };
39+
//yield return new[] { ID(), CodeSnippets.NestedNamespace };
40+
//yield return new[] { ID(), CodeSnippets.NestedTypes };
41+
//yield return new[] { ID(), CodeSnippets.UnsafeContext };
42+
//yield return new[] { ID(), CodeSnippets.UserDefinedEntryPoint };
43+
//yield return new[] { ID(), CodeSnippets.AllLibraryImportNamedArguments };
44+
//yield return new[] { ID(), CodeSnippets.DefaultParameters };
45+
//yield return new[] { ID(), CodeSnippets.UseCSharpFeaturesForConstants };
46+
47+
//// Parameter / return types
48+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<byte>() };
49+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<sbyte>() };
50+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<short>() };
51+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<ushort>() };
52+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<int>() };
53+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<uint>() };
54+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<long>() };
55+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<ulong>() };
56+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<float>() };
57+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<double>() };
58+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<IntPtr>() };
59+
//yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<UIntPtr>() };
60+
61+
// Parameter / return types for specially considered "strictly blittable" types.
62+
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<CLong>() };
63+
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<CULong>() };
64+
yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers<Guid>() };
6065

6166
// Arrays
6267
yield return new[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers<byte>() };

0 commit comments

Comments
 (0)