Skip to content

Commit 96d32a1

Browse files
committed
IUnion
1 parent 5067ef6 commit 96d32a1

57 files changed

Lines changed: 1017 additions & 34 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apiCount.include.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
**API count: 1021**
1+
**API count: 1023**
22

33
### Per Target Framework
44

api_list.include.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,6 +1510,10 @@
15101510

15111511
#### UnreachableException
15121512

1513+
#### IUnion
1514+
1515+
> Requires [`<PolyUnion>true</PolyUnion>`](#unions) in the consuming project.
1516+
15131517
#### StringStream
15141518

15151519
#### ReadOnlyMemoryStream

assemblySize.include.md

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@
33
| | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability |
44
|----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------|
55
| netstandard2.0 | 8.0KB | 353.0KB | +345.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
6-
| netstandard2.1 | 8.5KB | 306.5KB | +298.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
7-
| net461 | 8.5KB | 351.5KB | +343.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB |
8-
| net462 | 7.0KB | 355.0KB | +348.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
6+
| netstandard2.1 | 8.5KB | 306.5KB | +298.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
7+
| net461 | 8.5KB | 351.5KB | +343.0KB | +9.5KB | +6.5KB | +9.5KB | +14.0KB |
8+
| net462 | 7.0KB | 355.5KB | +348.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
99
| net47 | 7.0KB | 355.0KB | +348.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
10-
| net471 | 8.5KB | 354.0KB | +345.5KB | +8.0KB | +6.5KB | +9.0KB | +13.5KB |
11-
| net472 | 8.5KB | 352.5KB | +344.0KB | +9.5KB | +6.5KB | +9.5KB | +14.0KB |
12-
| net48 | 8.5KB | 352.5KB | +344.0KB | +9.5KB | +6.5KB | +9.5KB | +14.0KB |
13-
| net481 | 8.5KB | 352.5KB | +344.0KB | +9.5KB | +6.5KB | +9.5KB | +14.0KB |
14-
| netcoreapp2.0 | 9.0KB | 330.5KB | +321.5KB | +8.5KB | +6.0KB | +9.0KB | +13.5KB |
15-
| netcoreapp2.1 | 9.0KB | 310.0KB | +301.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
16-
| netcoreapp2.2 | 9.0KB | 310.0KB | +301.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
17-
| netcoreapp3.0 | 9.5KB | 302.5KB | +293.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
18-
| netcoreapp3.1 | 9.5KB | 301.0KB | +291.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
19-
| net5.0 | 9.5KB | 265.0KB | +255.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
20-
| net6.0 | 10.0KB | 206.5KB | +196.5KB | +9.5KB | +7.0KB | +512bytes | +3.5KB |
21-
| net7.0 | 10.0KB | 169.0KB | +159.0KB | +9.0KB | +5.5KB | +512bytes | +3.5KB |
22-
| net8.0 | 9.5KB | 139.5KB | +130.0KB | +8.0KB | | +512bytes | +3.0KB |
10+
| net471 | 8.5KB | 354.0KB | +345.5KB | +9.5KB | +6.5KB | +9.5KB | +14.0KB |
11+
| net472 | 8.5KB | 353.0KB | +344.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
12+
| net48 | 8.5KB | 353.0KB | +344.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
13+
| net481 | 8.5KB | 353.0KB | +344.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
14+
| netcoreapp2.0 | 9.0KB | 330.5KB | +321.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
15+
| netcoreapp2.1 | 9.0KB | 310.0KB | +301.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
16+
| netcoreapp2.2 | 9.0KB | 310.0KB | +301.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
17+
| netcoreapp3.0 | 9.5KB | 303.0KB | +293.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
18+
| netcoreapp3.1 | 9.5KB | 301.0KB | +291.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
19+
| net5.0 | 9.5KB | 265.0KB | +255.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
20+
| net6.0 | 10.0KB | 206.5KB | +196.5KB | +10.0KB | +7.0KB | +512bytes | +3.5KB |
21+
| net7.0 | 10.0KB | 169.0KB | +159.0KB | +9.5KB | +5.5KB | +512bytes | +3.5KB |
22+
| net8.0 | 9.5KB | 139.5KB | +130.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB |
2323
| net9.0 | 9.5KB | 92.5KB | +83.0KB | +8.5KB | | +512bytes | +3.5KB |
2424
| net10.0 | 10.0KB | 70.0KB | +60.0KB | +9.0KB | | +1.0KB | +3.5KB |
2525
| net11.0 | 10.0KB | 31.5KB | +21.5KB | +9.0KB | | +512bytes | +3.5KB |
@@ -30,23 +30,23 @@
3030
| | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability |
3131
|----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------|
3232
| netstandard2.0 | 8.0KB | 516.0KB | +508.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
33-
| netstandard2.1 | 8.5KB | 442.9KB | +434.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
34-
| net461 | 8.5KB | 515.6KB | +507.1KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB |
35-
| net462 | 7.0KB | 519.1KB | +512.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
33+
| netstandard2.1 | 8.5KB | 442.9KB | +434.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
34+
| net461 | 8.5KB | 515.6KB | +507.1KB | +17.2KB | +8.2KB | +14.4KB | +19.4KB |
35+
| net462 | 7.0KB | 519.6KB | +512.6KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
3636
| net47 | 7.0KB | 518.8KB | +511.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
37-
| net471 | 8.5KB | 517.5KB | +509.0KB | +15.7KB | +8.2KB | +13.9KB | +18.9KB |
38-
| net472 | 8.5KB | 514.9KB | +506.4KB | +17.2KB | +8.2KB | +14.4KB | +19.4KB |
39-
| net48 | 8.5KB | 514.9KB | +506.4KB | +17.2KB | +8.2KB | +14.4KB | +19.4KB |
40-
| net481 | 8.5KB | 514.9KB | +506.4KB | +17.2KB | +8.2KB | +14.4KB | +19.4KB |
41-
| netcoreapp2.0 | 9.0KB | 482.9KB | +473.9KB | +16.2KB | +7.7KB | +13.9KB | +18.9KB |
42-
| netcoreapp2.1 | 9.0KB | 450.2KB | +441.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
43-
| netcoreapp2.2 | 9.0KB | 450.2KB | +441.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
44-
| netcoreapp3.0 | 9.5KB | 433.9KB | +424.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
45-
| netcoreapp3.1 | 9.5KB | 432.4KB | +422.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
46-
| net5.0 | 9.5KB | 378.2KB | +368.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
47-
| net6.0 | 10.0KB | 299.6KB | +289.6KB | +17.2KB | +8.7KB | +1.1KB | +4.2KB |
48-
| net7.0 | 10.0KB | 243.5KB | +233.5KB | +16.6KB | +6.9KB | +1.1KB | +4.2KB |
49-
| net8.0 | 9.5KB | 198.5KB | +189.0KB | +15.5KB | +299bytes | +1.1KB | +3.7KB |
37+
| net471 | 8.5KB | 517.5KB | +509.0KB | +17.2KB | +8.2KB | +14.4KB | +19.4KB |
38+
| net472 | 8.5KB | 515.4KB | +506.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
39+
| net48 | 8.5KB | 515.4KB | +506.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
40+
| net481 | 8.5KB | 515.4KB | +506.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
41+
| netcoreapp2.0 | 9.0KB | 482.9KB | +473.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
42+
| netcoreapp2.1 | 9.0KB | 450.2KB | +441.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
43+
| netcoreapp2.2 | 9.0KB | 450.2KB | +441.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
44+
| netcoreapp3.0 | 9.5KB | 434.4KB | +424.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
45+
| netcoreapp3.1 | 9.5KB | 432.4KB | +422.9KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
46+
| net5.0 | 9.5KB | 378.2KB | +368.7KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
47+
| net6.0 | 10.0KB | 299.6KB | +289.6KB | +17.7KB | +8.7KB | +1.1KB | +4.2KB |
48+
| net7.0 | 10.0KB | 243.5KB | +233.5KB | +17.1KB | +6.9KB | +1.1KB | +4.2KB |
49+
| net8.0 | 9.5KB | 198.5KB | +189.0KB | +16.0KB | +811bytes | +1.1KB | +4.2KB |
5050
| net9.0 | 9.5KB | 130.4KB | +120.9KB | +16.0KB | | +1.1KB | +4.2KB |
5151
| net10.0 | 10.0KB | 99.5KB | +89.5KB | +16.5KB | | +1.6KB | +4.2KB |
5252
| net11.0 | 10.0KB | 46.9KB | +36.9KB | +16.5KB | | +1.1KB | +4.2KB |

readme.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,59 @@ Reference: [Improvements in native code interop in .NET 5.0](https://devblogs.mi
524524
* [DisableRuntimeMarshallingAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.disableruntimemarshallingattribute)
525525

526526

527+
### Unions
528+
529+
Polyfills the two marker types that the compiler references when emitting [C# 15 union types](https://devblogs.microsoft.com/dotnet/csharp-15-union-types/), so unions can target older runtimes:
530+
531+
* [UnionAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.unionattribute)
532+
* [IUnion](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.iunion)
533+
534+
This is opt-in. It is off by default to avoid clashing with the `UnionAttribute`/`IUnion` that current preview guidance has projects declare themselves. Enable it in the consuming project:
535+
536+
```xml
537+
<PropertyGroup>
538+
<PolyUnion>true</PolyUnion>
539+
<LangVersion>preview</LangVersion>
540+
</PropertyGroup>
541+
```
542+
543+
> Polyfill supplies only the `UnionAttribute` and `IUnion` types that the generated union struct references. The `union` keyword itself requires the C# 15 (or later) compiler and cannot be backported to an older compiler.
544+
545+
Declaring a union:
546+
547+
```cs
548+
public union Pet(Cat, Dog, Bird);
549+
```
550+
551+
Consuming a union, using the implicit conversion from a case type and pattern matching to unwrap it:
552+
553+
```cs
554+
Pet pet = new Dog("Rex");
555+
556+
var label = pet switch
557+
{
558+
Cat cat => $"cat {cat.Name}",
559+
Dog dog => $"dog {dog.Name}",
560+
Bird bird => $"bird {bird.Name}"
561+
};
562+
```
563+
564+
A union can also be declared explicitly using the polyfilled attribute and interface:
565+
566+
```cs
567+
[Union]
568+
public readonly struct IntOrString : IUnion
569+
{
570+
readonly object? value;
571+
572+
public IntOrString(int value) => this.value = value;
573+
public IntOrString(string value) => this.value = value;
574+
575+
public object? Value => value;
576+
}
577+
```
578+
579+
527580
## Extensions
528581

529582
The class `Polyfill` includes the following extension methods:

src/ApiBuilderTests/BuildApiTest.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public Task RunWithRoslyn()
2323
WriteHelper("Utf16", writer, ref count);
2424
WriteType(nameof(TaskCompletionSource), writer, ref count);
2525
WriteType(nameof(UnreachableException), writer, ref count);
26+
WriteType("IUnion", writer, ref count);
2627
WriteType("StringStream", writer, ref count);
2728
WriteType("ReadOnlyMemoryStream", writer, ref count);
2829
WriteType("WritableMemoryStream", writer, ref count);
@@ -311,6 +312,7 @@ static void WriteTypeMethods(string name, StreamWriter writer, ref int count, IE
311312
["ArgumentOutOfRangeException"] = ("PolyArgumentExceptions", "argumentexception-1"),
312313
["ObjectDisposedException"] = ("PolyArgumentExceptions", "argumentexception-1"),
313314
["Ensure"] = ("PolyEnsure", "ensure-1"),
315+
["IUnion"] = ("PolyUnion", "unions"),
314316
};
315317

316318
static void WriteSectionGate(string sectionName, StreamWriter writer)

src/Consume/Consume.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ class Consume
103103
type = typeof(RequiresUnreferencedCodeAttribute);
104104
type = typeof(UnconditionalSuppressMessageAttribute);
105105
type = typeof(CompilerFeatureRequiredAttribute);
106+
#if PolyUnion
107+
type = typeof(IUnion);
108+
type = typeof(UnionAttribute);
109+
#endif
106110
#if FeatureMemory
107111
type = typeof(CollectionBuilderAttribute);
108112
#endif

src/Consume/Consume.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<NoWarn>$(NoWarn);PolyfillTargetsForNuget</NoWarn>
44
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
5-
<DefineConstants>$(DefineConstants);PolyEnsure;PolyArgumentExceptions</DefineConstants>
5+
<DefineConstants>$(DefineConstants);PolyEnsure;PolyArgumentExceptions;PolyUnion</DefineConstants>
66
<TargetFrameworks>net461;net462;net47;net471;net472;net48;net481;net6.0-windows;netstandard2.0;netstandard2.1;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0;net11.0</TargetFrameworks>
77
<TargetFrameworks Condition="$(Configuration) == 'Release'">net481;netcoreapp3.1;net10.0;net11.0</TargetFrameworks>
88
</PropertyGroup>

src/ConsumeNoOptional/ConsumeNoOptional.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<PolyGuard>false</PolyGuard>
1010
<PolyArgumentExceptions>false</PolyArgumentExceptions>
1111
<PolyStringInterpolation>false</PolyStringInterpolation>
12+
<PolyUnion>false</PolyUnion>
1213
<DefineConstants>$(DefineConstants);NoStringInterpolation</DefineConstants>
1314
</PropertyGroup>
1415
<ItemGroup>

src/Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<PolyGuard>true</PolyGuard>
2020
<PolyArgumentExceptions>true</PolyArgumentExceptions>
2121
<PolyStringInterpolation>true</PolyStringInterpolation>
22+
<PolyUnion>true</PolyUnion>
2223
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
2324
<RunAnalyzers>false</RunAnalyzers>
2425
<Nullable>enable</Nullable>

src/NoExtrasTests/NoExtrasTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<PolyArgumentExceptions>false</PolyArgumentExceptions>
99
<PolyNullability>false</PolyNullability>
1010
<PolyStringInterpolation>false</PolyStringInterpolation>
11+
<PolyUnion>false</PolyUnion>
1112
<RootNamespace>NoExtras</RootNamespace>
1213
</PropertyGroup>
1314
<ItemGroup>

0 commit comments

Comments
 (0)