This commit is contained in:
nub31
2025-08-12 20:55:35 +02:00
parent f31c16bbac
commit 9dabf0d2f7
7 changed files with 248 additions and 174 deletions

View File

@@ -48,7 +48,7 @@ public partial class QBEGenerator
var elementType = ((ArrayTypeNode)arrayIndexAccess.Target.Type).ElementType;
var pointer = TmpName();
_writer.Indented($"{pointer} =l mul {index}, {elementType.Size(_definitionTable)}");
_writer.Indented($"{pointer} =l mul {index}, {SizeOf(elementType)}");
_writer.Indented($"{pointer} =l add {pointer}, 8");
_writer.Indented($"{pointer} =l add {array}, {pointer}");
return new Val(pointer, arrayIndexAccess.Type, ValKind.Pointer);
@@ -81,7 +81,7 @@ public partial class QBEGenerator
private Val EmitArrayInitializer(ArrayInitializerNode arrayInitializer)
{
var capacity = EmitUnwrap(EmitExpression(arrayInitializer.Capacity));
var elementSize = arrayInitializer.ElementType.Size(_definitionTable);
var elementSize = SizeOf(arrayInitializer.ElementType);
var capacityInBytes = TmpName();
_writer.Indented($"{capacityInBytes} =l mul {capacity}, {elementSize}");
@@ -291,7 +291,7 @@ public partial class QBEGenerator
}
case LiteralKind.String:
{
if (literal.Type is NubStringTypeNode)
if (literal.Type is StringTypeNode)
{
var stringLiteral = new StringLiteral(literal.Value, StringName());
_stringLiterals.Add(stringLiteral);
@@ -328,7 +328,7 @@ public partial class QBEGenerator
if (destination == null)
{
destination = TmpName();
var size = structInitializer.StructType.Size(_definitionTable);
var size = SizeOf(structInitializer.StructType);
if (structDef.InterfaceImplementations.Any())
{
@@ -413,7 +413,7 @@ public partial class QBEGenerator
_writer.Indented($"{output} =l add {target}, {offset}");
// If the accessed member is an inline struct, it will not be a pointer
if (structFieldAccess.Type is CustomTypeNode customType && customType.Kind(_definitionTable) == CustomTypeKind.Struct)
if (structFieldAccess.Type is StructTypeNode)
{
return new Val(output, structFieldAccess.Type, ValKind.Direct);
}

View File

@@ -189,7 +189,7 @@ public partial class QBEGenerator
{
var size = TmpName();
_writer.Indented($"{size} =l loadl {array}");
_writer.Indented($"{size} =l mul {size}, {type.ElementType.Size(_definitionTable)}");
_writer.Indented($"{size} =l mul {size}, {SizeOf(type.ElementType)}");
_writer.Indented($"{size} =l add {size}, 8");
return size;
}
@@ -250,9 +250,9 @@ public partial class QBEGenerator
}
else
{
if (complexType is CustomTypeNode customType)
if (complexType is StructTypeNode structType)
{
EmitMemcpy(value, destinationPointer, customType.Size(_definitionTable).ToString());
EmitMemcpy(value, destinationPointer, SizeOf(structType).ToString());
}
else
{
@@ -260,7 +260,8 @@ public partial class QBEGenerator
{
ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
CStringTypeNode => EmitCStringSizeInBytes(value),
NubStringTypeNode => EmitStringSizeInBytes(value),
StringTypeNode => EmitStringSizeInBytes(value),
InterfaceTypeNode => 16.ToString(),
_ => throw new ArgumentOutOfRangeException(nameof(source.Type))
};
@@ -309,8 +310,9 @@ public partial class QBEGenerator
{
ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
CStringTypeNode => EmitCStringSizeInBytes(value),
NubStringTypeNode => EmitStringSizeInBytes(value),
CustomTypeNode customType => customType.Size(_definitionTable).ToString(),
StringTypeNode => EmitStringSizeInBytes(value),
InterfaceTypeNode => 16.ToString(),
StructTypeNode structType => SizeOf(structType).ToString(),
_ => throw new ArgumentOutOfRangeException(nameof(source.Type))
};
@@ -339,9 +341,14 @@ public partial class QBEGenerator
};
}
if (complexType is CustomTypeNode customType)
if (complexType is StructTypeNode structType)
{
return CustomTypeName(customType.Name);
return StructTypeName(structType.Name);
}
if (complexType is InterfaceTypeNode interfaceType)
{
return InterfaceTypeName(interfaceType.Name);
}
return "l";
@@ -387,7 +394,7 @@ public partial class QBEGenerator
private void EmitStructTypeDefinition(StructNode structDef)
{
_writer.WriteLine($"type {CustomTypeName(structDef.Name)} = {{ ");
_writer.WriteLine($"type {StructTypeName(structDef.Name)} = {{ ");
var types = new Dictionary<string, string>();
@@ -422,9 +429,14 @@ public partial class QBEGenerator
};
}
if (complexType is CustomTypeNode customType)
if (complexType is StructTypeNode structType)
{
return CustomTypeName(customType.Name);
return StructTypeName(structType.Name);
}
if (complexType is InterfaceTypeNode interfaceType)
{
return InterfaceTypeName(interfaceType.Name);
}
return "l";
@@ -458,6 +470,93 @@ public partial class QBEGenerator
};
}
private static int SizeOf(TypeNode type)
{
return type switch
{
SimpleTypeNode simple => simple.StorageSize switch
{
StorageSize.Void => 0,
StorageSize.I8 or StorageSize.U8 => 1,
StorageSize.I16 or StorageSize.U16 => 2,
StorageSize.I32 or StorageSize.U32 or StorageSize.F32 => 4,
StorageSize.I64 or StorageSize.U64 or StorageSize.F64 => 8,
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown storage size: {simple.StorageSize}")
},
CStringTypeNode => 8,
StringTypeNode => 8,
ArrayTypeNode => 8,
StructTypeNode structType => CalculateStructSize(structType),
InterfaceTypeNode => 16,
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
};
}
private static int CalculateStructSize(StructTypeNode structType)
{
var offset = 0;
if (structType.InterfaceImplementations.Any())
{
offset = 8;
}
foreach (var field in structType.Fields)
{
var fieldAlignment = AlignmentOf(field);
offset = AlignTo(offset, fieldAlignment);
offset += SizeOf(field);
}
var structAlignment = CalculateStructAlignment(structType);
return AlignTo(offset, structAlignment);
}
private static int AlignmentOf(TypeNode type)
{
return type switch
{
SimpleTypeNode simple => simple.StorageSize switch
{
StorageSize.Void => 1,
StorageSize.I8 or StorageSize.U8 => 1,
StorageSize.I16 or StorageSize.U16 => 2,
StorageSize.I32 or StorageSize.U32 or StorageSize.F32 => 4,
StorageSize.I64 or StorageSize.U64 or StorageSize.F64 => 8,
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown storage size: {simple.StorageSize}")
},
CStringTypeNode => 8,
StringTypeNode => 8,
ArrayTypeNode => 8,
StructTypeNode structType => CalculateStructAlignment(structType),
InterfaceTypeNode => 8,
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
};
}
private static int CalculateStructAlignment(StructTypeNode structType)
{
var maxAlignment = 1;
if (structType.InterfaceImplementations.Any())
{
maxAlignment = Math.Max(maxAlignment, 8);
}
foreach (var field in structType.Fields)
{
var fieldAlignment = AlignmentOf(field);
maxAlignment = Math.Max(maxAlignment, fieldAlignment);
}
return maxAlignment;
}
private static int AlignTo(int offset, int alignment)
{
return (offset + alignment - 1) & ~(alignment - 1);
}
private int OffsetOf(StructNode structDef, string member)
{
var offset = 0;
@@ -474,10 +573,10 @@ public partial class QBEGenerator
return offset;
}
var fieldAlignment = field.Type.Alignment(_definitionTable);
var fieldAlignment = AlignmentOf(field.Type);
offset = TypeNode.AlignTo(offset, fieldAlignment);
offset += field.Type.Size(_definitionTable);
offset = AlignTo(offset, fieldAlignment);
offset += SizeOf(field.Type);
}
throw new UnreachableException($"Member '{member}' not found in struct");
@@ -515,7 +614,12 @@ public partial class QBEGenerator
return $"${funcDef.CallName}";
}
private string CustomTypeName(string name)
private string StructTypeName(string name)
{
return $":{name}";
}
private string InterfaceTypeName(string name)
{
return $":{name}";
}

View File

@@ -26,11 +26,11 @@ public class DefinitionTable
.Where(x => x.Name == name);
}
public IEnumerable<StructSyntax> LookupStruct(CustomTypeNode type)
public IEnumerable<StructSyntax> LookupStruct(string name)
{
return _definitions
.OfType<StructSyntax>()
.Where(x => x.Name == type.Name);
.Where(x => x.Name == name);
}
public IEnumerable<StructFieldSyntax> LookupStructField(StructSyntax @struct, string field)
@@ -43,11 +43,11 @@ public class DefinitionTable
return @struct.Functions.Where(x => x.Name == func);
}
public IEnumerable<InterfaceSyntax> LookupInterface(CustomTypeNode type)
public IEnumerable<InterfaceSyntax> LookupInterface(string name)
{
return _definitions
.OfType<InterfaceSyntax>()
.Where(x => x.Name == type.Name);
.Where(x => x.Name == name);
}
public IEnumerable<InterfaceFuncSyntax> LookupInterfaceFunc(InterfaceSyntax @interface, string name)

View File

@@ -14,7 +14,7 @@ public record StructFieldNode(int Index, string Name, TypeNode Type, Optional<Ex
public record StructFuncNode(string Name, FuncSignatureNode Signature, BlockNode Body) : Node;
public record StructNode(string Name, IReadOnlyList<StructFieldNode> Fields, IReadOnlyList<StructFuncNode> Functions, IReadOnlyList<CustomTypeNode> InterfaceImplementations) : DefinitionNode;
public record StructNode(string Name, IReadOnlyList<StructFieldNode> Fields, IReadOnlyList<StructFuncNode> Functions, IReadOnlyList<InterfaceTypeNode> InterfaceImplementations) : DefinitionNode;
public record InterfaceFuncNode(string Name, FuncSignatureNode Signature) : Node;

View File

@@ -46,12 +46,12 @@ public record AddressOfNode(TypeNode Type, ExpressionNode Expression) : Expressi
public record LiteralNode(TypeNode Type, string Value, LiteralKind Kind) : ExpressionNode(Type);
public record StructFieldAccessNode(TypeNode Type, CustomTypeNode StructType, ExpressionNode Target, string Field) : ExpressionNode(Type);
public record StructFieldAccessNode(TypeNode Type, StructTypeNode StructType, ExpressionNode Target, string Field) : ExpressionNode(Type);
public record StructFuncAccessNode(TypeNode Type, CustomTypeNode StructType, ExpressionNode Target, string Func) : ExpressionNode(Type);
public record StructFuncAccessNode(TypeNode Type, StructTypeNode StructType, ExpressionNode Target, string Func) : ExpressionNode(Type);
public record InterfaceFuncAccessNode(TypeNode Type, CustomTypeNode InterfaceType, ExpressionNode Target, string FuncName) : ExpressionNode(Type);
public record InterfaceFuncAccessNode(TypeNode Type, InterfaceTypeNode InterfaceType, ExpressionNode Target, string FuncName) : ExpressionNode(Type);
public record StructInitializerNode(CustomTypeNode StructType, Dictionary<string, ExpressionNode> Initializers) : ExpressionNode(StructType);
public record StructInitializerNode(StructTypeNode StructType, Dictionary<string, ExpressionNode> Initializers) : ExpressionNode(StructType);
public record DereferenceNode(TypeNode Type, ExpressionNode Expression) : ExpressionNode(Type);

View File

@@ -1,11 +1,10 @@
using System.Diagnostics.CodeAnalysis;
using NubLang.Generation;
namespace NubLang.TypeChecking.Node;
public abstract class TypeNode : IEquatable<TypeNode>
{
public bool IsSimpleType([NotNullWhen(true)] out SimpleTypeNode? simpleType, [NotNullWhen(false)] out NubComplexTypeNode? complexType)
public bool IsSimpleType([NotNullWhen(true)] out SimpleTypeNode? simpleType, [NotNullWhen(false)] out ComplexTypeNode? complexType)
{
if (this is SimpleTypeNode st)
{
@@ -14,7 +13,7 @@ public abstract class TypeNode : IEquatable<TypeNode>
return true;
}
if (this is NubComplexTypeNode ct)
if (this is ComplexTypeNode ct)
{
complexType = ct;
simpleType = null;
@@ -24,14 +23,6 @@ public abstract class TypeNode : IEquatable<TypeNode>
throw new ArgumentException($"Type {this} is not a simple type nor a complex type");
}
public abstract int Size(TypedDefinitionTable definitionTable);
public abstract int Alignment(TypedDefinitionTable definitionTable);
public static int AlignTo(int offset, int alignment)
{
return (offset + alignment - 1) & ~(alignment - 1);
}
public override bool Equals(object? obj) => obj is TypeNode other && Equals(other);
public abstract bool Equals(TypeNode? other);
@@ -60,23 +51,6 @@ public enum StorageSize
public abstract class SimpleTypeNode : TypeNode
{
public abstract StorageSize StorageSize { get; }
public override int Size(TypedDefinitionTable definitionTable)
{
return StorageSize switch
{
StorageSize.I64 or StorageSize.U64 or StorageSize.F64 => 8,
StorageSize.I32 or StorageSize.U32 or StorageSize.F32 => 4,
StorageSize.I16 or StorageSize.U16 => 2,
StorageSize.I8 or StorageSize.U8 => 1,
_ => throw new ArgumentOutOfRangeException(nameof(StorageSize))
};
}
public override int Alignment(TypedDefinitionTable definitionTable)
{
return Size(definitionTable);
}
}
#region Simple types
@@ -136,7 +110,7 @@ public class BoolTypeNode : SimpleTypeNode
public override int GetHashCode() => HashCode.Combine(typeof(BoolTypeNode));
}
public class FuncTypeNode(List<TypeNode> parameters, TypeNode returnType) : SimpleTypeNode
public class FuncTypeNode(IReadOnlyList<TypeNode> parameters, TypeNode returnType) : SimpleTypeNode
{
public IReadOnlyList<TypeNode> Parameters { get; } = parameters;
public TypeNode ReturnType { get; } = returnType;
@@ -182,110 +156,50 @@ public class VoidTypeNode : SimpleTypeNode
#endregion
public abstract class NubComplexTypeNode : TypeNode;
public abstract class ComplexTypeNode : TypeNode;
#region Complex types
public class CStringTypeNode : NubComplexTypeNode
public class CStringTypeNode : ComplexTypeNode
{
public override int Size(TypedDefinitionTable definitionTable) => 8;
public override int Alignment(TypedDefinitionTable definitionTable) => Size(definitionTable);
public override string ToString() => "cstring";
public override bool Equals(TypeNode? other) => other is CStringTypeNode;
public override int GetHashCode() => HashCode.Combine(typeof(CStringTypeNode));
}
public class NubStringTypeNode : NubComplexTypeNode
public class StringTypeNode : ComplexTypeNode
{
public override int Size(TypedDefinitionTable definitionTable) => 8;
public override int Alignment(TypedDefinitionTable definitionTable) => Size(definitionTable);
public override string ToString() => "string";
public override bool Equals(TypeNode? other) => other is NubStringTypeNode;
public override int GetHashCode() => HashCode.Combine(typeof(NubStringTypeNode));
public override bool Equals(TypeNode? other) => other is StringTypeNode;
public override int GetHashCode() => HashCode.Combine(typeof(StringTypeNode));
}
public class CustomTypeNode(string name) : NubComplexTypeNode
public class StructTypeNode(string name, IReadOnlyList<TypeNode> fields, IReadOnlyList<FuncTypeNode> functions, IReadOnlyList<InterfaceTypeNode> interfaceImplementations) : ComplexTypeNode
{
public string Name { get; } = name;
public CustomTypeKind Kind(TypedDefinitionTable definitionTable)
{
if (definitionTable.GetStructs().Any(x => x.Name == Name))
{
return CustomTypeKind.Struct;
}
if (definitionTable.GetInterfaces().Any(x => x.Name == Name))
{
return CustomTypeKind.Interface;
}
throw new ArgumentException($"Definition table does not have any type information for {this}");
}
public override int Size(TypedDefinitionTable definitionTable)
{
switch (Kind(definitionTable))
{
case CustomTypeKind.Struct:
{
var structDef = definitionTable.LookupStruct(Name);
var size = 0;
var maxAlignment = 1;
foreach (var field in structDef.Fields)
{
var fieldAlignment = field.Type.Alignment(definitionTable);
maxAlignment = Math.Max(maxAlignment, fieldAlignment);
size = AlignTo(size, fieldAlignment);
size += field.Type.Size(definitionTable);
}
return AlignTo(size, maxAlignment);
}
case CustomTypeKind.Interface:
{
return 16;
}
default:
throw new ArgumentOutOfRangeException();
}
}
public override int Alignment(TypedDefinitionTable definitionTable)
{
switch (Kind(definitionTable))
{
case CustomTypeKind.Struct:
return definitionTable.LookupStruct(Name).Fields.Max(f => f.Type.Alignment(definitionTable));
case CustomTypeKind.Interface:
return 8;
default:
throw new ArgumentOutOfRangeException();
}
}
public IReadOnlyList<TypeNode> Fields { get; } = fields;
public IReadOnlyList<FuncTypeNode> Functions { get; } = functions;
public IReadOnlyList<InterfaceTypeNode> InterfaceImplementations { get; } = interfaceImplementations;
public override string ToString() => Name;
public override bool Equals(TypeNode? other) => other is CustomTypeNode custom && Name == custom.Name;
public override int GetHashCode() => HashCode.Combine(typeof(CustomTypeNode), Name);
public override bool Equals(TypeNode? other) => other is StructTypeNode custom && Name == custom.Name;
public override int GetHashCode() => HashCode.Combine(typeof(StructTypeNode), Name);
}
public enum CustomTypeKind
public class InterfaceTypeNode(string name, IReadOnlyList<FuncTypeNode> funcs) : ComplexTypeNode
{
Struct,
Interface
public string Name { get; } = name;
public IReadOnlyList<FuncTypeNode> Funcs { get; } = funcs;
public override string ToString() => Name;
public override bool Equals(TypeNode? other) => other is InterfaceTypeNode custom && Name == custom.Name;
public override int GetHashCode() => HashCode.Combine(typeof(InterfaceTypeNode), Name);
}
public class ArrayTypeNode(TypeNode elementType) : NubComplexTypeNode
public class ArrayTypeNode(TypeNode elementType) : ComplexTypeNode
{
public TypeNode ElementType { get; } = elementType;
public override int Size(TypedDefinitionTable definitionTable) => 8;
public override int Alignment(TypedDefinitionTable definitionTable) => Size(definitionTable);
public override string ToString() => "[]" + ElementType;
public override bool Equals(TypeNode? other) => other is ArrayTypeNode array && ElementType.Equals(array.ElementType);

View File

@@ -38,7 +38,7 @@ public sealed class TypeChecker
{
definitions.Add(CheckDefinition(definition));
}
catch (CheckException e)
catch (TypeCheckerException e)
{
_diagnostics.Add(e.Diagnostic);
}
@@ -100,32 +100,32 @@ public sealed class TypeChecker
funcs.Add(new StructFuncNode(func.Name, CheckFuncSignature(func.Signature), CheckFuncBody(func.Body, CheckType(func.Signature.ReturnType), parameters)));
}
var interfaceImplementations = new List<CustomTypeNode>();
var interfaceImplementations = new List<InterfaceTypeNode>();
foreach (var interfaceImplementation in node.InterfaceImplementations)
{
var interfaceType = CheckType(interfaceImplementation);
if (interfaceType is not CustomTypeNode customType)
var type = CheckType(interfaceImplementation);
if (type is not InterfaceTypeNode interfaceType)
{
_diagnostics.Add(Diagnostic.Error("Interface implementation is not a custom type").Build());
continue;
}
var interfaceDefs = _definitionTable.LookupInterface(customType).ToArray();
var interfaceDefs = _definitionTable.LookupInterface(interfaceType.Name).ToArray();
if (interfaceDefs.Length == 0)
{
_diagnostics.Add(Diagnostic.Error($"Interface {customType.Name} is not defined").Build());
_diagnostics.Add(Diagnostic.Error($"Interface {interfaceType.Name} is not defined").Build());
continue;
}
if (interfaceDefs.Length > 1)
{
_diagnostics.Add(Diagnostic.Error($"Interface {customType.Name} has multiple definitions").Build());
_diagnostics.Add(Diagnostic.Error($"Interface {interfaceType.Name} has multiple definitions").Build());
continue;
}
interfaceImplementations.Add(customType);
interfaceImplementations.Add(interfaceType);
}
return new StructNode(node.Name, structFields, funcs, interfaceImplementations);
@@ -262,12 +262,12 @@ public sealed class TypeChecker
{
if (expectedType == null)
{
throw new CheckException(Diagnostic.Error("Cannot infer argument types for arrow function").Build());
throw new TypeCheckerException(Diagnostic.Error("Cannot infer argument types for arrow function").Build());
}
if (expectedType is not FuncTypeNode funcType)
{
throw new CheckException(Diagnostic.Error($"Expected {expectedType}, but got arrow function").Build());
throw new TypeCheckerException(Diagnostic.Error($"Expected {expectedType}, but got arrow function").Build());
}
var parameters = new List<FuncParameterNode>();
@@ -276,7 +276,7 @@ public sealed class TypeChecker
{
if (i >= funcType.Parameters.Count)
{
throw new CheckException(Diagnostic.Error($"Arrow function expected a maximum of {funcType.Parameters.Count} arguments").Build());
throw new TypeCheckerException(Diagnostic.Error($"Arrow function expected a maximum of {funcType.Parameters.Count} arguments").Build());
}
var expectedParameterType = funcType.Parameters[i];
@@ -353,7 +353,7 @@ public sealed class TypeChecker
{
if (localFuncs.Length > 1)
{
throw new CheckException(Diagnostic.Error($"Extern func {expression.Name} has multiple definitions").Build());
throw new TypeCheckerException(Diagnostic.Error($"Extern func {expression.Name} has multiple definitions").Build());
}
var localFunc = localFuncs[0];
@@ -369,7 +369,7 @@ public sealed class TypeChecker
{
if (externFuncs.Length > 1)
{
throw new CheckException(Diagnostic.Error($"Extern func {expression.Name} has multiple definitions").Build());
throw new TypeCheckerException(Diagnostic.Error($"Extern func {expression.Name} has multiple definitions").Build());
}
var externFunc = externFuncs[0];
@@ -380,7 +380,7 @@ public sealed class TypeChecker
return new ExternFuncIdentNode(type, expression.Name);
}
throw new CheckException(Diagnostic.Error($"No identifier with the name {expression.Name} exists").Build());
throw new TypeCheckerException(Diagnostic.Error($"No identifier with the name {expression.Name} exists").Build());
}
private LiteralNode CheckLiteral(LiteralSyntax expression, TypeNode? expectedType = null)
@@ -389,7 +389,7 @@ public sealed class TypeChecker
{
LiteralKind.Integer => new IntTypeNode(true, 64),
LiteralKind.Float => new FloatTypeNode(64),
LiteralKind.String => new NubStringTypeNode(),
LiteralKind.String => new StringTypeNode(),
LiteralKind.Bool => new BoolTypeNode(),
_ => throw new ArgumentOutOfRangeException()
};
@@ -401,14 +401,14 @@ public sealed class TypeChecker
{
var boundExpression = CheckExpression(expression.Target);
if (boundExpression.Type is CustomTypeNode customType)
if (boundExpression.Type is InterfaceTypeNode customType)
{
var interfaces = _definitionTable.LookupInterface(customType).ToArray();
var interfaces = _definitionTable.LookupInterface(customType.Name).ToArray();
if (interfaces.Length > 0)
{
if (interfaces.Length > 1)
{
throw new CheckException(Diagnostic.Error($"Interface {customType} has multiple definitions").Build());
throw new TypeCheckerException(Diagnostic.Error($"Interface {customType} has multiple definitions").Build());
}
var @interface = interfaces[0];
@@ -418,7 +418,7 @@ public sealed class TypeChecker
{
if (interfaceFuncs.Length > 1)
{
throw new CheckException(Diagnostic.Error($"Interface {customType} has multiple functions with the name {expression.Member}").Build());
throw new TypeCheckerException(Diagnostic.Error($"Interface {customType} has multiple functions with the name {expression.Member}").Build());
}
var interfaceFunc = interfaceFuncs[0];
@@ -429,13 +429,16 @@ public sealed class TypeChecker
return new InterfaceFuncAccessNode(type, customType, boundExpression, expression.Member);
}
}
}
var structs = _definitionTable.LookupStruct(customType).ToArray();
if (boundExpression.Type is StructTypeNode structType)
{
var structs = _definitionTable.LookupStruct(structType.Name).ToArray();
if (structs.Length > 0)
{
if (structs.Length > 1)
{
throw new CheckException(Diagnostic.Error($"Struct {customType} has multiple definitions").Build());
throw new TypeCheckerException(Diagnostic.Error($"Struct {structType} has multiple definitions").Build());
}
var @struct = structs[0];
@@ -445,12 +448,12 @@ public sealed class TypeChecker
{
if (fields.Length > 1)
{
throw new CheckException(Diagnostic.Error($"Struct {customType} has multiple fields with the name {expression.Member}").Build());
throw new TypeCheckerException(Diagnostic.Error($"Struct {structType} has multiple fields with the name {expression.Member}").Build());
}
var field = fields[0];
return new StructFieldAccessNode(CheckType(field.Type), customType, boundExpression, expression.Member);
return new StructFieldAccessNode(CheckType(field.Type), structType, boundExpression, expression.Member);
}
var funcs = _definitionTable.LookupStructFunc(@struct, expression.Member).ToArray();
@@ -458,40 +461,40 @@ public sealed class TypeChecker
{
if (funcs.Length > 1)
{
throw new CheckException(Diagnostic.Error($"Struct {customType} has multiple functions with the name {expression.Member}").Build());
throw new TypeCheckerException(Diagnostic.Error($"Struct {structType} has multiple functions with the name {expression.Member}").Build());
}
var func = funcs[0];
var parameters = func.Signature.Parameters.Select(x => CheckType(x.Type)).ToList();
var returnType = CheckType(func.Signature.ReturnType);
return new StructFuncAccessNode(new FuncTypeNode(parameters, returnType), customType, boundExpression, expression.Member);
return new StructFuncAccessNode(new FuncTypeNode(parameters, returnType), structType, boundExpression, expression.Member);
}
}
}
throw new CheckException(Diagnostic.Error($"{boundExpression.Type} does not have a member with the name {expression.Member}").Build());
throw new TypeCheckerException(Diagnostic.Error($"{boundExpression.Type} does not have a member with the name {expression.Member}").Build());
}
private StructInitializerNode CheckStructInitializer(StructInitializerSyntax expression)
{
var boundType = CheckType(expression.StructType);
if (boundType is not CustomTypeNode structType)
if (boundType is not StructTypeNode structType)
{
throw new CheckException(Diagnostic.Error($"Cannot initialize non-struct type {expression.StructType}").Build());
throw new TypeCheckerException(Diagnostic.Error($"Cannot initialize non-struct type {expression.StructType}").Build());
}
var structs = _definitionTable.LookupStruct(structType).ToArray();
var structs = _definitionTable.LookupStruct(structType.Name).ToArray();
if (structs.Length == 0)
{
throw new CheckException(Diagnostic.Error($"Struct {structType} is not defined").Build());
throw new TypeCheckerException(Diagnostic.Error($"Struct {structType} is not defined").Build());
}
if (structs.Length > 1)
{
throw new CheckException(Diagnostic.Error($"Struct {structType} has multiple definitions").Build());
throw new TypeCheckerException(Diagnostic.Error($"Struct {structType} has multiple definitions").Build());
}
var @struct = structs[0];
@@ -504,12 +507,12 @@ public sealed class TypeChecker
if (fields.Length == 0)
{
throw new CheckException(Diagnostic.Error($"Struct {structType} does not have a field with the name {field}").Build());
throw new TypeCheckerException(Diagnostic.Error($"Struct {structType} does not have a field with the name {field}").Build());
}
if (fields.Length > 1)
{
throw new CheckException(Diagnostic.Error($"Struct {structType} has multiple fields with the name {field}").Build());
throw new TypeCheckerException(Diagnostic.Error($"Struct {structType} has multiple fields with the name {field}").Build());
}
initializers[field] = CheckExpression(initializer, CheckType(fields[0].Type));
@@ -632,16 +635,69 @@ public sealed class TypeChecker
ArrayTypeSyntax type => new ArrayTypeNode(CheckType(type.BaseType)),
BoolTypeSyntax => new BoolTypeNode(),
CStringTypeSyntax => new CStringTypeNode(),
CustomTypeSyntax type => new CustomTypeNode(type.Name),
CustomTypeSyntax type => CheckCustomType(type),
FloatTypeSyntax @float => new FloatTypeNode(@float.Width),
FuncTypeSyntax type => new FuncTypeNode(type.Parameters.Select(CheckType).ToList(), CheckType(type.ReturnType)),
IntTypeSyntax @int => new IntTypeNode(@int.Signed, @int.Width),
PointerTypeSyntax type => new PointerTypeNode(CheckType(type.BaseType)),
StringTypeSyntax => new NubStringTypeNode(),
StringTypeSyntax => new StringTypeNode(),
VoidTypeSyntax => new VoidTypeNode(),
_ => throw new ArgumentOutOfRangeException(nameof(node))
};
}
private TypeNode CheckCustomType(CustomTypeSyntax type)
{
var structs = _definitionTable.LookupStruct(type.Name).ToArray();
if (structs.Length > 0)
{
if (structs.Length > 1)
{
throw new TypeCheckerException(Diagnostic.Error($"Struct {type.Name} has multiple definitions").Build());
}
var @struct = structs[0];
var fields = @struct.Fields.Select(x => CheckType(x.Type)).ToList();
var funcs = @struct.Functions
.Select(x => new FuncTypeNode(x.Signature.Parameters.Select(p => CheckType(p.Type)).ToList(), CheckType(x.Signature.ReturnType)))
.ToList();
var interfaceImplementations = new List<InterfaceTypeNode>();
foreach (var structInterfaceImplementation in @struct.InterfaceImplementations)
{
var checkedInterfaceType = CheckType(structInterfaceImplementation);
if (checkedInterfaceType is not InterfaceTypeNode interfaceType)
{
throw new TypeCheckerException(Diagnostic.Error($"{type.Name} cannot implement non-interface type {checkedInterfaceType}").Build());
}
interfaceImplementations.Add(interfaceType);
}
return new StructTypeNode(type.Name, fields, funcs, interfaceImplementations);
}
var interfaces = _definitionTable.LookupInterface(type.Name).ToArray();
if (interfaces.Length > 0)
{
if (interfaces.Length > 1)
{
throw new TypeCheckerException(Diagnostic.Error($"Interface {type.Name} has multiple definitions").Build());
}
var @interface = interfaces[0];
var functions = @interface.Functions
.Select(x => new FuncTypeNode(x.Signature.Parameters.Select(y => CheckType(y.Type)).ToList(), CheckType(x.Signature.ReturnType)))
.ToList();
return new InterfaceTypeNode(type.Name, functions);
}
throw new TypeCheckerException(Diagnostic.Error($"Type {type.Name} is not defined").Build());
}
}
public record Variable(string Name, TypeNode Type);
@@ -672,11 +728,11 @@ public class Scope(Scope? parent = null)
}
}
public class CheckException : Exception
public class TypeCheckerException : Exception
{
public Diagnostic Diagnostic { get; }
public CheckException(Diagnostic diagnostic) : base(diagnostic.Message)
public TypeCheckerException(Diagnostic diagnostic) : base(diagnostic.Message)
{
Diagnostic = diagnostic;
}