diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs b/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs index 0e80025..cb510cb 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs @@ -1,4 +1,6 @@ -namespace Appegy.Union.Generator.Shapes; +using System.Runtime.InteropServices; + +namespace Appegy.Union.Generator.Shapes; public interface IShape { @@ -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; \ No newline at end of file diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionComparisonPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionComparisonPart.cs index 9c96e8f..95c7b39 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionComparisonPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionComparisonPart.cs @@ -6,7 +6,8 @@ public class UnionComparisonPart : GeneratorPart { 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); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionConstructorsPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionConstructorsPart.cs index fce1ca4..12a57f9 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionConstructorsPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionConstructorsPart.cs @@ -7,7 +7,8 @@ public class UnionConstructorsPart : GeneratorPart { 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++) { diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionDeclarationPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionDeclarationPart.cs index e22ad2e..653a00d 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionDeclarationPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionDeclarationPart.cs @@ -6,10 +6,10 @@ public class UnionDeclarationPart : GeneratorPart { 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); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionEqualsPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionEqualsPart.cs index b1de4ab..335f88a 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionEqualsPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionEqualsPart.cs @@ -6,7 +6,8 @@ public class UnionEqualsPart : GeneratorPart { 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('{'); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionFieldsPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionFieldsPart.cs index 86b2629..2cfdc67 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionFieldsPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionFieldsPart.cs @@ -6,14 +6,21 @@ public class UnionFieldsPart : GeneratorPart { 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(' '); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionGetHashCodePart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionGetHashCodePart.cs index e701f07..e0f3a8b 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionGetHashCodePart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionGetHashCodePart.cs @@ -6,7 +6,7 @@ public class UnionGetHashCodePart : GeneratorPart { 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('{'); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionOperatorsPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionOperatorsPart.cs index eb81c47..e7a253a 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionOperatorsPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionOperatorsPart.cs @@ -6,7 +6,8 @@ public class UnionOperatorsPart : GeneratorPart { 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++) { diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionPropertiesPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionPropertiesPart.cs index e62589c..288d1eb 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionPropertiesPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionPropertiesPart.cs @@ -1,5 +1,4 @@ using System.CodeDom.Compiler; -using SymbolDisplayFormat = Microsoft.CodeAnalysis.SymbolDisplayFormat; namespace Appegy.Union.Generator; @@ -7,7 +6,7 @@ public class UnionPropertiesPart : GeneratorPart { public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartInput input) { - var (_, types) = input; + var types = input.Types; codeWriter.WriteLine("public Kind Type => _type;"); codeWriter.WriteLine(); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionToStringPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionToStringPart.cs index e896cdb..3d6765d 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionToStringPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionToStringPart.cs @@ -6,7 +6,7 @@ public class UnionToStringPart : GeneratorPart { 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('{'); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionTypeEnumPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionTypeEnumPart.cs index 63e3cc9..72cb116 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionTypeEnumPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionTypeEnumPart.cs @@ -6,7 +6,7 @@ public class UnionTypeEnumPart : GeneratorPart { 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"); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeGenerator.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeGenerator.cs index 9700817..81e9d92 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeGenerator.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeGenerator.cs @@ -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; @@ -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; + }); + } } \ No newline at end of file diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributePartInput.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributePartInput.cs index 8475807..83ca80b 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributePartInput.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributePartInput.cs @@ -4,7 +4,7 @@ namespace Appegy.Union.Generator; -public record struct UnionAttributePartInput(StructDeclarationSyntax Syntax, IReadOnlyList Types); +public record struct UnionAttributePartInput(StructDeclarationSyntax Syntax, IReadOnlyList Types, bool ExplicitLayout); public readonly struct UnionTypeInfo(INamedTypeSymbol symbol) { diff --git a/Runtime/Appegy.Union.Generator.dll b/Runtime/Appegy.Union.Generator.dll index 2f522f7..3d1d07a 100644 --- a/Runtime/Appegy.Union.Generator.dll +++ b/Runtime/Appegy.Union.Generator.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1141df8050ec5086c10e75ac2e0047630095079b1707cbe1e61f6500574e597 -size 55296 +oid sha256:8f674aa7f435e096cf702b4424fecc8de7c7a6fcdb162f2f1dd4b8fe1dcadbd2 +size 55808 diff --git a/Runtime/Appegy.Union.Generator.pdb b/Runtime/Appegy.Union.Generator.pdb index a10f83c..775af18 100644 Binary files a/Runtime/Appegy.Union.Generator.pdb and b/Runtime/Appegy.Union.Generator.pdb differ