Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ Here's an example of some of the new features that **PolySharp** can enable down
- `[ExtensionMarker]` (needed for [extensions](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14#extension-members))
- `[IsClosedType]` (needed for [closed hierarchies](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-15.0/closed-hierarchies))
- `[RequiresUnsafe]` (needed for [unsafe evolution](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/unsafe-evolution))
- Union types (needed for [union types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union))
- `[Union]`
- `IUnion`

To leverage them, make sure to bump your C# language version. You can do this by setting the `<LangVersion>` MSBuild property in your project. For instance, by adding `<LangVersion>15.0</LangVersion>` (or your desired C# version) to the first `<PropertyGroup>` of your .csproj file. For more info on this, [see here](https://sergiopedri.medium.com/enabling-and-using-c-9-features-on-older-and-unsupported-runtimes-ce384d8debb), but remember that you don't need to manually copy polyfills anymore: simply adding a reference to **PolySharp** will do this for you automatically.

Expand Down
3 changes: 3 additions & 0 deletions src/PolySharp.Package/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ Here's an example of some of the new features that **PolySharp** can enable down
- `[ExtensionMarker]` (needed for [extensions](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14#extension-members))
- `[IsClosedType]` (needed for [closed hierarchies](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-15.0/closed-hierarchies))
- `[RequiresUnsafe]` (needed for [unsafe evolution](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/unsafe-evolution))
- Union types (needed for [union types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union))
- `[Union]`
- `IUnion`

To leverage them, make sure to bump your C# language version. You can do this by setting the `<LangVersion>` MSBuild property in your project. For instance, by adding `<LangVersion>15.0</LangVersion>` (or your desired C# version) to the first `<PropertyGroup>` of your .csproj file. For more info on this, [see here](https://sergiopedri.medium.com/enabling-and-using-c-9-features-on-older-and-unsupported-runtimes-ce384d8debb), but remember that you don't need to manually copy polyfills anymore: simply adding a reference to **PolySharp** will do this for you automatically.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// <auto-generated/>
#pragma warning disable
#nullable enable annotations

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Runtime.CompilerServices
{
/// <summary>
/// Provides a common interface for accessing the contents of a union type at runtime.
/// </summary>
/// <remarks>
/// The C# compiler automatically implements this interface on types generated by union declarations.
/// User-defined types annotated with <see cref="global::System.Runtime.CompilerServices.UnionAttribute"/> may also implement this interface
/// to provide runtime access to the union's value. Implementing this interface is not required
/// for union behaviors provided by the compiler.
/// </remarks>
/// <seealso cref="global::System.Runtime.CompilerServices.UnionAttribute"/>
[global::Microsoft.CodeAnalysis.Embedded]
internal interface IUnion
{
/// <summary>
/// Gets the value contained in the union, or <see langword="null"/> if the union has no value.
/// </summary>
/// <value>
/// The current value of the union as one of its case types, or <see langword="null"/>.
/// </value>
object? Value { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// <auto-generated/>
#pragma warning disable
#nullable enable annotations

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Runtime.CompilerServices
{
/// <summary>
/// Indicates that a class or struct is a union type, enabling compiler support for union behaviors.
/// </summary>
/// <remarks>
/// Any class or struct annotated with this attribute is recognized by the C# compiler as a union type.
/// Union types may support behaviors such as implicit conversions from case types, pattern matching
/// that unwraps the union's contents, and switch exhaustiveness checking.
/// </remarks>
/// <seealso cref="global::System.Runtime.CompilerServices.IUnion"/>
[global::System.AttributeUsage(
global::System.AttributeTargets.Class |
global::System.AttributeTargets.Struct,
AllowMultiple = false,
Inherited = false)]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
Comment thread
AndiJingzhiTse marked this conversation as resolved.
[global::Microsoft.CodeAnalysis.Embedded]
internal sealed class UnionAttribute : global::System.Attribute
{
}
}
93 changes: 93 additions & 0 deletions tests/PolySharp.Tests/LanguageFeatures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,99 @@ public static void AnotherCpuIntrinsic([ConstantExpected(Min = 0, Max = 8)] int
}
}

// UnionAttribute, IUnion
internal static class UnionTests
{
public static int Test()
{
IntOrString number = 42;
IntOrString text = "PolySharp";
IUnion union = number;

_ = union.Value;
_ = GetValue(number);
_ = GetValue(default);

return GetValue(text);
}

public static int GetValue(IntOrString value)
{
return value switch
{
int number => number,
string text => text.Length,
null => 0
};
}

public static int TestManualUnion()
{
IntOrBool number = 42;
IntOrBool boolean = true;

_ = GetManualValue(number);
_ = GetManualValue(default);

return GetManualValue(boolean);
}

public static int GetManualValue(IntOrBool value)
{
return value switch
{
int number => number,
bool boolean => boolean ? 1 : 0,
null => -1
};
}

public readonly union IntOrString(int, string);

[Union]
public readonly struct IntOrBool : IUnion
{
private readonly int number;
private readonly bool boolean;
private readonly byte tag;

public IntOrBool(int value)
{
this.number = value;
this.tag = 1;
}

public IntOrBool(bool value)
{
this.boolean = value;
this.tag = 2;
}

public object? Value => this.tag switch
{
1 => this.number,
2 => this.boolean,
_ => null
};

public bool HasValue => this.tag != 0;

public bool TryGetValue(out int value)
{
value = this.number;

return this.tag == 1;
}

public bool TryGetValue(out bool value)
{
value = this.boolean;

return this.tag == 2;
}
}
}

[CompilerLoweringPreserve]
internal sealed class TestFlowingAttribute : Attribute;

Expand Down
Loading