Interfaces working?

This commit is contained in:
nub31
2025-08-13 00:41:28 +02:00
parent 853f57102c
commit 41edf3b64b
5 changed files with 119 additions and 58 deletions

View File

@@ -1,27 +1,32 @@
extern func puts(fmt: cstring)
interface Printable interface Printable
{ {
func print() func print()
} }
struct Human : Printable { struct Human : Printable
{
name: cstring name: cstring
func print_name()
{
puts(this^.name)
}
func print() func print()
{ {
puts("example") puts(this^.name)
} }
} }
func main(args: []cstring): i64 func main(args: []cstring): i64
{ {
let human: Human = alloc Human { let human = alloc Human {
name = "oliver" name = "oliver"
} }
human.print() human.print()
print_printable(human)
return 0 return 0
} }
func print_printable(printable: Printable)
{
printable.print()
}

View File

@@ -19,6 +19,7 @@ public partial class QBEGenerator
BinaryExpressionNode binaryExpression => EmitBinaryExpression(binaryExpression), BinaryExpressionNode binaryExpression => EmitBinaryExpression(binaryExpression),
FuncCallNode funcCallExpression => EmitFuncCall(funcCallExpression), FuncCallNode funcCallExpression => EmitFuncCall(funcCallExpression),
InterfaceFuncAccessNode interfaceFuncAccess => EmitInterfaceFuncAccess(interfaceFuncAccess), InterfaceFuncAccessNode interfaceFuncAccess => EmitInterfaceFuncAccess(interfaceFuncAccess),
InterfaceInitializerNode interfaceInitializer => EmitInterfaceInitializer(interfaceInitializer),
ExternFuncIdentNode externFuncIdent => EmitExternFuncIdent(externFuncIdent), ExternFuncIdentNode externFuncIdent => EmitExternFuncIdent(externFuncIdent),
LocalFuncIdentNode localFuncIdent => EmitLocalFuncIdent(localFuncIdent), LocalFuncIdentNode localFuncIdent => EmitLocalFuncIdent(localFuncIdent),
VariableIdentNode variableIdent => EmitVariableIdent(variableIdent), VariableIdentNode variableIdent => EmitVariableIdent(variableIdent),
@@ -329,15 +330,14 @@ public partial class QBEGenerator
{ {
destination = TmpName(); destination = TmpName();
var size = SizeOf(structInitializer.StructType); var size = SizeOf(structInitializer.StructType);
if (structDef.InterfaceImplementations.Any())
{
size += 8;
}
_writer.Indented($"{destination} =l alloc8 {size}"); _writer.Indented($"{destination} =l alloc8 {size}");
} }
// if (structDef.InterfaceImplementations.Any())
// {
// _writer.Indented($"storel {destination}, {StructVtableName(structDef.Name)}");
// }
foreach (var field in structDef.Fields) foreach (var field in structDef.Fields)
{ {
if (!structInitializer.Initializers.TryGetValue(field.Name, out var valueExpression)) if (!structInitializer.Initializers.TryGetValue(field.Name, out var valueExpression))
@@ -424,15 +424,50 @@ public partial class QBEGenerator
private Val EmitStructFuncAccess(StructFuncAccessNode structFuncAccess) private Val EmitStructFuncAccess(StructFuncAccessNode structFuncAccess)
{ {
var target = EmitExpression(structFuncAccess.Target); var target = EmitExpression(structFuncAccess.Target);
Debug.Assert(target.Kind == ValKind.Pointer);
var structDef = _definitionTable.LookupStruct(structFuncAccess.StructType.Name); var structDef = _definitionTable.LookupStruct(structFuncAccess.StructType.Name);
var func = StructFuncName(structDef.Name, structFuncAccess.Func); var func = StructFuncName(structDef.Name, structFuncAccess.Func);
return new Val(func, structFuncAccess.Type, ValKind.Direct, target); return new Val(func, structFuncAccess.Type, ValKind.Direct, target.Name);
} }
private Val EmitInterfaceFuncAccess(InterfaceFuncAccessNode interfaceFuncAccess) private Val EmitInterfaceFuncAccess(InterfaceFuncAccessNode interfaceFuncAccess)
{ {
throw new NotImplementedException(); var target = EmitUnwrap(EmitExpression(interfaceFuncAccess.Target));
var interfaceDef = _definitionTable.LookupInterface(interfaceFuncAccess.InterfaceType.Name);
var functionIndex = interfaceDef.Functions.ToList().FindIndex(x => x.Name == interfaceFuncAccess.FuncName);
var offset = functionIndex * 8;
var vtable = TmpName();
_writer.Indented($"{vtable} =l loadl {target}");
var funcOffset = TmpName();
_writer.Indented($"{funcOffset} =l add {vtable}, {offset}");
var func = TmpName();
_writer.Indented($"{func} =l loadl {funcOffset}");
var data = TmpName();
_writer.Indented($"{data} =l add {target}, 8");
return new Val(func, interfaceFuncAccess.Type, ValKind.Direct, data);
}
private Val EmitInterfaceInitializer(InterfaceInitializerNode interfaceInitializer)
{
var implementation = EmitUnwrap(EmitExpression(interfaceInitializer.Implementation));
var result = TmpName();
_writer.Indented($"{result} =l alloc8 16");
_writer.Indented($"storel {StructVtableName(interfaceInitializer.StructType.Name)}, {result}");
var objectPointer = TmpName();
_writer.Indented($"{objectPointer} =l add {result}, 8");
_writer.Indented($"storel {implementation}, {objectPointer}");
return new Val(result, interfaceInitializer.InterfaceType, ValKind.Direct);
} }
private Val EmitFuncCall(FuncCallNode funcCall) private Val EmitFuncCall(FuncCallNode funcCall)
@@ -444,8 +479,7 @@ public partial class QBEGenerator
if (expression.ThisArg != null) if (expression.ThisArg != null)
{ {
Debug.Assert(expression.ThisArg.Kind == ValKind.Pointer); parameterStrings.Add($"l {expression.ThisArg}");
parameterStrings.Add($"l {expression.ThisArg.Name}");
} }
foreach (var parameter in funcCall.Parameters) foreach (var parameter in funcCall.Parameters)

View File

@@ -346,11 +346,6 @@ public partial class QBEGenerator
return StructTypeName(structType.Name); return StructTypeName(structType.Name);
} }
if (complexType is InterfaceTypeNode interfaceType)
{
return InterfaceTypeName(interfaceType.Name);
}
return "l"; return "l";
} }
@@ -434,11 +429,6 @@ public partial class QBEGenerator
return StructTypeName(structType.Name); return StructTypeName(structType.Name);
} }
if (complexType is InterfaceTypeNode interfaceType)
{
return InterfaceTypeName(interfaceType.Name);
}
return "l"; return "l";
} }
} }
@@ -496,10 +486,10 @@ public partial class QBEGenerator
{ {
var offset = 0; var offset = 0;
if (structType.InterfaceImplementations.Any()) // if (structType.InterfaceImplementations.Any())
{ // {
offset = 8; // offset = 8;
} // }
foreach (var field in structType.Fields) foreach (var field in structType.Fields)
{ {
@@ -561,10 +551,10 @@ public partial class QBEGenerator
{ {
var offset = 0; var offset = 0;
if (structDef.InterfaceImplementations.Any()) // if (structDef.InterfaceImplementations.Any())
{ // {
offset = 8; // offset = 8;
} // }
foreach (var field in structDef.Fields) foreach (var field in structDef.Fields)
{ {
@@ -619,11 +609,6 @@ public partial class QBEGenerator
return $":{name}"; return $":{name}";
} }
private string InterfaceTypeName(string name)
{
return $":{name}";
}
private string StructFuncName(string structName, string funcName) private string StructFuncName(string structName, string funcName)
{ {
return $"${structName}_{funcName}"; return $"${structName}_{funcName}";
@@ -649,7 +634,7 @@ public class CStringLiteral(string value, string name)
public string Name { get; } = name; public string Name { get; } = name;
} }
public record Val(string Name, TypeNode Type, ValKind Kind, Val? ThisArg = null); public record Val(string Name, TypeNode Type, ValKind Kind, string? ThisArg = null);
public class Scope(Scope? parent = null) public class Scope(Scope? parent = null)
{ {

View File

@@ -55,3 +55,5 @@ public record InterfaceFuncAccessNode(TypeNode Type, InterfaceTypeNode Interface
public record StructInitializerNode(StructTypeNode 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); public record DereferenceNode(TypeNode Type, ExpressionNode Expression) : ExpressionNode(Type);
public record InterfaceInitializerNode(TypeNode Type, InterfaceTypeNode InterfaceType, StructTypeNode StructType, ExpressionNode Implementation) : ExpressionNode(Type);

View File

@@ -204,22 +204,36 @@ public sealed class TypeChecker
{ {
TypeNode? type = null; TypeNode? type = null;
var assignment = Optional<ExpressionNode>.Empty();
if (statement.Assignment.HasValue)
{
var boundValue = CheckExpression(statement.Assignment.Value, type);
assignment = boundValue;
type = boundValue.Type;
}
if (statement.ExplicitType.HasValue) if (statement.ExplicitType.HasValue)
{ {
type = CheckType(statement.ExplicitType.Value); type = CheckType(statement.ExplicitType.Value);
} }
var assignment = Optional<ExpressionNode>.Empty();
if (statement.Assignment.HasValue)
{
var boundValue = CheckExpression(statement.Assignment.Value, type);
assignment = boundValue;
if (type != null)
{
if (boundValue.Type != type)
{
throw new TypeCheckerException(Diagnostic.Error($"{boundValue.Type} is not assignable to {type}").Build());
}
}
else
{
if (type == null) if (type == null)
{ {
throw new NotImplementedException("Diagnostics not implemented"); type = boundValue.Type;
}
}
}
if (type == null)
{
throw new TypeCheckerException(Diagnostic.Error($"Unknown type of variable {statement.Name}").Build());
} }
Scope.Declare(new Variable(statement.Name, type)); Scope.Declare(new Variable(statement.Name, type));
@@ -234,7 +248,7 @@ public sealed class TypeChecker
private ExpressionNode CheckExpression(ExpressionSyntax node, TypeNode? expectedType = null) private ExpressionNode CheckExpression(ExpressionSyntax node, TypeNode? expectedType = null)
{ {
return node switch var result = node switch
{ {
AddressOfSyntax expression => CheckAddressOf(expression), AddressOfSyntax expression => CheckAddressOf(expression),
ArrowFuncSyntax expression => CheckArrowFunc(expression, expectedType), ArrowFuncSyntax expression => CheckArrowFunc(expression, expectedType),
@@ -250,6 +264,27 @@ public sealed class TypeChecker
UnaryExpressionSyntax expression => CheckUnaryExpression(expression), UnaryExpressionSyntax expression => CheckUnaryExpression(expression),
_ => throw new ArgumentOutOfRangeException(nameof(node)) _ => throw new ArgumentOutOfRangeException(nameof(node))
}; };
if (expectedType != null && result.Type != expectedType)
{
return CheckConversion(expectedType, result);
}
return result;
}
private ExpressionNode CheckConversion(TypeNode targetType, ExpressionNode expression)
{
// todo(nub): Add conversions for primitive types such as i32 -> i64 etc.
switch (expression.Type)
{
case StructTypeNode structType when targetType is InterfaceTypeNode interfaceType:
{
return new InterfaceInitializerNode(interfaceType, interfaceType, structType, expression);
}
}
throw new TypeCheckerException(Diagnostic.Error($"Cannot convert {expression.Type} to {targetType}").Build());
} }
private AddressOfNode CheckAddressOf(AddressOfSyntax expression) private AddressOfNode CheckAddressOf(AddressOfSyntax expression)
@@ -401,14 +436,14 @@ public sealed class TypeChecker
{ {
var boundExpression = CheckExpression(expression.Target); var boundExpression = CheckExpression(expression.Target);
if (boundExpression.Type is InterfaceTypeNode customType) if (boundExpression.Type is InterfaceTypeNode interfaceType)
{ {
var interfaces = _definitionTable.LookupInterface(customType.Name).ToArray(); var interfaces = _definitionTable.LookupInterface(interfaceType.Name).ToArray();
if (interfaces.Length > 0) if (interfaces.Length > 0)
{ {
if (interfaces.Length > 1) if (interfaces.Length > 1)
{ {
throw new TypeCheckerException(Diagnostic.Error($"Interface {customType} has multiple definitions").Build()); throw new TypeCheckerException(Diagnostic.Error($"Interface {interfaceType} has multiple definitions").Build());
} }
var @interface = interfaces[0]; var @interface = interfaces[0];
@@ -418,7 +453,7 @@ public sealed class TypeChecker
{ {
if (interfaceFuncs.Length > 1) if (interfaceFuncs.Length > 1)
{ {
throw new TypeCheckerException(Diagnostic.Error($"Interface {customType} has multiple functions with the name {expression.Member}").Build()); throw new TypeCheckerException(Diagnostic.Error($"Interface {interfaceType} has multiple functions with the name {expression.Member}").Build());
} }
var interfaceFunc = interfaceFuncs[0]; var interfaceFunc = interfaceFuncs[0];
@@ -426,7 +461,7 @@ public sealed class TypeChecker
var returnType = CheckType(interfaceFunc.Signature.ReturnType); var returnType = CheckType(interfaceFunc.Signature.ReturnType);
var parameterTypes = interfaceFunc.Signature.Parameters.Select(p => CheckType(p.Type)).ToList(); var parameterTypes = interfaceFunc.Signature.Parameters.Select(p => CheckType(p.Type)).ToList();
var type = new FuncTypeNode(parameterTypes, returnType); var type = new FuncTypeNode(parameterTypes, returnType);
return new InterfaceFuncAccessNode(type, customType, boundExpression, expression.Member); return new InterfaceFuncAccessNode(type, interfaceType, boundExpression, expression.Member);
} }
} }
} }