diff --git a/example/src/main.nub b/example/src/main.nub index 516aaab..d648d1f 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -1,27 +1,32 @@ +extern func puts(fmt: cstring) + interface Printable { func print() } -struct Human : Printable { +struct Human : Printable +{ name: cstring - func print_name() - { - puts(this^.name) - } - func print() { - puts("example") + puts(this^.name) } } func main(args: []cstring): i64 { - let human: Human = alloc Human { + let human = alloc Human { name = "oliver" } + human.print() + print_printable(human) return 0 } + +func print_printable(printable: Printable) +{ + printable.print() +} \ No newline at end of file diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs index 8064180..1e68d2f 100644 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs +++ b/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs @@ -19,6 +19,7 @@ public partial class QBEGenerator BinaryExpressionNode binaryExpression => EmitBinaryExpression(binaryExpression), FuncCallNode funcCallExpression => EmitFuncCall(funcCallExpression), InterfaceFuncAccessNode interfaceFuncAccess => EmitInterfaceFuncAccess(interfaceFuncAccess), + InterfaceInitializerNode interfaceInitializer => EmitInterfaceInitializer(interfaceInitializer), ExternFuncIdentNode externFuncIdent => EmitExternFuncIdent(externFuncIdent), LocalFuncIdentNode localFuncIdent => EmitLocalFuncIdent(localFuncIdent), VariableIdentNode variableIdent => EmitVariableIdent(variableIdent), @@ -329,15 +330,14 @@ public partial class QBEGenerator { destination = TmpName(); var size = SizeOf(structInitializer.StructType); - - if (structDef.InterfaceImplementations.Any()) - { - size += 8; - } - _writer.Indented($"{destination} =l alloc8 {size}"); } + // if (structDef.InterfaceImplementations.Any()) + // { + // _writer.Indented($"storel {destination}, {StructVtableName(structDef.Name)}"); + // } + foreach (var field in structDef.Fields) { if (!structInitializer.Initializers.TryGetValue(field.Name, out var valueExpression)) @@ -424,15 +424,50 @@ public partial class QBEGenerator private Val EmitStructFuncAccess(StructFuncAccessNode structFuncAccess) { var target = EmitExpression(structFuncAccess.Target); + Debug.Assert(target.Kind == ValKind.Pointer); + var structDef = _definitionTable.LookupStruct(structFuncAccess.StructType.Name); 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) { - 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) @@ -444,8 +479,7 @@ public partial class QBEGenerator if (expression.ThisArg != null) { - Debug.Assert(expression.ThisArg.Kind == ValKind.Pointer); - parameterStrings.Add($"l {expression.ThisArg.Name}"); + parameterStrings.Add($"l {expression.ThisArg}"); } foreach (var parameter in funcCall.Parameters) diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs index cb2435d..718f449 100644 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -346,11 +346,6 @@ public partial class QBEGenerator return StructTypeName(structType.Name); } - if (complexType is InterfaceTypeNode interfaceType) - { - return InterfaceTypeName(interfaceType.Name); - } - return "l"; } @@ -434,11 +429,6 @@ public partial class QBEGenerator return StructTypeName(structType.Name); } - if (complexType is InterfaceTypeNode interfaceType) - { - return InterfaceTypeName(interfaceType.Name); - } - return "l"; } } @@ -496,10 +486,10 @@ public partial class QBEGenerator { var offset = 0; - if (structType.InterfaceImplementations.Any()) - { - offset = 8; - } + // if (structType.InterfaceImplementations.Any()) + // { + // offset = 8; + // } foreach (var field in structType.Fields) { @@ -561,10 +551,10 @@ public partial class QBEGenerator { var offset = 0; - if (structDef.InterfaceImplementations.Any()) - { - offset = 8; - } + // if (structDef.InterfaceImplementations.Any()) + // { + // offset = 8; + // } foreach (var field in structDef.Fields) { @@ -619,11 +609,6 @@ public partial class QBEGenerator return $":{name}"; } - private string InterfaceTypeName(string name) - { - return $":{name}"; - } - private string StructFuncName(string structName, string funcName) { return $"${structName}_{funcName}"; @@ -649,7 +634,7 @@ public class CStringLiteral(string value, string 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) { diff --git a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs b/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs index 9d835d4..19becfe 100644 --- a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs +++ b/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs @@ -54,4 +54,6 @@ public record InterfaceFuncAccessNode(TypeNode Type, InterfaceTypeNode Interface public record StructInitializerNode(StructTypeNode StructType, Dictionary Initializers) : ExpressionNode(StructType); -public record DereferenceNode(TypeNode Type, ExpressionNode Expression) : ExpressionNode(Type); \ No newline at end of file +public record DereferenceNode(TypeNode Type, ExpressionNode Expression) : ExpressionNode(Type); + +public record InterfaceInitializerNode(TypeNode Type, InterfaceTypeNode InterfaceType, StructTypeNode StructType, ExpressionNode Implementation) : ExpressionNode(Type); diff --git a/src/compiler/NubLang/TypeChecking/TypeChecker.cs b/src/compiler/NubLang/TypeChecking/TypeChecker.cs index 2fdf35e..77d0537 100644 --- a/src/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/src/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -204,22 +204,36 @@ public sealed class TypeChecker { TypeNode? type = null; - var assignment = Optional.Empty(); - if (statement.Assignment.HasValue) - { - var boundValue = CheckExpression(statement.Assignment.Value, type); - assignment = boundValue; - type = boundValue.Type; - } - if (statement.ExplicitType.HasValue) { type = CheckType(statement.ExplicitType.Value); } + var assignment = Optional.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) + { + type = boundValue.Type; + } + } + } + if (type == null) { - throw new NotImplementedException("Diagnostics not implemented"); + throw new TypeCheckerException(Diagnostic.Error($"Unknown type of variable {statement.Name}").Build()); } Scope.Declare(new Variable(statement.Name, type)); @@ -234,7 +248,7 @@ public sealed class TypeChecker private ExpressionNode CheckExpression(ExpressionSyntax node, TypeNode? expectedType = null) { - return node switch + var result = node switch { AddressOfSyntax expression => CheckAddressOf(expression), ArrowFuncSyntax expression => CheckArrowFunc(expression, expectedType), @@ -250,6 +264,27 @@ public sealed class TypeChecker UnaryExpressionSyntax expression => CheckUnaryExpression(expression), _ => 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) @@ -401,14 +436,14 @@ public sealed class TypeChecker { 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 > 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]; @@ -418,7 +453,7 @@ public sealed class TypeChecker { 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]; @@ -426,7 +461,7 @@ public sealed class TypeChecker var returnType = CheckType(interfaceFunc.Signature.ReturnType); var parameterTypes = interfaceFunc.Signature.Parameters.Select(p => CheckType(p.Type)).ToList(); var type = new FuncTypeNode(parameterTypes, returnType); - return new InterfaceFuncAccessNode(type, customType, boundExpression, expression.Member); + return new InterfaceFuncAccessNode(type, interfaceType, boundExpression, expression.Member); } } }