diff --git a/compiler/NubLang/Ast/Node.cs b/compiler/NubLang/Ast/Node.cs index cb6974e..2e48c0a 100644 --- a/compiler/NubLang/Ast/Node.cs +++ b/compiler/NubLang/Ast/Node.cs @@ -581,9 +581,25 @@ public class SizeNode(List tokens, NubType targetType) : RValue(tokens, n } } -public class CastNode(List tokens, NubType type, ExpressionNode value) : RValue(tokens, type) +public class CastNode(List tokens, NubType type, ExpressionNode value, CastNode.Conversion conversionType) : RValue(tokens, type) { + public enum Conversion + { + IntToInt, + FloatToFloat, + IntToFloat, + FloatToInt, + + PointerToPointer, + PointerToUInt64, + UInt64ToPointer, + + ConstArrayToArray, + ConstArrayToSlice, + } + public ExpressionNode Value { get; } = value; + public Conversion ConversionType { get; } = conversionType; public override IEnumerable Children() { diff --git a/compiler/NubLang/Ast/TypeChecker.cs b/compiler/NubLang/Ast/TypeChecker.cs index 628a6d5..bf747fc 100644 --- a/compiler/NubLang/Ast/TypeChecker.cs +++ b/compiler/NubLang/Ast/TypeChecker.cs @@ -322,9 +322,9 @@ public sealed class TypeChecker return result; } - if (IsCastAllowed(result.Type, expectedType)) + if (IsCastAllowed(result.Type, expectedType, out var conversion)) { - return new CastNode(result.Tokens, expectedType, result); + return new CastNode(result.Tokens, expectedType, result, conversion); } } @@ -354,26 +354,39 @@ public sealed class TypeChecker return value; } - if (!IsCastAllowed(value.Type, expectedType, false)) + if (!IsCastAllowed(value.Type, expectedType, out var conversion, false)) { throw new CompileException(Diagnostic .Error($"Cannot cast from {value.Type} to {expectedType}") .Build()); } - return new CastNode(expression.Tokens, expectedType, value); + return new CastNode(expression.Tokens, expectedType, value, conversion); } - private static bool IsCastAllowed(NubType from, NubType to, bool strict = true) + private static bool IsCastAllowed(NubType from, NubType to, out CastNode.Conversion conversion, bool strict = true) { // note(nub31): Implicit casts switch (from) { case NubIntType fromInt when to is NubIntType toInt && fromInt.Width < toInt.Width: + { + conversion = CastNode.Conversion.IntToInt; + return true; + } case NubPointerType when to is NubPointerType { BaseType: NubVoidType }: + { + conversion = CastNode.Conversion.PointerToPointer; + return true; + } case NubConstArrayType constArrayType1 when to is NubArrayType arrayType && constArrayType1.ElementType == arrayType.ElementType: + { + conversion = CastNode.Conversion.ConstArrayToArray; + return true; + } case NubConstArrayType constArrayType3 when to is NubSliceType sliceType2 && constArrayType3.ElementType == sliceType2.ElementType: { + conversion = CastNode.Conversion.ConstArrayToSlice; return true; } } @@ -384,19 +397,44 @@ public sealed class TypeChecker switch (from) { case NubIntType when to is NubIntType: - case NubIntType when to is NubFloatType: - case NubFloatType when to is NubIntType: - case NubFloatType when to is NubFloatType: - case NubPointerType when to is NubPointerType: - case NubPointerType when to is NubIntType: - case NubIntType when to is NubPointerType: { + conversion = CastNode.Conversion.IntToInt; + return true; + } + case NubIntType when to is NubFloatType: + { + conversion = CastNode.Conversion.IntToFloat; + return true; + } + case NubFloatType when to is NubIntType: + { + conversion = CastNode.Conversion.FloatToInt; + return true; + } + case NubFloatType when to is NubFloatType: + { + conversion = CastNode.Conversion.FloatToFloat; + return true; + } + case NubPointerType when to is NubPointerType: + { + conversion = CastNode.Conversion.PointerToPointer; + return true; + } + case NubPointerType when to is NubIntType { Signed: false, Width: 64 }: + { + conversion = CastNode.Conversion.PointerToUInt64; + return true; + } + case NubIntType { Signed: false, Width: 64 } when to is NubPointerType: + { + conversion = CastNode.Conversion.UInt64ToPointer; return true; } } } - + conversion = default; return false; } diff --git a/compiler/NubLang/Generation/LlvmGenerator.cs b/compiler/NubLang/Generation/LlvmGenerator.cs index e87ccf2..d1a2828 100644 --- a/compiler/NubLang/Generation/LlvmGenerator.cs +++ b/compiler/NubLang/Generation/LlvmGenerator.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Text; using NubLang.Ast; using NubLang.Modules; @@ -156,9 +157,7 @@ public class LlvmGenerator EmitWhile(writer, whileNode); break; default: - { - throw new NotImplementedException(); - } + throw new ArgumentOutOfRangeException(nameof(statementNode)); } } @@ -590,73 +589,81 @@ public class LlvmGenerator private Tmp EmitCast(IndentedTextWriter writer, CastNode castNode) { var source = Unwrap(writer, EmitExpression(writer, castNode.Value)); - var sourceType = castNode.Value.Type; - var targetType = castNode.Type; - var result = NewTmp("cast"); - switch (sourceType, targetType) + switch (castNode.ConversionType) { - case (NubIntType sourceInt, NubIntType targetInt): + case CastNode.Conversion.IntToInt: { - if (sourceInt.Width < targetInt.Width) - { - var op = sourceInt.Signed ? "sext" : "zext"; - writer.WriteLine($"{result} = {op} {MapType(sourceType)} {source} to {MapType(targetType)}"); - } - else if (sourceInt.Width > targetInt.Width) - { - writer.WriteLine($"{result} = trunc {MapType(sourceType)} {source} to {MapType(targetType)}"); - } - else - { - writer.WriteLine($"{result} = bitcast {MapType(sourceType)} {source} to {MapType(targetType)}"); - } + var sourceInt = (NubIntType)castNode.Value.Type; + var targetInt = (NubIntType)castNode.Type; - break; - } - case (NubFloatType sourceFloat, NubFloatType targetFloat): - { - if (sourceFloat.Width < targetFloat.Width) - { - writer.WriteLine($"{result} = fpext {MapType(sourceType)} {source} to {MapType(targetType)}"); - } - else - { - writer.WriteLine($"{result} = fptrunc {MapType(sourceType)} {source} to {MapType(targetType)}"); - } + var op = sourceInt.Width < targetInt.Width + ? sourceInt.Signed + ? "sext" + : "zext" + : sourceInt.Width > targetInt.Width + ? "trunc" + : "bitcast"; + writer.WriteLine($"{result} = {op} {MapType(sourceInt)} {source} to {MapType(targetInt)}"); break; } - case (NubIntType intType, NubFloatType): + case CastNode.Conversion.FloatToFloat: { - var intToFloatOp = intType.Signed ? "sitofp" : "uitofp"; - writer.WriteLine($"{result} = {intToFloatOp} {MapType(sourceType)} {source} to {MapType(targetType)}"); + var sourceFloat = (NubFloatType)castNode.Value.Type; + var targetFloat = (NubFloatType)castNode.Type; + + var op = sourceFloat.Width < targetFloat.Width ? "fpext" : "fptrunc"; + writer.WriteLine($"{result} = {op} {MapType(sourceFloat)} {source} to {MapType(targetFloat)}"); break; } - case (NubFloatType, NubIntType targetInt): + case CastNode.Conversion.IntToFloat: { - var floatToIntOp = targetInt.Signed ? "fptosi" : "fptoui"; - writer.WriteLine($"{result} = {floatToIntOp} {MapType(sourceType)} {source} to {MapType(targetType)}"); + var sourceInt = (NubIntType)castNode.Value.Type; + var targetFloat = (NubFloatType)castNode.Type; + + var op = sourceInt.Signed ? "sitofp" : "uitofp"; + writer.WriteLine($"{result} = {op} {MapType(sourceInt)} {source} to {MapType(targetFloat)}"); break; } - case (NubPointerType, NubPointerType): - case (NubPointerType, NubIntType): - case (NubIntType, NubPointerType): + case CastNode.Conversion.FloatToInt: { - writer.WriteLine($"{result} = inttoptr {MapType(sourceType)} {source} to {MapType(targetType)}"); + var sourceFloat = (NubFloatType)castNode.Value.Type; + var targetInt = (NubIntType)castNode.Type; + + var op = targetInt.Signed ? "fptosi" : "fptoui"; + writer.WriteLine($"{result} = {op} {MapType(sourceFloat)} {source} to {MapType(targetInt)}"); break; } + case CastNode.Conversion.PointerToPointer: + case CastNode.Conversion.PointerToUInt64: + case CastNode.Conversion.UInt64ToPointer: + { + writer.WriteLine($"{result} = inttoptr {MapType(castNode.Value.Type)} {source} to {MapType(castNode.Type)}"); + break; + } + case CastNode.Conversion.ConstArrayToArray: + { + var sourceConstArrayType = (NubConstArrayType)castNode.Value.Type; + var targetArrayType = (NubArrayType)castNode.Type; + + writer.WriteLine($"{result} = getelementptr {MapType(sourceConstArrayType)}, {MapType(targetArrayType)} {source}, i32 0, i32 0"); + break; + } + case CastNode.Conversion.ConstArrayToSlice: + { + throw new NotImplementedException(); + } default: { - throw new NotImplementedException($"Cast from {sourceType} to {targetType} not implemented"); + throw new UnreachableException(); } } return new Tmp(result, castNode.Type, false); } - private Tmp EmitConstArrayIndexAccess(IndentedTextWriter writer, ConstArrayIndexAccessNode constArrayIndexAccessNode) { var arrayPtr = Unwrap(writer, EmitExpression(writer, constArrayIndexAccessNode.Target)); @@ -898,6 +905,7 @@ public class LlvmGenerator switch (unaryExpressionNode.Operator) { case UnaryOperator.Negate: + { switch (unaryExpressionNode.Operand.Type) { case NubIntType intType: @@ -907,15 +915,20 @@ public class LlvmGenerator writer.WriteLine($"{result} = fneg {MapType(floatType)} {operand}"); break; default: - throw new ArgumentOutOfRangeException(); + throw new UnreachableException(); } break; + } case UnaryOperator.Invert: + { writer.WriteLine($"{result} = xor i1 {operand}, true"); break; + } default: + { throw new ArgumentOutOfRangeException(); + } } return new Tmp(result, unaryExpressionNode.Type, false); diff --git a/examples/playgroud/main.nub b/examples/playgroud/main.nub index 020560f..f8cbe17 100644 --- a/examples/playgroud/main.nub +++ b/examples/playgroud/main.nub @@ -9,9 +9,12 @@ struct Test extern "main" func main(argc: i64, argv: [?]^i8) { - let x: Test = { - field = 23 - } + let x = [1, 2, 3] + + test(x) +} + +func test(arr: [?]i64) +{ - test::test() } \ No newline at end of file