...
This commit is contained in:
@@ -43,42 +43,97 @@ public class QBEGenerator
|
||||
_stringLiteralIndex = 0;
|
||||
_codeIsReachable = true;
|
||||
|
||||
_writer.Comment("========== Builtin functions ==========");
|
||||
|
||||
_writer.WriteLine("""
|
||||
function l $.cstring_len(l %str) {
|
||||
@start
|
||||
%count =l copy 0
|
||||
@loop
|
||||
%address =l add %str, %count
|
||||
%value =w loadub %address
|
||||
jnz %value, @continue, @end
|
||||
@continue
|
||||
%count =l add %count, 1
|
||||
jmp @loop
|
||||
@end
|
||||
ret %count
|
||||
}
|
||||
|
||||
function $.memcpy(l %source, l %destination, l %length) {
|
||||
@start
|
||||
%count =l copy 0
|
||||
@loop
|
||||
%condition =w cultl %count, %length
|
||||
jnz %condition, @continue, @end
|
||||
@continue
|
||||
%source_address =l add %source, %count
|
||||
%destination_address =l add %destination, %count
|
||||
%value =w loadub %source_address
|
||||
storeb %value, %destination_address
|
||||
%count =l add %count, 1
|
||||
jmp @loop
|
||||
@end
|
||||
ret
|
||||
}
|
||||
|
||||
function $.memset(l %destination, l %value, l %length) {
|
||||
@start
|
||||
%count =l copy 0
|
||||
@loop
|
||||
%condition =w cultl %count, %length
|
||||
jnz %condition, @continue, @end
|
||||
@continue
|
||||
%destination_address =l add %destination, %count
|
||||
storeb %value, %destination_address
|
||||
%count =l add %count, 1
|
||||
jmp @loop
|
||||
@end
|
||||
ret
|
||||
}
|
||||
|
||||
function l $.array_size(l %array) {
|
||||
@start
|
||||
%size =l loadl %array
|
||||
ret %size
|
||||
}
|
||||
""");
|
||||
|
||||
_writer.Comment("========== Referenced structs ==========");
|
||||
|
||||
foreach (var structType in _structTypes)
|
||||
{
|
||||
EmitStructType(structType);
|
||||
}
|
||||
|
||||
_writer.NewLine();
|
||||
|
||||
_writer.Comment("========== Struct definitions ==========");
|
||||
|
||||
foreach (var structDef in _definitions.OfType<StructNode>())
|
||||
{
|
||||
EmitStructDefinition(structDef);
|
||||
}
|
||||
|
||||
_writer.NewLine();
|
||||
_writer.Comment("========== Function definitions ==========");
|
||||
|
||||
foreach (var funcDef in _definitions.OfType<FuncNode>())
|
||||
{
|
||||
EmitFuncDefinition(funcDef);
|
||||
}
|
||||
|
||||
// foreach (var structDef in _definitions.OfType<StructNode>().Where(x => x.InterfaceImplementations.Count > 0))
|
||||
// {
|
||||
// _writer.Write($"data {StructVtableName(_module.Name, structDef.Name)} = {{ ");
|
||||
//
|
||||
// foreach (var interfaceImplementation in structDef.InterfaceImplementations)
|
||||
// {
|
||||
// var interfaceDef = _definitionTable.LookupInterface(interfaceImplementation.Name);
|
||||
// foreach (var func in interfaceDef.Functions)
|
||||
// {
|
||||
// _writer.Write($"l {StructFuncName(_module.Name, structDef.Name, func.Name)}, ");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// _writer.WriteLine("}");
|
||||
// }
|
||||
_writer.NewLine();
|
||||
_writer.Comment("========== cstring literals ==========");
|
||||
|
||||
foreach (var cStringLiteral in _cStringLiterals)
|
||||
{
|
||||
_writer.WriteLine($"data {cStringLiteral.Name} = {{ b \"{cStringLiteral.Value}\", b 0 }}");
|
||||
}
|
||||
|
||||
_writer.NewLine();
|
||||
_writer.Comment("========== string literals ==========");
|
||||
|
||||
foreach (var stringLiteral in _stringLiterals)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(stringLiteral.Value).Select(b => $"b {b}");
|
||||
@@ -163,67 +218,18 @@ public class QBEGenerator
|
||||
|
||||
private void EmitMemset(string destination, int value, string length)
|
||||
{
|
||||
var count = TmpName();
|
||||
_writer.Indented($"{count} =l copy 0");
|
||||
|
||||
var loopLabel = LabelName();
|
||||
_writer.WriteLine(loopLabel);
|
||||
|
||||
var continueLabel = LabelName();
|
||||
var doneLabel = LabelName();
|
||||
var condition = TmpName();
|
||||
_writer.Indented($"{condition} =w cultl {count}, {length}");
|
||||
_writer.Indented($"jnz {condition}, {continueLabel}, {doneLabel}");
|
||||
|
||||
_writer.WriteLine(continueLabel);
|
||||
|
||||
var destinationAddress = TmpName();
|
||||
_writer.Indented($"{destinationAddress} =l add {destination}, {count}");
|
||||
|
||||
_writer.Indented($"storeb {value}, {destinationAddress}");
|
||||
|
||||
_writer.Indented($"{count} =l add {count}, 1");
|
||||
_writer.Indented($"jmp {loopLabel}");
|
||||
|
||||
_writer.WriteLine(doneLabel);
|
||||
_writer.Indented($"call $.memset(l {destination}, l {value}, l {length})");
|
||||
}
|
||||
|
||||
private void EmitMemcpy(string source, string destination, string length)
|
||||
{
|
||||
var count = TmpName();
|
||||
_writer.Indented($"{count} =l copy 0");
|
||||
|
||||
var loopLabel = LabelName();
|
||||
_writer.WriteLine(loopLabel);
|
||||
|
||||
var continueLabel = LabelName();
|
||||
var doneLabel = LabelName();
|
||||
var condition = TmpName();
|
||||
_writer.Indented($"{condition} =w cultl {count}, {length}");
|
||||
_writer.Indented($"jnz {condition}, {continueLabel}, {doneLabel}");
|
||||
|
||||
_writer.WriteLine(continueLabel);
|
||||
|
||||
var sourceAddress = TmpName();
|
||||
_writer.Indented($"{sourceAddress} =l add {source}, {count}");
|
||||
|
||||
var destinationAddress = TmpName();
|
||||
_writer.Indented($"{destinationAddress} =l add {destination}, {count}");
|
||||
|
||||
var value = TmpName();
|
||||
_writer.Indented($"{value} =w loadub {sourceAddress}");
|
||||
_writer.Indented($"storeb {value}, {destinationAddress}");
|
||||
|
||||
_writer.Indented($"{count} =l add {count}, 1");
|
||||
_writer.Indented($"jmp {loopLabel}");
|
||||
|
||||
_writer.WriteLine(doneLabel);
|
||||
_writer.Indented($"call $.memcpy(l {source}, l {destination}, l {length})");
|
||||
}
|
||||
|
||||
private string EmitArraySizeInBytes(ArrayTypeNode type, string array)
|
||||
{
|
||||
var size = TmpName();
|
||||
_writer.Indented($"{size} =l loadl {array}");
|
||||
_writer.Indented($"{size} =l call $.array_size(l {array})");
|
||||
_writer.Indented($"{size} =l mul {size}, {SizeOf(type.ElementType)}");
|
||||
_writer.Indented($"{size} =l add {size}, 8");
|
||||
return size;
|
||||
@@ -231,27 +237,10 @@ public class QBEGenerator
|
||||
|
||||
private string EmitCStringSizeInBytes(string cstring)
|
||||
{
|
||||
var count = TmpName();
|
||||
_writer.Indented($"{count} =l copy 0");
|
||||
|
||||
var loopLabel = LabelName();
|
||||
_writer.WriteLine(loopLabel);
|
||||
|
||||
var address = TmpName();
|
||||
_writer.Indented($"{address} =l add {cstring}, {count}");
|
||||
|
||||
var value = TmpName();
|
||||
_writer.Indented($"{value} =w loadub {address}");
|
||||
|
||||
var notZeroLabel = LabelName();
|
||||
var zeroLabel = LabelName();
|
||||
_writer.Indented($"jnz {value}, {notZeroLabel}, {zeroLabel}");
|
||||
_writer.WriteLine(notZeroLabel);
|
||||
_writer.Indented($"{count} =l add {count}, 1");
|
||||
_writer.Indented($"jmp {loopLabel}");
|
||||
_writer.WriteLine(zeroLabel);
|
||||
|
||||
return count;
|
||||
var result = TmpName();
|
||||
_writer.Indented($"{result} =l call $.cstring_len(l {cstring})");
|
||||
_writer.Indented($"{result} =l add {result}, 1");
|
||||
return result;
|
||||
}
|
||||
|
||||
private string EmitStringSizeInBytes(string nubstring)
|
||||
@@ -367,82 +356,6 @@ public class QBEGenerator
|
||||
return "l";
|
||||
}
|
||||
|
||||
private void EmitFuncDefinition(FuncNode funcDef)
|
||||
{
|
||||
if (funcDef.Body == null) return;
|
||||
|
||||
_labelIndex = 0;
|
||||
_tmpIndex = 0;
|
||||
|
||||
_writer.Write(funcDef.ExternSymbol != null ? "export function " : "function ");
|
||||
|
||||
if (funcDef.Signature.ReturnType is not VoidTypeNode)
|
||||
{
|
||||
_writer.Write(FuncQBETypeName(funcDef.Signature.ReturnType) + ' ');
|
||||
}
|
||||
|
||||
_writer.Write(FuncName(funcDef.Module, funcDef.Name, funcDef.ExternSymbol));
|
||||
|
||||
_writer.Write("(");
|
||||
foreach (var parameter in funcDef.Signature.Parameters)
|
||||
{
|
||||
_writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}");
|
||||
}
|
||||
|
||||
_writer.WriteLine(") {");
|
||||
_writer.WriteLine("@start");
|
||||
|
||||
EmitBlock(funcDef.Body);
|
||||
|
||||
// Implicit return for void functions if no explicit return has been set
|
||||
if (funcDef.Signature.ReturnType is VoidTypeNode && funcDef.Body.Statements.LastOrDefault() is not ReturnNode)
|
||||
{
|
||||
_writer.Indented("ret");
|
||||
}
|
||||
|
||||
_writer.WriteLine("}");
|
||||
}
|
||||
|
||||
private void EmitStructDefinition(StructNode structDef)
|
||||
{
|
||||
// todo(nub31): Find a way do run the initializers from other modules where the definition is not available.
|
||||
// A constructor is probably the answer
|
||||
|
||||
foreach (var function in structDef.Functions)
|
||||
{
|
||||
_labelIndex = 0;
|
||||
_tmpIndex = 0;
|
||||
|
||||
_writer.Write("export function ");
|
||||
|
||||
if (function.Signature.ReturnType is not VoidTypeNode)
|
||||
{
|
||||
_writer.Write(FuncQBETypeName(function.Signature.ReturnType) + ' ');
|
||||
}
|
||||
|
||||
_writer.Write(StructFuncName(structDef.Module, structDef.Name, function.Name));
|
||||
|
||||
_writer.Write("(l %this, ");
|
||||
foreach (var parameter in function.Signature.Parameters)
|
||||
{
|
||||
_writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}, ");
|
||||
}
|
||||
|
||||
_writer.WriteLine(") {");
|
||||
_writer.WriteLine("@start");
|
||||
|
||||
EmitBlock(function.Body);
|
||||
|
||||
// Implicit return for void functions if no explicit return has been set
|
||||
if (function.Signature.ReturnType is VoidTypeNode && function.Body.Statements.LastOrDefault() is not ReturnNode)
|
||||
{
|
||||
_writer.Indented("ret");
|
||||
}
|
||||
|
||||
_writer.WriteLine("}");
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitStructType(StructTypeNode structType)
|
||||
{
|
||||
_writer.Write($"type {StructTypeName(structType.Module, structType.Name)} = {{ ");
|
||||
@@ -480,6 +393,99 @@ public class QBEGenerator
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitStructDefinition(StructNode structDef)
|
||||
{
|
||||
_writer.Comment($" ===== {structDef.Module}::{structDef.Name} =====");
|
||||
_writer.WriteLine($"export function {StructCtorName(structDef.Module, structDef.Name)}(l %struct) {{");
|
||||
_writer.WriteLine("@start");
|
||||
|
||||
foreach (var field in structDef.Fields)
|
||||
{
|
||||
if (field.Value.TryGetValue(out var value))
|
||||
{
|
||||
var offset = OffsetOf(structDef.StructType, field.Name);
|
||||
var destination = TmpName();
|
||||
_writer.Indented($"{destination} =l add %struct, {offset}");
|
||||
EmitCopyInto(value, destination);
|
||||
}
|
||||
}
|
||||
|
||||
_writer.Indented("ret");
|
||||
_writer.WriteLine("}");
|
||||
|
||||
foreach (var function in structDef.Functions)
|
||||
{
|
||||
_writer.Comment($" ===== {structDef.Module}::{structDef.Name}.{function.Name} =====");
|
||||
_labelIndex = 0;
|
||||
_tmpIndex = 0;
|
||||
|
||||
_writer.NewLine();
|
||||
_writer.Write("export function ");
|
||||
|
||||
if (function.Signature.ReturnType is not VoidTypeNode)
|
||||
{
|
||||
_writer.Write(FuncQBETypeName(function.Signature.ReturnType) + ' ');
|
||||
}
|
||||
|
||||
_writer.Write(StructFuncName(structDef.Module, structDef.Name, function.Name));
|
||||
|
||||
_writer.Write("(l %this, ");
|
||||
foreach (var parameter in function.Signature.Parameters)
|
||||
{
|
||||
_writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}, ");
|
||||
}
|
||||
|
||||
_writer.WriteLine(") {");
|
||||
_writer.WriteLine("@start");
|
||||
|
||||
EmitBlock(function.Body);
|
||||
|
||||
// Implicit return for void functions if no explicit return has been set
|
||||
if (function.Signature.ReturnType is VoidTypeNode && function.Body.Statements.LastOrDefault() is not ReturnNode)
|
||||
{
|
||||
_writer.Indented("ret");
|
||||
}
|
||||
|
||||
_writer.WriteLine("}");
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitFuncDefinition(FuncNode funcDef)
|
||||
{
|
||||
if (funcDef.Body == null) return;
|
||||
|
||||
_labelIndex = 0;
|
||||
_tmpIndex = 0;
|
||||
|
||||
_writer.Write(funcDef.ExternSymbol != null ? "export function " : "function ");
|
||||
|
||||
if (funcDef.Signature.ReturnType is not VoidTypeNode)
|
||||
{
|
||||
_writer.Write(FuncQBETypeName(funcDef.Signature.ReturnType) + ' ');
|
||||
}
|
||||
|
||||
_writer.Write(FuncName(funcDef.Module, funcDef.Name, funcDef.ExternSymbol));
|
||||
|
||||
_writer.Write("(");
|
||||
foreach (var parameter in funcDef.Signature.Parameters)
|
||||
{
|
||||
_writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}");
|
||||
}
|
||||
|
||||
_writer.WriteLine(") {");
|
||||
_writer.WriteLine("@start");
|
||||
|
||||
EmitBlock(funcDef.Body);
|
||||
|
||||
// Implicit return for void functions if no explicit return has been set
|
||||
if (funcDef.Signature.ReturnType is VoidTypeNode && funcDef.Body.Statements.LastOrDefault() is not ReturnNode)
|
||||
{
|
||||
_writer.Indented("ret");
|
||||
}
|
||||
|
||||
_writer.WriteLine("}");
|
||||
}
|
||||
|
||||
private void EmitBlock(BlockNode block)
|
||||
{
|
||||
foreach (var statement in block.Statements)
|
||||
@@ -1011,6 +1017,7 @@ public class QBEGenerator
|
||||
var destination = TmpName();
|
||||
var size = SizeOf(structInitializer.StructType);
|
||||
_writer.Indented($"{destination} =l alloc8 {size}");
|
||||
_writer.Indented($"call {StructCtorName(structInitializer.StructType.Module, structInitializer.StructType.Name)}(l {destination})");
|
||||
|
||||
foreach (var (field, value) in structInitializer.Initializers)
|
||||
{
|
||||
@@ -1361,27 +1368,27 @@ public class QBEGenerator
|
||||
|
||||
private string TmpName()
|
||||
{
|
||||
return $"%t.{++_tmpIndex}";
|
||||
return $"%.t{++_tmpIndex}";
|
||||
}
|
||||
|
||||
private string LabelName()
|
||||
{
|
||||
return $"@l.{++_labelIndex}";
|
||||
return $"@.l{++_labelIndex}";
|
||||
}
|
||||
|
||||
private string CStringName()
|
||||
{
|
||||
return $"$cstr.{++_cStringLiteralIndex}";
|
||||
return $"$.cstr{++_cStringLiteralIndex}";
|
||||
}
|
||||
|
||||
private string StringName()
|
||||
{
|
||||
return $"$str.{++_stringLiteralIndex}";
|
||||
return $"$.str{++_stringLiteralIndex}";
|
||||
}
|
||||
|
||||
private static string FuncName(string module, string name, string? externSymbol)
|
||||
{
|
||||
return $"${externSymbol ?? $"{module}.{name}"}";
|
||||
return $"${externSymbol ?? $".{module}.{name}"}";
|
||||
}
|
||||
|
||||
private static string StructTypeName(string module, string name)
|
||||
@@ -1389,9 +1396,14 @@ public class QBEGenerator
|
||||
return $":{module}.{name}";
|
||||
}
|
||||
|
||||
private static string StructCtorName(string module, string name)
|
||||
{
|
||||
return $"$.{module}.{name}.ctor";
|
||||
}
|
||||
|
||||
private static string StructFuncName(string module, string structName, string funcName)
|
||||
{
|
||||
return $"${module}.{structName}.func.{funcName}";
|
||||
return $"$.{module}.{structName}.func.{funcName}";
|
||||
}
|
||||
|
||||
private static string StructVtableName(string module, string structName)
|
||||
|
||||
@@ -12,8 +12,8 @@ public record StructFieldNode(string Name, TypeNode Type, Optional<ExpressionNod
|
||||
|
||||
public record StructFuncNode(string Name, FuncSignatureNode Signature, BlockNode Body) : Node;
|
||||
|
||||
public record StructNode(string Module, string Name, List<StructFieldNode> Fields, List<StructFuncNode> Functions, List<InterfaceTypeNode> InterfaceImplementations) : DefinitionNode(Module, Name);
|
||||
public record StructNode(StructTypeNode StructType, string Module, string Name, List<StructFieldNode> Fields, List<StructFuncNode> Functions, List<InterfaceTypeNode> InterfaceImplementations) : DefinitionNode(Module, Name);
|
||||
|
||||
public record InterfaceFuncNode(string Name, FuncSignatureNode Signature) : Node;
|
||||
|
||||
public record InterfaceNode(string Module, string Name, List<InterfaceFuncNode> Functions) : DefinitionNode(Module, Name);
|
||||
public record InterfaceNode(InterfaceTypeNode InterfaceType, string Module, string Name, List<InterfaceFuncNode> Functions) : DefinitionNode(Module, Name);
|
||||
@@ -200,11 +200,10 @@ public class StructTypeNode(string module, string name, List<StructTypeField> fi
|
||||
public override int GetHashCode() => HashCode.Combine(typeof(StructTypeNode), Name);
|
||||
}
|
||||
|
||||
public class InterfaceTypeFunc(string name, FuncTypeNode type, int index)
|
||||
public class InterfaceTypeFunc(string name, FuncTypeNode type)
|
||||
{
|
||||
public string Name { get; } = name;
|
||||
public FuncTypeNode Type { get; } = type;
|
||||
public int Index { get; } = index;
|
||||
}
|
||||
|
||||
public class InterfaceTypeNode(string module, string name, List<InterfaceTypeFunc> functions) : ComplexTypeNode
|
||||
|
||||
@@ -74,7 +74,17 @@ public sealed class TypeChecker
|
||||
.Select(function => new InterfaceFuncNode(function.Name, CheckFuncSignature(function.Signature)))
|
||||
.ToList();
|
||||
|
||||
return new InterfaceNode(_syntaxTree.Metadata.ModuleName, node.Name, functions);
|
||||
var functionTypes = new List<InterfaceTypeFunc>();
|
||||
foreach (var function in node.Functions)
|
||||
{
|
||||
var parameters = function.Signature.Parameters.Select(x => ResolveType(x.Type)).ToList();
|
||||
var funcType = new FuncTypeNode(parameters, ResolveType(function.Signature.ReturnType));
|
||||
functionTypes.Add(new InterfaceTypeFunc(function.Name, funcType));
|
||||
}
|
||||
|
||||
var type = new InterfaceTypeNode(_syntaxTree.Metadata.ModuleName, node.Name, functionTypes);
|
||||
|
||||
return new InterfaceNode(type, _syntaxTree.Metadata.ModuleName, node.Name, functions);
|
||||
}
|
||||
|
||||
private StructNode CheckStructDefinition(StructSyntax node)
|
||||
@@ -85,7 +95,7 @@ public sealed class TypeChecker
|
||||
var value = Optional.Empty<ExpressionNode>();
|
||||
if (field.Value.HasValue)
|
||||
{
|
||||
value = CheckExpression(field.Value.Value);
|
||||
value = CheckExpression(field.Value.Value, ResolveType(field.Type));
|
||||
}
|
||||
|
||||
fields.Add(new StructFieldNode(field.Name, ResolveType(field.Type), value));
|
||||
@@ -110,8 +120,8 @@ public sealed class TypeChecker
|
||||
var interfaceImplementations = new List<InterfaceTypeNode>();
|
||||
foreach (var interfaceImplementation in node.InterfaceImplementations)
|
||||
{
|
||||
var type = ResolveType(interfaceImplementation);
|
||||
if (type is not InterfaceTypeNode interfaceType)
|
||||
var resolvedType = ResolveType(interfaceImplementation);
|
||||
if (resolvedType is not InterfaceTypeNode interfaceType)
|
||||
{
|
||||
_diagnostics.Add(Diagnostic.Error($"Struct {node.Name} cannot implement non-struct type {interfaceImplementation}").At(interfaceImplementation).Build());
|
||||
continue;
|
||||
@@ -120,7 +130,34 @@ public sealed class TypeChecker
|
||||
interfaceImplementations.Add(interfaceType);
|
||||
}
|
||||
|
||||
return new StructNode(_syntaxTree.Metadata.ModuleName, node.Name, fields, functions, interfaceImplementations);
|
||||
var fieldTypes = node.Fields
|
||||
.Select(x => new StructTypeField(x.Name, ResolveType(x.Type), x.Value.HasValue))
|
||||
.ToList();
|
||||
|
||||
var functionTypes = new List<StructTypeFunc>();
|
||||
foreach (var function in node.Functions)
|
||||
{
|
||||
var parameters = function.Signature.Parameters.Select(x => ResolveType(x.Type)).ToList();
|
||||
var funcType = new FuncTypeNode(parameters, ResolveType(function.Signature.ReturnType));
|
||||
functionTypes.Add(new StructTypeFunc(function.Name, funcType));
|
||||
}
|
||||
|
||||
var interfaceImplementationTypes = new List<InterfaceTypeNode>();
|
||||
foreach (var interfaceImplementation in node.InterfaceImplementations)
|
||||
{
|
||||
var resolvedType = ResolveType(interfaceImplementation);
|
||||
if (resolvedType is not InterfaceTypeNode interfaceType)
|
||||
{
|
||||
_diagnostics.Add(Diagnostic.Error($"Struct {node.Name} cannot implement non-struct type {interfaceImplementation}").At(interfaceImplementation).Build());
|
||||
continue;
|
||||
}
|
||||
|
||||
interfaceImplementationTypes.Add(interfaceType);
|
||||
}
|
||||
|
||||
var type = new StructTypeNode(_syntaxTree.Metadata.ModuleName, node.Name, fieldTypes, functionTypes, interfaceImplementationTypes);
|
||||
|
||||
return new StructNode(type, _syntaxTree.Metadata.ModuleName, node.Name, fields, functions, interfaceImplementations);
|
||||
}
|
||||
|
||||
private FuncNode CheckFuncDefinition(FuncSyntax node)
|
||||
@@ -466,7 +503,18 @@ public sealed class TypeChecker
|
||||
|
||||
foreach (var initializer in expression.Initializers)
|
||||
{
|
||||
initializers.Add(initializer.Key, CheckExpression(initializer.Value));
|
||||
var typeField = structType.Fields.FirstOrDefault(x => x.Name == initializer.Key);
|
||||
if (typeField == null)
|
||||
{
|
||||
_diagnostics.AddRange(Diagnostic
|
||||
.Error($"Struct {structType.Name} does not have a field named {initializer.Key}")
|
||||
.At(initializer.Value)
|
||||
.Build());
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
initializers.Add(initializer.Key, CheckExpression(initializer.Value, typeField.Type));
|
||||
}
|
||||
|
||||
var missingFields = structType.Fields
|
||||
|
||||
Reference in New Issue
Block a user