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
14 changes: 8 additions & 6 deletions scratch/Program.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
var u = new MyUnion(new A(1, 2.0f));
using System.Runtime.CompilerServices;

Console.WriteLine(u);
var u = new MyUnion(new A(1, 2));

if (u is A)
Console.WriteLine("I'm an A");
Console.WriteLine($"size: {Unsafe.SizeOf<MyUnion>()}");

if (u is byte)
Console.WriteLine("I'm a byte");

if (u is IFoo)
Console.WriteLine("I'm an IFoo");
Expand All @@ -20,12 +22,12 @@ partial void Cases(
int x,
IBar bar,
IFoo foo,
A a
byte y
);
}

public interface IFoo {}
public interface IBar {}
public record struct A(int X, float Y) : IFoo;
public record struct A(int X, byte Y) : IFoo;


2 changes: 1 addition & 1 deletion scratch/Scratch.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<ItemGroup>
<!-- version must match SourceGenerators.Package build output; clear ~/.nuget/packages/uniontypes.toolkit.generators after rebuilding -->
<PackageReference Include="UnionTypes.Toolkit.Generators" Version="0.0.0" PrivateAssets="all" />
<PackageReference Include="UnionTypes.Toolkit.Generator" Version="0.0.0" PrivateAssets="all" />
</ItemGroup>

</Project>
3 changes: 2 additions & 1 deletion src/GeneratedTypes/SimplePrimitivesUnion_generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
#pragma warning disable CS8618

[System.Runtime.CompilerServices.Union]
[StructLayout(LayoutKind.Sequential)]
public partial struct SimplePrimitivesUnion : System.Runtime.CompilerServices.IUnion
{
private readonly int _kind;
private readonly Overlapped _overlapped;
private readonly byte _kind;

[StructLayout(LayoutKind.Explicit)]
private struct Overlapped
Expand Down
3 changes: 2 additions & 1 deletion src/GeneratedTypes/TagLikeUnion_generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
#pragma warning disable CS8618

[System.Runtime.CompilerServices.Union]
[StructLayout(LayoutKind.Sequential)]
public partial struct TagLikeUnion : System.Runtime.CompilerServices.IUnion
{
private readonly int _kind;
private readonly Overlapped _overlapped;
private readonly byte _kind;

[StructLayout(LayoutKind.Explicit)]
private struct Overlapped
Expand Down
10 changes: 5 additions & 5 deletions src/Generators.Tests/CustomUnionGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public void TestOverlappablePrimitiveCases()
generatedText =>
{
// prove that the two primitives got overlapped into the overlapped field
Assert.IsTrue(generatedText.Contains("int _kind"));
Assert.IsTrue(generatedText.Contains("_kind"));
Assert.IsTrue(generatedText.Contains("_overlapped"));
Assert.IsFalse(generatedText.Contains("_value1"));

Expand Down Expand Up @@ -79,7 +79,7 @@ public record struct B(float Value);
),
generatedText =>
{
Assert.IsTrue(generatedText.Contains("int _kind"));
Assert.IsTrue(generatedText.Contains("_kind"));
Assert.IsTrue(generatedText.Contains("_overlapped"));
Assert.IsFalse(generatedText.Contains("_value1"));
Assert.IsTrue(generatedText.Contains("A Case1"));
Expand Down Expand Up @@ -167,7 +167,7 @@ public record struct B(float Value, int Value2, string Value3);
),
generatedText =>
{
Assert.IsTrue(generatedText.Contains("int _kind"));
Assert.IsTrue(generatedText.Contains("_kind"));
Assert.IsTrue(generatedText.Contains("_overlapped"));
Assert.IsTrue(generatedText.Contains("_value1"));
Assert.IsTrue(generatedText.Contains("int Case1"));
Expand Down Expand Up @@ -225,7 +225,7 @@ public record struct F(int Value, int Value2);
),
generatedText =>
{
Assert.IsTrue(generatedText.Contains("int _kind"));
Assert.IsTrue(generatedText.Contains("_kind"));
Assert.IsTrue(generatedText.Contains("_overlapped"));
Assert.IsTrue(generatedText.Contains("object? _value1"));
Assert.IsTrue(generatedText.Contains("object? _value2"));
Expand Down Expand Up @@ -256,7 +256,7 @@ public record struct B();
),
generatedText =>
{
Assert.IsTrue(generatedText.Contains("int _kind"));
Assert.IsTrue(generatedText.Contains("_kind"));
// there is no overlapped data so the field should not exist
Assert.IsFalse(generatedText.Contains("_overlapped"));
// there is no data other than the tag _kind, so no value fields should exist
Expand Down
16 changes: 12 additions & 4 deletions src/Generators/CustomUnionGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ private void WriteUnionType(UnionInfo union)
void WriteUnion()
{
_writer.WriteLine("[System.Runtime.CompilerServices.Union]");
_writer.WriteLine($"[StructLayout(LayoutKind.Sequential)]");
_writer.WriteLine($"{union.Accessibility} partial struct {union.DeclarationName} : System.Runtime.CompilerServices.IUnion");
_writer.WriteBraceNested(() =>
{
Expand All @@ -91,15 +92,22 @@ void WriteUnion()
/// </summary>
private void WriteStorageFields(UnionLayout layout)
{
if (layout.TagField != null)
_writer.WriteLine($"private readonly {layout.TagField.Type.TypeName} {layout.TagField.Name};");
// place data fields up front since they are either reference type fiels or
// likely contain reference types (otherwise they would have been overlapped)
// and will always be pointer-size aligned.
foreach (var field in layout.DataFields)
{
_writer.WriteLine($"private readonly {field.Type.TypeName} {field.Name};");
}

if (layout.OverlappedField != null)
_writer.WriteLine($"private readonly {layout.OverlappedField.Type.TypeName} {layout.OverlappedField.Name};");

foreach (var field in layout.DataFields)
// put tag field at end so it may fit inside any otherwise padding space left over from the overlapped field being less than pointer size aligned.
if (layout.TagField != null)
{
_writer.WriteLine($"private readonly {field.Type.TypeName} {field.Name};");
var type = layout.CaseLayouts.Count <= 255 ? "byte" : "ushort";
_writer.WriteLine($"private readonly {type} {layout.TagField.Name};");
}
}

Expand Down
Loading