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
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Appegy.Union.Generator.Shapes;
using System.Runtime.InteropServices;

namespace Appegy.Union.Generator.Shapes;

public interface IShape
{
Expand All @@ -7,4 +9,5 @@ public interface IShape

[Union(typeof(Circle), typeof(Rectangle), typeof(Hexagon))]
[Expose(typeof(IShape))]
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public partial struct Shape;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ public class UnionComparisonPart : GeneratorPart<UnionAttributePartInput>
{
public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input)
{
var (syntax, types) = input;
var syntax = input.Syntax;
var types = input.Types;

codeWriter.Write("public static bool operator ==(");
codeWriter.Write(syntax.Identifier.Text);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ public class UnionConstructorsPart : GeneratorPart<UnionAttributePartInput>
{
public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input)
{
var (syntax, types) = input;
var syntax = input.Syntax;
var types = input.Types;

for (var i = 0; i < types.Count; i++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ public class UnionDeclarationPart : GeneratorPart<UnionAttributePartInput>
{
public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input)
{
var (syntax, types) = input;
var syntax = input.Syntax;
var types = input.Types;

codeWriter.WriteLine(AttributesNames.GeneratedCodeAttribute);
codeWriter.WriteLine("[global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Explicit, Pack = 1)]");
codeWriter.Write("partial struct ");
codeWriter.WriteLine(syntax.Identifier.Text);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ public class UnionEqualsPart : GeneratorPart<UnionAttributePartInput>
{
public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input)
{
var (syntax, types) = input;
var syntax = input.Syntax;
var types = input.Types;

codeWriter.WriteLine("public override bool Equals(object boxed) => boxed switch");
codeWriter.WriteLine('{');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@ public class UnionFieldsPart : GeneratorPart<UnionAttributePartInput>
{
public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input)
{
var (_, types) = input;
var types = input.Types;
var explicitLayout = input.ExplicitLayout;

codeWriter.WriteLine("[global::System.Runtime.InteropServices.FieldOffset(0)]");
if (explicitLayout)
{
codeWriter.WriteLine("[global::System.Runtime.InteropServices.FieldOffset(0)]");
}
codeWriter.WriteLine("private Kind _type;");

foreach (var type in types)
{
codeWriter.WriteLine("[global::System.Runtime.InteropServices.FieldOffset(1)]");
if (explicitLayout)
{
codeWriter.WriteLine("[global::System.Runtime.InteropServices.FieldOffset(1)]");
}
codeWriter.Write("private ");
codeWriter.Write(type.FullName);
codeWriter.Write(' ');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class UnionGetHashCodePart : GeneratorPart<UnionAttributePartInput>
{
public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input)
{
var (_, types) = input;
var types = input.Types;

codeWriter.WriteLine("public override int GetHashCode() => _type switch");
codeWriter.WriteLine('{');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ public class UnionOperatorsPart : GeneratorPart<UnionAttributePartInput>
{
public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input)
{
var (syntax, types) = input;
var syntax = input.Syntax;
var types = input.Types;

for (var i = 0; i < types.Count; i++)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System.CodeDom.Compiler;
using SymbolDisplayFormat = Microsoft.CodeAnalysis.SymbolDisplayFormat;

namespace Appegy.Union.Generator;

public class UnionPropertiesPart : GeneratorPart<UnionAttributePartInput>
{
public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input)
{
var (_, types) = input;
var types = input.Types;

codeWriter.WriteLine("public Kind Type => _type;");
codeWriter.WriteLine();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class UnionToStringPart : GeneratorPart<UnionAttributePartInput>
{
public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input)
{
var (_, types) = input;
var types = input.Types;

codeWriter.WriteLine("public override string ToString() => _type switch");
codeWriter.WriteLine('{');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class UnionTypeEnumPart : GeneratorPart<UnionAttributePartInput>
{
public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input)
{
var (_, types) = input;
var types = input.Types;

codeWriter.WriteLine("[global::System.Serializable]");
codeWriter.WriteLine("public enum Kind : global::System.Byte");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,21 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
transform: static (ctx, _) =>
{
var syntax = (StructDeclarationSyntax)ctx.TargetNode;
var symbol = ctx.SemanticModel.GetDeclaredSymbol(syntax);
var attribute = ctx.Attributes.First();
var types = attribute
.GetTypesFromConstructor(TypeKind.Struct)
.Select(c => new UnionTypeInfo(c))
.ToImmutableList();
return (syntax, types);

var explicitLayout = HasExplicitStructLayoutAttribute(symbol);

return (syntax, types, explicitLayout);
});

context.RegisterSourceOutput(sources, static (ctx, input) =>
{
var (syntax, types) = input;
var (syntax, types, explicitLayout) = input;

if (!syntax.Modifiers.Any(SyntaxKind.PartialKeyword)) return;
if (types.Count == 0) return;
Expand All @@ -63,10 +67,36 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
using var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8);
using var codeWriter = new IndentedTextWriter(streamWriter, " ");

codeWriter.AppendParts(Parts, new UnionAttributePartInput(syntax, types));
codeWriter.AppendParts(Parts, new UnionAttributePartInput(syntax, types, explicitLayout));

streamWriter.Flush();
ctx.AddSource($"{syntax.Identifier.Text}_Union.g.cs", SourceText.From(memoryStream, Encoding.UTF8, canBeEmbedded: true));
});
}

private static bool HasExplicitStructLayoutAttribute(INamedTypeSymbol? symbol)
{
if (symbol == null) return false;

return symbol.GetAttributes().Any(attr =>
{
if (attr.AttributeClass?.Name != "StructLayoutAttribute" ||
attr.AttributeClass.ContainingNamespace?.ToDisplayString() != "System.Runtime.InteropServices")
{
return false;
}

if (attr.ConstructorArguments.Length > 0)
{
var layoutKindArg = attr.ConstructorArguments[0];

if (layoutKindArg.Type?.Name == "LayoutKind" && layoutKindArg.Type.ContainingNamespace?.ToDisplayString() == "System.Runtime.InteropServices")
{
return layoutKindArg.Value is 2;
}
}

return false;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Appegy.Union.Generator;

public record struct UnionAttributePartInput(StructDeclarationSyntax Syntax, IReadOnlyList<UnionTypeInfo> Types);
public record struct UnionAttributePartInput(StructDeclarationSyntax Syntax, IReadOnlyList<UnionTypeInfo> Types, bool ExplicitLayout);

public readonly struct UnionTypeInfo(INamedTypeSymbol symbol)
{
Expand Down
4 changes: 2 additions & 2 deletions Runtime/Appegy.Union.Generator.dll
Git LFS file not shown
Binary file modified Runtime/Appegy.Union.Generator.pdb
Binary file not shown.