From a586b1acf2ff88f2f7d3640996655a09890e64b5 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sat, 12 Feb 2022 23:50:43 +0700 Subject: [PATCH] (#72) IR: support type generation for structs --- Cesium.Ast/Declarations.cs | 6 +-- ...s.SingleFieldStructDefinition.verified.txt | 7 +++ .../Contexts/TranslationUnitContext.cs | 14 +++++- Cesium.CodeGen/Extensions/TypeDefinitionEx.cs | 12 +++-- Cesium.CodeGen/Extensions/TypeSystemEx.cs | 2 +- .../Ir/BlockItems/DeclarationBlockItem.cs | 2 +- .../Ir/Declarations/LocalDeclarationInfo.cs | 25 +++++++++- .../Ir/TopLevel/FunctionDefinition.cs | 4 +- .../Ir/TopLevel/TopLevelDeclaration.cs | 10 ++-- Cesium.CodeGen/Ir/Types/ConstType.cs | 3 +- Cesium.CodeGen/Ir/Types/INamedType.cs | 9 ++++ Cesium.CodeGen/Ir/Types/IType.cs | 3 +- Cesium.CodeGen/Ir/Types/PointerType.cs | 3 +- Cesium.CodeGen/Ir/Types/PrimitiveType.cs | 17 ++++--- Cesium.CodeGen/Ir/Types/StructType.cs | 47 +++++++++++++++++-- 15 files changed, 133 insertions(+), 31 deletions(-) create mode 100644 Cesium.CodeGen.Tests/CodeGenTypeTests.SingleFieldStructDefinition.verified.txt create mode 100644 Cesium.CodeGen/Ir/Types/INamedType.cs diff --git a/Cesium.Ast/Declarations.cs b/Cesium.Ast/Declarations.cs index 72e60062..9d685ac1 100644 --- a/Cesium.Ast/Declarations.cs +++ b/Cesium.Ast/Declarations.cs @@ -15,7 +15,7 @@ public interface IDeclarationSpecifier { } public record StorageClassSpecifier(string Name) : IDeclarationSpecifier; // 6.7.2 Type specifiers -public interface ITypeSpecifier : ISpecifierQualifierListItem, IDeclarationSpecifier { } +public interface ITypeSpecifier : ISpecifierQualifierListItem { } public record SimpleTypeSpecifier(string TypeName) : ITypeSpecifier; public record StructOrUnionSpecifier( @@ -33,12 +33,12 @@ public record StructDeclaration( ImmutableArray SpecifiersQualifiers, ImmutableArray? Declarators); -public interface ISpecifierQualifierListItem {} +public interface ISpecifierQualifierListItem : IDeclarationSpecifier {} public record StructDeclarator(Declarator Declarator); // 6.7.3 Type qualifiers -public record TypeQualifier(string Name) : IDeclarationSpecifier, ISpecifierQualifierListItem; +public record TypeQualifier(string Name) : ISpecifierQualifierListItem; // 6.7.7 Type names public record AbstractDeclarator(Pointer? Pointer = null, IDirectAbstractDeclarator? DirectAbstractDeclarator = null); diff --git a/Cesium.CodeGen.Tests/CodeGenTypeTests.SingleFieldStructDefinition.verified.txt b/Cesium.CodeGen.Tests/CodeGenTypeTests.SingleFieldStructDefinition.verified.txt new file mode 100644 index 00000000..ea2a76b6 --- /dev/null +++ b/Cesium.CodeGen.Tests/CodeGenTypeTests.SingleFieldStructDefinition.verified.txt @@ -0,0 +1,7 @@ +Module: Primary + Type: + + Type: foo + Fields: + System.Int32 foo::x + Init with: [] diff --git a/Cesium.CodeGen/Contexts/TranslationUnitContext.cs b/Cesium.CodeGen/Contexts/TranslationUnitContext.cs index 62454b86..1996b9c0 100644 --- a/Cesium.CodeGen/Contexts/TranslationUnitContext.cs +++ b/Cesium.CodeGen/Contexts/TranslationUnitContext.cs @@ -1,4 +1,5 @@ using Cesium.CodeGen.Contexts.Meta; +using Cesium.CodeGen.Ir.Types; using Mono.Cecil; namespace Cesium.CodeGen.Contexts; @@ -10,5 +11,16 @@ public record TranslationUnitContext(AssemblyContext AssemblyContext) public TypeSystem TypeSystem => Module.TypeSystem; public TypeDefinition ModuleType => Module.GetType(""); internal Dictionary Functions => AssemblyContext.Functions; - internal Dictionary Types { get; } = new(); + + private readonly Dictionary _generatedTypes = new(); + private readonly Dictionary _types = new(); + + internal void GenerateType(INamedType type, string name) + { + var typeReference = type.Emit(name, this); + _generatedTypes.Add(type, typeReference); + _types.Add(name, typeReference); + } + + internal TypeReference? GetTypeReference(INamedType type) => _generatedTypes.GetValueOrDefault(type); } diff --git a/Cesium.CodeGen/Extensions/TypeDefinitionEx.cs b/Cesium.CodeGen/Extensions/TypeDefinitionEx.cs index 3f8aa0e5..18328b11 100644 --- a/Cesium.CodeGen/Extensions/TypeDefinitionEx.cs +++ b/Cesium.CodeGen/Extensions/TypeDefinitionEx.cs @@ -1,3 +1,4 @@ +using Cesium.CodeGen.Contexts; using Cesium.CodeGen.Ir; using Mono.Cecil; @@ -7,7 +8,7 @@ internal static class TypeDefinitionEx { public static MethodDefinition DefineMethod( this TypeDefinition type, - TypeSystem typeSystem, + TranslationUnitContext context, string name, TypeReference returnType, ParametersInfo? parameters) @@ -16,12 +17,15 @@ public static MethodDefinition DefineMethod( name, MethodAttributes.Public | MethodAttributes.Static, returnType); - AddParameters(typeSystem, method, parameters); + AddParameters(context, method, parameters); type.Methods.Add(method); return method; } - private static void AddParameters(TypeSystem typeSystem, MethodDefinition method, ParametersInfo? parametersInfo) + private static void AddParameters( + TranslationUnitContext context, + MethodReference method, + ParametersInfo? parametersInfo) { if (parametersInfo == null) return; var (parameters, isVoid, isVarArg) = parametersInfo; @@ -34,7 +38,7 @@ private static void AddParameters(TypeSystem typeSystem, MethodDefinition method foreach (var parameter in parameters) { var (type, name) = parameter; - var parameterDefinition = new ParameterDefinition(type.Resolve(typeSystem)) + var parameterDefinition = new ParameterDefinition(type.Resolve(context)) { Name = name }; diff --git a/Cesium.CodeGen/Extensions/TypeSystemEx.cs b/Cesium.CodeGen/Extensions/TypeSystemEx.cs index a6a82c00..a9a617b6 100644 --- a/Cesium.CodeGen/Extensions/TypeSystemEx.cs +++ b/Cesium.CodeGen/Extensions/TypeSystemEx.cs @@ -4,7 +4,7 @@ namespace Cesium.CodeGen.Extensions; -public static class TypeSystemEx +internal static class TypeSystemEx { public static MethodReference? MethodLookup(this TranslationUnitContext context, string memberName) { diff --git a/Cesium.CodeGen/Ir/BlockItems/DeclarationBlockItem.cs b/Cesium.CodeGen/Ir/BlockItems/DeclarationBlockItem.cs index 69b0cb36..907cb8ca 100644 --- a/Cesium.CodeGen/Ir/BlockItems/DeclarationBlockItem.cs +++ b/Cesium.CodeGen/Ir/BlockItems/DeclarationBlockItem.cs @@ -77,7 +77,7 @@ private static void EmitScopedIdentifier(FunctionScope scope, ScopedIdentifierDe throw new NotSupportedException( $"Local declaration with a CLI import member name {cliImportMemberName} isn't supported."); - var typeReference = type.Resolve(scope.TypeSystem); + var typeReference = type.Resolve(scope.Context); var variable = new VariableDefinition(typeReference); method.Body.Variables.Add(variable); scope.Variables.Add(identifier, variable); diff --git a/Cesium.CodeGen/Ir/Declarations/LocalDeclarationInfo.cs b/Cesium.CodeGen/Ir/Declarations/LocalDeclarationInfo.cs index 2df50def..1c363556 100644 --- a/Cesium.CodeGen/Ir/Declarations/LocalDeclarationInfo.cs +++ b/Cesium.CodeGen/Ir/Declarations/LocalDeclarationInfo.cs @@ -148,7 +148,7 @@ private static (IType, string? cliImportMemberName) ProcessSpecifiers( if (identifier != null) throw new NotImplementedException($"Named structures aren't supported, yet: {identifier}."); - type = new StructType(structDeclarations); + type = new StructType(GetTypeMemberDeclarations(structDeclarations)); break; } @@ -163,4 +163,27 @@ private static (IType, string? cliImportMemberName) ProcessSpecifiers( return (isConst ? new ConstType(type) : type, cliImportMemberName); } + + private static IEnumerable GetTypeMemberDeclarations( + IEnumerable structDeclarations) + { + return structDeclarations.SelectMany(memberDeclarator => + { + var (specifiersQualifiers, declarators) = memberDeclarator; + if (declarators == null) + throw new NotSupportedException( + "Empty declarator list on a struct member declaration:" + + $"{string.Join(", ", specifiersQualifiers)}."); + + var collection = specifiersQualifiers + .Select(x => x) + .ToList(); + + return declarators.Select(d => + { + d.Deconstruct(out var declarator); + return Of(collection, declarator); + }); + }); + } } diff --git a/Cesium.CodeGen/Ir/TopLevel/FunctionDefinition.cs b/Cesium.CodeGen/Ir/TopLevel/FunctionDefinition.cs index ed1f0ade..ae8f1580 100644 --- a/Cesium.CodeGen/Ir/TopLevel/FunctionDefinition.cs +++ b/Cesium.CodeGen/Ir/TopLevel/FunctionDefinition.cs @@ -42,7 +42,7 @@ public FunctionDefinition(Ast.FunctionDefinition function) public void EmitTo(TranslationUnitContext context) { - var returnType = _returnType.Resolve(context.TypeSystem); + var returnType = _returnType.Resolve(context); if (IsMain && returnType != context.TypeSystem.Int32) throw new NotSupportedException( $"Invalid return type for the {_name} function: " + @@ -56,7 +56,7 @@ public void EmitTo(TranslationUnitContext context) var method = declaration switch { - null => context.ModuleType.DefineMethod(context.TypeSystem, _name, returnType, _parameters), + null => context.ModuleType.DefineMethod(context, _name, returnType, _parameters), { MethodReference: MethodDefinition md } => md, _ => throw new NotSupportedException($"Function {_name} already defined as immutable.") }; diff --git a/Cesium.CodeGen/Ir/TopLevel/TopLevelDeclaration.cs b/Cesium.CodeGen/Ir/TopLevel/TopLevelDeclaration.cs index 59b03d24..678fba8b 100644 --- a/Cesium.CodeGen/Ir/TopLevel/TopLevelDeclaration.cs +++ b/Cesium.CodeGen/Ir/TopLevel/TopLevelDeclaration.cs @@ -103,11 +103,10 @@ private static void EmitFunctionDeclaration( return; } - var typeSystem = context.TypeSystem; var method = context.ModuleType.DefineMethod( - typeSystem, + context, identifier, - returnType.Resolve(typeSystem), + returnType.Resolve(context), parametersInfo); context.Functions.Add(identifier, new FunctionInfo(parametersInfo, returnType, method)); @@ -128,7 +127,10 @@ private static void EmitTypeDef(TranslationUnitContext context, TypeDefDeclarati if (cliImportMemberName != null) throw new NotSupportedException($"typedef for CLI import not supported: {cliImportMemberName}."); - context.Types.Add(identifier, type.Resolve(context.TypeSystem)); + if (type is INamedType t) + context.GenerateType(t, identifier); + else + throw new NotSupportedException($"Not supported type generation for type {type}."); } } } diff --git a/Cesium.CodeGen/Ir/Types/ConstType.cs b/Cesium.CodeGen/Ir/Types/ConstType.cs index a5f1690c..852e0288 100644 --- a/Cesium.CodeGen/Ir/Types/ConstType.cs +++ b/Cesium.CodeGen/Ir/Types/ConstType.cs @@ -1,8 +1,9 @@ +using Cesium.CodeGen.Contexts; using Mono.Cecil; namespace Cesium.CodeGen.Ir.Types; internal record ConstType(IType Base) : IType { - public TypeReference Resolve(TypeSystem typeSystem) => Base.Resolve(typeSystem); + public TypeReference Resolve(TranslationUnitContext context) => Base.Resolve(context); } diff --git a/Cesium.CodeGen/Ir/Types/INamedType.cs b/Cesium.CodeGen/Ir/Types/INamedType.cs new file mode 100644 index 00000000..91ddbd67 --- /dev/null +++ b/Cesium.CodeGen/Ir/Types/INamedType.cs @@ -0,0 +1,9 @@ +using Cesium.CodeGen.Contexts; +using Mono.Cecil; + +namespace Cesium.CodeGen.Ir.Types; + +internal interface INamedType +{ + TypeDefinition Emit(string name, TranslationUnitContext context); +} diff --git a/Cesium.CodeGen/Ir/Types/IType.cs b/Cesium.CodeGen/Ir/Types/IType.cs index 2df97d26..a55deb25 100644 --- a/Cesium.CodeGen/Ir/Types/IType.cs +++ b/Cesium.CodeGen/Ir/Types/IType.cs @@ -1,8 +1,9 @@ +using Cesium.CodeGen.Contexts; using Mono.Cecil; namespace Cesium.CodeGen.Ir.Types; internal interface IType { - TypeReference Resolve(TypeSystem typeSystem); + TypeReference Resolve(TranslationUnitContext context); } diff --git a/Cesium.CodeGen/Ir/Types/PointerType.cs b/Cesium.CodeGen/Ir/Types/PointerType.cs index 347b4f12..2559605d 100644 --- a/Cesium.CodeGen/Ir/Types/PointerType.cs +++ b/Cesium.CodeGen/Ir/Types/PointerType.cs @@ -1,3 +1,4 @@ +using Cesium.CodeGen.Contexts; using Mono.Cecil; using Mono.Cecil.Rocks; @@ -5,5 +6,5 @@ namespace Cesium.CodeGen.Ir.Types; internal record PointerType(IType Base) : IType { - public TypeReference Resolve(TypeSystem typeSystem) => Base.Resolve(typeSystem).MakePointerType(); + public TypeReference Resolve(TranslationUnitContext context) => Base.Resolve(context).MakePointerType(); } diff --git a/Cesium.CodeGen/Ir/Types/PrimitiveType.cs b/Cesium.CodeGen/Ir/Types/PrimitiveType.cs index f438c6c3..0220b93f 100644 --- a/Cesium.CodeGen/Ir/Types/PrimitiveType.cs +++ b/Cesium.CodeGen/Ir/Types/PrimitiveType.cs @@ -1,3 +1,4 @@ +using Cesium.CodeGen.Contexts; using Mono.Cecil; namespace Cesium.CodeGen.Ir.Types; @@ -11,11 +12,15 @@ internal enum PrimitiveTypeKind internal record PrimitiveType(PrimitiveTypeKind Kind) : IType { - public TypeReference Resolve(TypeSystem typeSystem) => Kind switch + public TypeReference Resolve(TranslationUnitContext context) { - PrimitiveTypeKind.Char => typeSystem.Byte, - PrimitiveTypeKind.Int => typeSystem.Int32, - PrimitiveTypeKind.Void => typeSystem.Void, - _ => throw new NotImplementedException($"Primitive type not supported, yet: {this}.") - }; + var typeSystem = context.TypeSystem; + return Kind switch + { + PrimitiveTypeKind.Char => typeSystem.Byte, + PrimitiveTypeKind.Int => typeSystem.Int32, + PrimitiveTypeKind.Void => typeSystem.Void, + _ => throw new NotImplementedException($"Primitive type not supported, yet: {this}.") + }; + } } diff --git a/Cesium.CodeGen/Ir/Types/StructType.cs b/Cesium.CodeGen/Ir/Types/StructType.cs index 0b2fb548..9ff73155 100644 --- a/Cesium.CodeGen/Ir/Types/StructType.cs +++ b/Cesium.CodeGen/Ir/Types/StructType.cs @@ -1,14 +1,51 @@ -using Cesium.Ast; +using Cesium.CodeGen.Contexts; +using Cesium.CodeGen.Ir.Declarations; using Mono.Cecil; namespace Cesium.CodeGen.Ir.Types; -public class StructType : IType +internal class StructType : IType, INamedType { - public StructType(IEnumerable declarations) + private readonly IEnumerable _members; + public StructType(IEnumerable members) { + _members = members; } - public TypeReference Resolve(TypeSystem typeSystem) => - throw new NotImplementedException(); + public TypeDefinition Emit(string name, TranslationUnitContext context) + { + var structType = new TypeDefinition( + "", + name, + TypeAttributes.Sealed, + context.Module.ImportReference(typeof(ValueType))); + context.Module.Types.Add(structType); + + foreach (var member in _members) + { + var (type, identifier, parametersInfo, cliImportMemberName) = member; + if (identifier == null) + throw new NotImplementedException( + $"Anonymous struct members for {name} aren't supported, yet: {type}."); + + if (parametersInfo != null) + throw new NotImplementedException( + $"Functional struct members for {name} aren't supported, yet: {identifier}."); + + if (cliImportMemberName != null) + throw new NotSupportedException( + $"CLI imports inside struct members aren't supported: {cliImportMemberName}."); + + structType.Fields.Add( + new FieldDefinition( + identifier, + FieldAttributes.Public, + type.Resolve(context))); + } + + return structType; + } + + public TypeReference Resolve(TranslationUnitContext context) => + context.GetTypeReference(this) ?? throw new NotSupportedException($"Type {this} was not found."); }