From ab62e4c2bd1a4085119a77b65369445880496af8 Mon Sep 17 00:00:00 2001 From: Ivan Murashka Date: Sat, 6 Sep 2025 13:01:51 +0200 Subject: [PATCH] Move StructLayout attribute to base declaration typo --- .../Shapes/Shape.cs | 5 ++- .../Union/Parts/UnionComparisonPart.cs | 3 +- .../Union/Parts/UnionConstructorsPart.cs | 3 +- .../Union/Parts/UnionDeclarationPart.cs | 4 +- .../Union/Parts/UnionEqualsPart.cs | 3 +- .../Union/Parts/UnionFieldsPart.cs | 13 +++++-- .../Union/Parts/UnionGetHashCodePart.cs | 2 +- .../Union/Parts/UnionOperatorsPart.cs | 3 +- .../Union/Parts/UnionPropertiesPart.cs | 3 +- .../Union/Parts/UnionToStringPart.cs | 2 +- .../Union/Parts/UnionTypeEnumPart.cs | 2 +- .../Union/UnionAttributeGenerator.cs | 36 ++++++++++++++++-- .../Union/UnionAttributePartInput.cs | 2 +- Runtime/Appegy.Union.Generator.dll | 4 +- Runtime/Appegy.Union.Generator.pdb | Bin 26952 -> 27300 bytes 15 files changed, 64 insertions(+), 21 deletions(-) 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 a10f83c5e6099ecfdce22ef0e4ff221f08ec0bdf..775af1825958cd34de9ad59ebd71eef1d0ff0d23 100644 GIT binary patch delta 7036 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 delta 6632 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 H-LU>I-!7l>