Skip to content

Commit

Permalink
(#72) LocalDeclarationInfo: extract the function parameter informatio…
Browse files Browse the repository at this point in the history
…n into FunctionType
  • Loading branch information
ForNeVeR committed Jul 23, 2022
1 parent 147a4d8 commit 64cb53f
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 78 deletions.
8 changes: 8 additions & 0 deletions Cesium.CodeGen.Tests/CodeGenTypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,12 @@ public Task ArrayDeclaration() => DoTest(@"int main()
int x[1];
return 0;
}");

[Fact]
public Task StructFunctionMemberDeclaration() => DoTest(@"typedef struct { void bar(int unused); } foo;
int main(void) {}");

[Fact]
public Task BasicTypeDef() => DoTest(@"typedef int foo;
int main(void) { foo x; }");
}
3 changes: 1 addition & 2 deletions Cesium.CodeGen/Contexts/TranslationUnitContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ internal void GenerateType(IGeneratedType type, string name)
_types.Add(name, typeReference);
}

// TODO[#72]: Unnecessary.
internal void AddType(TypeReference type, string name) => _types.Add(name, type);
internal void AddPlainType(IType type, string name) => _types.Add(name, type.Resolve(this));

internal TypeReference? GetTypeReference(IGeneratedType type) => _generatedTypes.GetValueOrDefault(type);
internal TypeReference? GetTypeReference(string typeName) => _types.GetValueOrDefault(typeName);
Expand Down
6 changes: 1 addition & 5 deletions Cesium.CodeGen/Ir/BlockItems/DeclarationBlockItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,13 @@ private static void EmitScopedIdentifier(IDeclarationScope scope, ScopedIdentifi
foreach (var (declaration, initializer) in declarations)
{
var method = scope.Method;
var (type, identifier, parametersInfo, cliImportMemberName) = declaration;
var (type, identifier, cliImportMemberName) = declaration;

// TODO[#91]: A place to register whether {type} is const or not.

if (identifier == null)
throw new NotSupportedException("An anonymous local declaration isn't supported.");

if (parametersInfo != null)
throw new NotImplementedException(
$"A local declaration of function {identifier} isn't supported, yet.");

if (cliImportMemberName != null)
throw new NotSupportedException(
$"Local declaration with a CLI import member name {cliImportMemberName} isn't supported.");
Expand Down
21 changes: 12 additions & 9 deletions Cesium.CodeGen/Ir/Declarations/LocalDeclarationInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ namespace Cesium.CodeGen.Ir.Declarations;
internal record LocalDeclarationInfo(
IType Type,
string? Identifier,
ParametersInfo? Parameters,
string? CliImportMemberName)
{
public static LocalDeclarationInfo Of(IReadOnlyList<IDeclarationSpecifier> specifiers, Declarator? declarator)
{
var (type, cliImportMemberName) = ProcessSpecifiers(specifiers);
if (declarator == null)
return new LocalDeclarationInfo(type, null, null, null);
return new LocalDeclarationInfo(type, null, null);

var (pointer, directDeclarator) = declarator;
if (pointer != null)
Expand All @@ -32,7 +31,6 @@ public static LocalDeclarationInfo Of(IReadOnlyList<IDeclarationSpecifier> speci
}

string? identifier = null;
ParametersInfo? parameters = null;

var currentDirectDeclarator = directDeclarator;
while (currentDirectDeclarator != null)
Expand All @@ -46,6 +44,11 @@ public static LocalDeclarationInfo Of(IReadOnlyList<IDeclarationSpecifier> speci
throw new NotImplementedException(
"Non-empty identifier list inside of a direct declarator is not supported, yet:" +
$" {string.Join(", ", identifiers)}");

// An empty identifier list is `()` in a declaration like `int main()`. It means that there's an
// empty parameter list, actually.
type = ProcessFunctionParameters(type, null);

break;
}

Expand All @@ -57,11 +60,8 @@ public static LocalDeclarationInfo Of(IReadOnlyList<IDeclarationSpecifier> speci
break;

case ParameterListDirectDeclarator parametersD:
if (parameters != null)
throw new NotSupportedException(
$"Second parameters list declarator for an entity already having one: {parametersD}.");

parameters = ParametersInfo.Of(parametersD.Parameters);
var (_ /* base */, parameters) = parametersD;
type = ProcessFunctionParameters(type, parameters);
break;

case ArrayDirectDeclarator array:
Expand Down Expand Up @@ -112,7 +112,7 @@ public static LocalDeclarationInfo Of(IReadOnlyList<IDeclarationSpecifier> speci
currentDirectDeclarator = currentDirectDeclarator.Base;
}

return new LocalDeclarationInfo(type, identifier, parameters, cliImportMemberName);
return new LocalDeclarationInfo(type, identifier, cliImportMemberName);
}

private static (IType, string? cliImportMemberName) ProcessSpecifiers(
Expand Down Expand Up @@ -281,4 +281,7 @@ private static IType ProcessSimpleTypeSpecifiers(
$"Simple type specifiers are not supported: {string.Join(" ", typeNames)}"),
});
}

private static IType ProcessFunctionParameters(IType returnType, ParameterTypeList? parameters) =>
new FunctionType(ParametersInfo.Of(parameters), returnType);
}
8 changes: 3 additions & 5 deletions Cesium.CodeGen/Ir/ParametersInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ namespace Cesium.CodeGen.Ir;

internal record ParametersInfo(IList<ParameterInfo> Parameters, bool IsVoid, bool IsVarArg)
{
public static ParametersInfo Of(ParameterTypeList parameters)
public static ParametersInfo? Of(ParameterTypeList? parameters)
{
if (parameters == null) return null;
var (parameterList, hasEllipsis) = parameters;

bool isVoid;
Expand Down Expand Up @@ -46,10 +47,7 @@ public static ParameterInfo Of(ParameterDeclaration declaration)
throw new NotImplementedException(
$"Parameter with abstract declarator is not supported, yet: {declaration}.");

var (type, identifier, parameters, cliImportMemberName) = LocalDeclarationInfo.Of(specifiers, declarator);

if (parameters != null)
throw new NotImplementedException($"Parameters with parameters are not supported, yet: {parameters}.");
var (type, identifier, cliImportMemberName) = LocalDeclarationInfo.Of(specifiers, declarator);

if (cliImportMemberName != null)
throw new NotSupportedException("CLI import specifier isn't supported for a parameter.");
Expand Down
30 changes: 15 additions & 15 deletions Cesium.CodeGen/Ir/TopLevel/FunctionDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,18 @@ internal class FunctionDefinition : ITopLevelNode
{
private const string MainFunctionName = "main";

private readonly IType _returnType;
private readonly FunctionType _functionType;
private readonly string _name;
private readonly ParametersInfo? _parameters;
private readonly CompoundStatement _statement;

private bool IsMain => _name == MainFunctionName;


public FunctionDefinition(Ast.FunctionDefinition function)
{
var (specifiers, declarator, declarations, astStatement) = function;
(_returnType, var name, _parameters, var cliImportMemberName) =
LocalDeclarationInfo.Of(specifiers, declarator);
var (type, name, cliImportMemberName) = LocalDeclarationInfo.Of(specifiers, declarator);
_functionType = type as FunctionType
?? throw new NotSupportedException($"Function of not a function type: {type}.");
_name = name ?? throw new NotSupportedException($"Function without name: {function}.");

if (declarations?.IsEmpty == false)
Expand All @@ -42,21 +41,22 @@ public FunctionDefinition(Ast.FunctionDefinition function)

public void EmitTo(TranslationUnitContext context)
{
var returnType = _returnType.Resolve(context);
if (IsMain && returnType != context.TypeSystem.Int32)
var (parameters, returnType) = _functionType;
var resolvedReturnType = returnType.Resolve(context);
if (IsMain && resolvedReturnType != context.TypeSystem.Int32)
throw new NotSupportedException(
$"Invalid return type for the {_name} function: " +
$"int expected, got {_returnType}.");
$"int expected, got {returnType}.");

if (IsMain && _parameters?.IsVarArg == true)
if (IsMain && parameters?.IsVarArg == true)
throw new NotSupportedException($"Variable arguments for the {_name} function aren't supported.");

var declaration = context.Functions.GetValueOrDefault(_name);
declaration?.VerifySignatureEquality(_name, _parameters, _returnType);
declaration?.VerifySignatureEquality(_name, parameters, returnType);

var method = declaration switch
{
null => context.ModuleType.DefineMethod(context, _name, returnType, _parameters),
null => context.ModuleType.DefineMethod(context, _name, resolvedReturnType, parameters),
{ MethodReference: MethodDefinition md } => md,
_ => throw new NotSupportedException($"Function {_name} already defined as immutable.")
};
Expand All @@ -65,7 +65,7 @@ public void EmitTo(TranslationUnitContext context)
throw new NotSupportedException($"Double definition of function {_name}.");

if (declaration == null)
context.Functions.Add(_name, new FunctionInfo(_parameters, _returnType, method, IsDefined: true));
context.Functions.Add(_name, new FunctionInfo(parameters, returnType, method, IsDefined: true));
else
context.Functions[_name] = declaration with { IsDefined = true };

Expand All @@ -91,10 +91,10 @@ public void EmitTo(TranslationUnitContext context)
/// <returns>Whether the synthetic entry point should be generated.</returns>
private bool ValidateMainParameters()
{
if (_parameters == null)
if (_functionType.Parameters == null)
return false; // TODO[#87]: Decide whether this is normal or not.

var (parameterList, isVoid, isVarArg) = _parameters;
var (parameterList, isVoid, isVarArg) = _functionType.Parameters;
if (isVoid) return false; // supported, no synthetic entry point required

if (isVarArg)
Expand Down Expand Up @@ -264,7 +264,7 @@ private void EmitCode(TranslationUnitContext context, FunctionScope scope)
instructions.Add(Instruction.Create(OpCodes.Ldc_I4_0));
instructions.Add(Instruction.Create(OpCodes.Ret));
}
else if (_returnType.Resolve(context) == context.TypeSystem.Void)
else if (_functionType.ReturnType.Resolve(context) == context.TypeSystem.Void)
{
var instructions = scope.Method.Body.Instructions;
instructions.Add(Instruction.Create(OpCodes.Ret));
Expand Down
42 changes: 14 additions & 28 deletions Cesium.CodeGen/Ir/TopLevel/TopLevelDeclaration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ private static void EmitScopedIdentifier(

foreach (var (declaration, initializer) in items)
{
var (type, identifier, parametersInfo, cliImportMemberName) = declaration;
var (type, identifier, cliImportMemberName) = declaration;
if (identifier == null)
throw new NotSupportedException($"Unnamed global symbol of type {type} is not supported.");

Expand All @@ -49,17 +49,20 @@ private static void EmitScopedIdentifier(
throw new NotSupportedException(
$"Initializer expression for a CLI import isn't supported: {initializer}.");

EmitCliImportDeclaration(context, identifier, parametersInfo, type, cliImportMemberName);
if (type is not FunctionType cliFunction)
throw new NotSupportedException($"CLI initializer should be a function for identifier {identifier}.");

EmitCliImportDeclaration(context, identifier, cliFunction, cliImportMemberName);
continue;
}

if (parametersInfo != null)
if (type is FunctionType functionType)
{
if (initializer != null)
throw new NotSupportedException(
$"Initializer expression for a function declaration isn't supported: {initializer}.");

EmitFunctionDeclaration(context, identifier, parametersInfo, type);
EmitFunctionDeclaration(context, identifier, functionType);
continue;
}

Expand All @@ -78,23 +81,23 @@ private static void EmitScopedIdentifier(
private static void EmitCliImportDeclaration(
TranslationUnitContext context,
string name,
ParametersInfo? parametersInfo,
IType returnType,
FunctionType functionType,
string memberName)
{
var method = context.MethodLookup(memberName);
if (method == null) throw new NotSupportedException($"Cannot find CLI-imported member {memberName}.");

// TODO[#93]: Verify method signature: {parametersIInfo, type}.
var (parametersInfo, returnType) = functionType;
// TODO[#93]: Verify method signature: {parametersInfo, type}.
context.Functions.Add(name, new FunctionInfo(parametersInfo, returnType, method, IsDefined: true));
}

private static void EmitFunctionDeclaration(
TranslationUnitContext context,
string identifier,
ParametersInfo parametersInfo,
IType returnType)
FunctionType functionType)
{
var (parametersInfo, returnType) = functionType;
var existingFunction = context.Functions.GetValueOrDefault(identifier);
if (existingFunction != null)
{
Expand All @@ -118,34 +121,17 @@ private static void EmitTypeDef(TranslationUnitContext context, TypeDefDeclarati
declaration.Deconstruct(out var types);
foreach (var typeDef in types)
{
var (type, identifier, parametersInfo, cliImportMemberName) = typeDef;
var (type, identifier, cliImportMemberName) = typeDef;
if (identifier == null)
throw new NotSupportedException($"Anonymous typedef not supported: {type}.");

if (parametersInfo != null)
GenerateFunctionType(context, identifier, parametersInfo, type);

if (cliImportMemberName != null)
throw new NotSupportedException($"typedef for CLI import not supported: {cliImportMemberName}.");

if (type is IGeneratedType t)
context.GenerateType(t, identifier);
else
throw new NotSupportedException($"Not supported type generation for type {type}.");
context.AddPlainType(type, identifier);
}
}

private static void GenerateFunctionType(
TranslationUnitContext context,
string identifier,
ParametersInfo parametersInfo,
IType returnType)
{
var type = new FunctionPointerType();

throw new NotImplementedException(
$"Function type not supported, yet: {identifier}: {parametersInfo}{returnType}");

context.AddType(type, identifier);
}
}
12 changes: 12 additions & 0 deletions Cesium.CodeGen/Ir/Types/FunctionType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Cesium.CodeGen.Contexts;
using Mono.Cecil;

namespace Cesium.CodeGen.Ir.Types;

internal record FunctionType(ParametersInfo? Parameters, IType ReturnType) : IType
{
public TypeReference Resolve(TranslationUnitContext context) =>
throw new NotImplementedException();

public int SizeInBytes => throw new NotImplementedException("Could not calculate size yet.");
}
9 changes: 0 additions & 9 deletions Cesium.CodeGen/Ir/Types/IGeneratedType.cs

This file was deleted.

11 changes: 11 additions & 0 deletions Cesium.CodeGen/Ir/Types/IType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,19 @@

namespace Cesium.CodeGen.Ir.Types;

/// <summary>An interface representing a C type.</summary>
/// <remarks>
/// Can be of two flavors: an <see cref="IGeneratedType"/> or a plain type that doesn't require any byte code to be
/// generated (a basic type, a pointer or a function pointer.
/// </remarks>
internal interface IType
{
TypeReference Resolve(TranslationUnitContext context);
int SizeInBytes { get; }
}

/// <summary>A generated type, i.e. a type that has some bytecode to be generated once.</summary>
internal interface IGeneratedType : IType
{
TypeDefinition Emit(string name, TranslationUnitContext context);
}
6 changes: 1 addition & 5 deletions Cesium.CodeGen/Ir/Types/StructType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,11 @@ public TypeDefinition Emit(string name, TranslationUnitContext context)

foreach (var member in _members)
{
var (type, identifier, parametersInfo, cliImportMemberName) = member;
var (type, identifier, 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}.");
Expand Down

0 comments on commit 64cb53f

Please sign in to comment.