From b1aa64cdcc156eefc94e2008f329b5a8163e28e7 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 20 Mar 2020 17:55:24 +0100 Subject: [PATCH] TypeInfo gained a span. Hex lexing fixed. Basic block returns fixed. Removal of Ast.exit. Enum parameter list parses. Enum analysis improved. Exit deduction improved. Switch over a bool allowed. Switch analysis improved. Added -% operator. Updated implicit casts. Use of bigint. Signed-unsigned comparisons. --- CMakeLists.txt | 2 +- resources/testfragments/comparisons.c3 | 178 ++++++++ resources/testfragments/helloworld.c3 | 2 +- resources/testfragments/super_simple.c3 | 149 ++++++- src/compiler/ast.c | 31 +- src/compiler/bigint.c | 61 ++- src/compiler/bigint.h | 11 +- src/compiler/casts.c | 534 ++++++++++++------------ src/compiler/compiler_internal.h | 102 +++-- src/compiler/enums.h | 22 +- src/compiler/lexer.c | 5 +- src/compiler/llvm_codegen_debug_info.c | 1 - src/compiler/llvm_codegen_expr.c | 214 ++++++++-- src/compiler/llvm_codegen_function.c | 6 +- src/compiler/llvm_codegen_stmt.c | 16 +- src/compiler/llvm_codegen_type.c | 5 +- src/compiler/number.c | 265 ++++++++++++ src/compiler/parse_expr.c | 87 ++-- src/compiler/parse_stmt.c | 1 - src/compiler/parser.c | 50 ++- src/compiler/sema_decls.c | 60 ++- src/compiler/sema_expr.c | 351 +++++++++++----- src/compiler/sema_stmts.c | 136 ++++-- src/compiler/sema_types.c | 6 +- src/compiler/types.c | 56 +-- 25 files changed, 1679 insertions(+), 672 deletions(-) create mode 100644 resources/testfragments/comparisons.c3 create mode 100644 src/compiler/number.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 244f62cd2..5b4c33b12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,7 +84,7 @@ add_executable(c3c src/compiler/llvm_codegen_type.c src/compiler/llvm_codegen_function.c src/build/builder.c - src/utils/toml.c src/build/project.c src/build/build_internal.h src/compiler/sema_name_resolution.c src/target_info/target_info.c src/compiler/parse_expr.c src/compiler/parser_internal.h src/compiler/parse_stmt.c src/compiler/sema_passes.c src/compiler/sema_internal.h src/compiler/sema_decls.c src/compiler/sema_types.c src/compiler/sema_stmts.c) + src/utils/toml.c src/build/project.c src/build/build_internal.h src/compiler/sema_name_resolution.c src/target_info/target_info.c src/compiler/parse_expr.c src/compiler/parser_internal.h src/compiler/parse_stmt.c src/compiler/sema_passes.c src/compiler/sema_internal.h src/compiler/sema_decls.c src/compiler/sema_types.c src/compiler/sema_stmts.c src/compiler/number.c) target_compile_options(c3c PRIVATE -Wimplicit-int -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter) diff --git a/resources/testfragments/comparisons.c3 b/resources/testfragments/comparisons.c3 new file mode 100644 index 000000000..76a4bc814 --- /dev/null +++ b/resources/testfragments/comparisons.c3 @@ -0,0 +1,178 @@ +module comparisons; + +func void test_signed() +{ + int a = 0; + int b = 1; + + bool ab_gt = a > b; + bool ab_ge = a >= b; + bool ab_le = a <= b; + bool ab_lt = a < b; + bool ab_ne = a != b; + bool ab_eq = a == b; + +} + +func void test_unsigned() +{ + uint a = 0; + uint b = 1; + + bool ab_gt = a > b; + bool ab_ge = a >= b; + bool ab_le = a <= b; + bool ab_lt = a < b; + bool ab_ne = a != b; + bool ab_eq = a == b; + +} + +extern func void printf(char *s); + +func void test_signedunsigned() +{ + char a = 0 - 1; + byte b = cast(a, byte); + + printf("Signed-unsigned -1 0xFF \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + + a = 1; + b = 1; + + printf("Signed-unsigned 1 1 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + + a = 1; + b = 4; + + printf("Signed-unsigned 1 4 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + + a = 4; + b = 1; + + printf("Signed-unsigned 4 1 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + + a = 4; + b = 129; + + printf("Signed-unsigned 4 129 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + + a = 0 - 4; + b = 129; + + printf("Signed-unsigned -4 129 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + +} + +func void test_unsignedsigned() +{ + int b = -1; + uint a = cast(b, uint); + + printf("Unsigned-signed 0xFFFFFFFF -1 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + + a = 1; + b = 1; + + printf("Unsigned-signed 1 1 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + + a = 4; + b = 1; + + printf("Unsigned-signed 4 1 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + + a = 1; + b = 4; + + printf("Unsigned-signed 1 4 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + + a = 0x8000_0001; + b = 4; + + printf("Unsigned-signed 0x8000_0001 4 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + + b = 0 - 4; + a = 0x8000_0001; + + printf("Unsigned-signed 0x8000_0001 -4 \n"); + if (a > b) printf("a > b\n"); + if (a >= b) printf("a >= b\n"); + if (a < b) printf("a < b\n"); + if (a <= b) printf("a <= b\n"); + if (a != b) printf("a != b\n"); + if (a == b) printf("a == b\n"); + +} + +func void main() +{ + test_signedunsigned(); + test_unsignedsigned(); + printf("-- Done ---\n"); +} \ No newline at end of file diff --git a/resources/testfragments/helloworld.c3 b/resources/testfragments/helloworld.c3 index 73fdd4334..4e1710d70 100644 --- a/resources/testfragments/helloworld.c3 +++ b/resources/testfragments/helloworld.c3 @@ -1,6 +1,6 @@ module helloworld; -func void printf(char *str); +extern func void printf(char *str); func void main() { diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index a84f2a372..fb6a3730a 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -38,6 +38,87 @@ struct Teob int oekfeo; } +enum EnumTest : long +{ + VALUE1 = 4, + VALUE2 +} + +enum EnumTestDefault +{ + VALUE, + VALUE2 +} + +enum EnumTestNoOverflowAfterLong : long +{ + VALUE = 0x7FFF_FFFF_FFFF_FFFE, + VALUE_NO_EXCEED +} + +enum EnumTestSmall : ushort +{ + VALUE = 0xFF, + VALUE2 = 0xFFFF +} + +enum EnumWithData : ushort (int a, char[] x, long b = 4) +{ + // Currently the args are ignored TODO! + TEST1(42, "hello", 328) = 3, + TEST2(12, "world") +} + + +/* ERRORS + +enum EnumWithErrorData : int (int +{ + TEST +} + +enum EnumWithErrorWithMissingName : int (int) +{ + TEST +} + +enum EnumTestNoOverflowAfterULong : ulong +{ + VALUE = 0xFFFF_FFFF_FFFF_FFFE, + VALUE_NO_EXCEED +} + +enum EnumTestOverflow +{ + VALUE = 0x80000000, +} + + +enum EnumTestOverflowAfter +{ + VALUE = 0x80000000 - 1, + VALUE_EXCEED +} + +enum EnumTestOverflowAfterLong : long +{ + VALUE = 0x7FFF_FFFF_FFFF_FFFF, + VALUE_EXCEED +} + +enum EnumTestOverflowAfterULong : ulong +{ + VALUE = 0xFFFF_FFFF_FFFF_FFFF, + VALUE_EXCEED +} + +enum EnumTestErrorType : float +{ + VALUE_BOOM +} + +*/ + error Error { BLURB, @@ -60,6 +141,71 @@ func int borok() throws { return 1; } +func void testNoReturn() +{ + int i = 0; +} + +func int testReturn() +{ + int i = 0; + return i; +} + +func int testReturnWithOtherAtEnd() +{ + int i = 0; + return i; + if (i == 10) i++; + i = i + 2; +} + +func int testReturnWithError() throws Error +{ + int i = 0; + throw Error.NO_SUCH_FILE; + i = i + 1; +} + +func int testReturnWithConditional() +{ + int i = 0; + if (i > 0) + { + return 1; + } + else + { + return 2; + } +} + +func int testReturnWithCondThrow() throws Error +{ + int i = 0; + if (i > 0) + { + throw Error.NO_SUCH_FILE; + } + else + { + throw Error.NO_SUCH_FILE; + } +} + +func int testReturnSwitch() +{ + int i = 0; + switch (i) + { + case 1: + return 2; + case 2: + return 3; + default: + return 4; + } +} func int barok() throws Error, OtherError { @@ -281,6 +427,7 @@ func int main(int x) { int efd = 9; int okfe = 1; + return 1; switch (int bobe = okfe > 0 ? 1 : 0) { case 0: @@ -304,7 +451,6 @@ func int main(int x) printf("default\n"); break; } - return 1; int aa = x++; int bb = x--; int cc = ++x; @@ -377,6 +523,7 @@ int i = 0; EX: printf("EX\n"); goto YEF; + return 1; } func void test2(int* x, int y, int z) diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 6aba30d4d..25b0c6a4e 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -256,6 +256,7 @@ UnaryOp unary_op[TOKEN_LAST + 1] = { [TOKEN_BIT_NOT] = UNARYOP_BITNEG, [TOKEN_NOT] = UNARYOP_NOT, [TOKEN_MINUS] = UNARYOP_NEG, + [TOKEN_MINUS_MOD] = UNARYOP_NEGMOD, [TOKEN_PLUSPLUS] = UNARYOP_INC, [TOKEN_MINUSMINUS] = UNARYOP_DEC, }; @@ -411,9 +412,6 @@ void fprint_type_recursive(FILE *file, Type *type, int indent) case TYPE_IXX: fprintf_indented(file, indent, "(comp time int)\n"); break; - case TYPE_UXX: - fprintf_indented(file, indent, "(comp time uint)\n"); - break; case TYPE_FXX: fprintf_indented(file, indent, "(comp time float)\n"); break; @@ -530,31 +528,8 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) break; case EXPR_CONST: fprintf_indented(file, indent, "(const "); - switch (expr->const_expr.type) - { - case CONST_NIL: - fprintf(file, "nil\n"); - break; - case CONST_BOOL: - fprintf(file, expr->const_expr.b ? "true\n" : "false\n"); - break; - case CONST_INT: - if (expr->type->type_kind >= TYPE_U8 && expr->type->type_kind <= TYPE_UXX) - { - fprintf(file, "%llu\n", (unsigned long long)expr->const_expr.i); - } - else - { - fprintf(file, "%lld\n", (long long)expr->const_expr.i); - } - break; - case CONST_FLOAT: - fprintf(file, "%Lf\n", expr->const_expr.f); - break; - case CONST_STRING: - fprintf(file, "%s\n", expr->const_expr.string.chars); - break; - } + expr_const_fprint(file, &expr->const_expr); + fprintf(file, "\n"); fprint_expr_common(file, expr, indent + 1); break; case EXPR_BINARY: diff --git a/src/compiler/bigint.c b/src/compiler/bigint.c index bf6ceb961..383bb520c 100644 --- a/src/compiler/bigint.c +++ b/src/compiler/bigint.c @@ -129,7 +129,7 @@ void bigint_init_bigint(BigInt *dest, const BigInt *src) { return bigint_init_unsigned(dest, 0); } - else if (src->digit_count == 1) + if (src->digit_count == 1) { dest->digit_count = 1; dest->digit = src->digit; @@ -1878,6 +1878,65 @@ void bigint_print(BigInt *bigint, uint64_t base) } } +const char *bigint_to_error_string(const BigInt *bigint, uint64_t base) +{ + if (bigint->digit_count == 0) + { + return "0"; + } + if (bigint->digit_count == 1 && base == 10) + { + char *res = NULL; + asprintf(&res, "%" PRIu64, bigint->digit); + return res; + } + size_t len = bigint->digit_count * 64; + char *start = malloc_arena(len); + char *buf = start; + + BigInt digit_bi = { 0 }; + BigInt a1 = { 0 }; + BigInt a2 = { 0 }; + + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, bigint); + + BigInt base_bi = { 0 }; + bigint_init_unsigned(&base_bi, 10); + + for (;;) + { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); + *(buf++) = digit_to_char(digit, false); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CMP_EQ) + { + break; + } + } + + // reverse + char *out = malloc_arena(buf - start + 2); + char *current = out; + if (bigint->is_negative) + { + *(current++) = '-'; + } + for (char *ptr = buf - 1; ptr >= start; ptr--) + { + *(current++) = *ptr; + } + *(current++) = '\0'; + return current; +} + void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base) { if (bigint->digit_count == 0) diff --git a/src/compiler/bigint.h b/src/compiler/bigint.h index 0f91b0865..b1cc8c68a 100644 --- a/src/compiler/bigint.h +++ b/src/compiler/bigint.h @@ -6,16 +6,6 @@ #include "compiler_internal.h" -typedef struct _BigInt -{ - unsigned digit_count; - bool is_negative; - union { - uint64_t digit; - uint64_t *digits; - }; -} BigInt; - typedef enum _CmpRes { CMP_LT, @@ -54,6 +44,7 @@ bool bigint_eql(BigInt a, BigInt b); CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2); CmpRes bigint_cmp_zero(const BigInt *op); uint32_t bigint_hash(BigInt x); +const char *bigint_to_error_string(const BigInt *bigint, uint64_t base); void bigint_print(BigInt *bigint, uint64_t base); void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base); uint64_t bigint_as_unsigned(const BigInt *bigint); diff --git a/src/compiler/casts.c b/src/compiler/casts.c index 175596cf3..07b06642b 100644 --- a/src/compiler/casts.c +++ b/src/compiler/casts.c @@ -3,9 +3,11 @@ // a copy of which can be found in the LICENSE file. #include "compiler_internal.h" +#include "bigint.h" #define EXIT_T_MISMATCH() return sema_type_mismatch(left, canonical, cast_type) - +#define IS_EXPLICT() +#define RETURN_NON_CONST_CAST(kind) do { if (left->expr_kind == EXPR_CONST) { insert_cast(left, kind, canonical); return true; } } while (0) static inline void insert_cast(Expr *expr, CastKind kind, Type *canonical) { @@ -48,33 +50,28 @@ bool erro(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ EXIT_T_MISMATCH(); } -bool ptxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +bool ptxi(Expr *left, Type *canonical, Type *type, CastType cast_type) { if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_NIL); - left->const_expr.type = sign_from_type(canonical); - left->const_expr.i = 0; - left->type = type; - return true; - } - insert_cast(left, CAST_PTRXI, canonical); + RETURN_NON_CONST_CAST(CAST_PTRXI); + + assert(left->const_expr.kind == TYPE_POINTER); + expr_const_set_int(&left->const_expr, 0, type->type_kind); + left->type = type; + return true; } -bool ptbo(Expr* left, Type *ignored, Type *canonical, Type *type, CastType cast_type) +bool ptbo(Expr *left, Type *canonical, Type *type, CastType cast_type) { if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_NIL); - left->const_expr.type = CONST_BOOL; - left->const_expr.b = false; - left->type = type; - return true; - } - insert_cast(left, CAST_PTRBOOL, canonical); + RETURN_NON_CONST_CAST(CAST_PTRBOOL); + + assert(left->const_expr.kind == TYPE_POINTER); + left->const_expr.b = false; + + left->type = type; + return true; } @@ -136,18 +133,15 @@ bool ptpt(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastTyp { return sema_type_mismatch(left, type, cast_type); } - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_NIL); - left->type = type; - return true; - } - insert_cast(left, CAST_PTRPTR, canonical); + RETURN_NON_CONST_CAST(CAST_PTRPTR); + assert(left->const_expr.kind == TYPE_POINTER); + left->type = type; return true; } bool strpt(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type) { + // TODO insert_cast(left, CAST_PTRPTR, canonical); return true; @@ -163,284 +157,272 @@ bool stpt(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ return true; } - -bool boxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +void const_int_to_fp_cast(Expr *left, Type *canonical, Type *type) { - if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) + long double f = bigint_as_float(&left->const_expr.i); + switch (canonical->type_kind) { - assert(left->const_expr.type == CONST_BOOL); - left->const_expr.type = CONST_INT; - left->const_expr.i = left->const_expr.b ? 1 : 0; - left->type = type; - return true; + case TYPE_F32: + left->const_expr.f = (float)f; + break; + case TYPE_F64: + left->const_expr.f = (double)f; + break; + default: + left->const_expr.f = f; + break; } - insert_cast(left, CAST_BOOLINT, canonical); + left->type = type; + left->const_expr.kind = canonical->type_kind; +} + +/** + * Bool into a signed or unsigned int. Explicit conversions only. + * @return true unless this is not an explicit conversion. + */ +bool boxi(Expr *left, Type *canonical, Type *type, CastType cast_type) +{ + if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH(); + RETURN_NON_CONST_CAST(CAST_BOOLINT); + assert(left->const_expr.kind == TYPE_BOOL); + expr_const_set_int(&left->const_expr, left->const_expr.b ? 1 : 0, canonical->type_kind); + left->type = type; return true; } -bool bofp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +/** + * Bool into a float. Explicit conversions only. + * @return true unless this is not an explicit conversion. + */ +bool bofp(Expr *left, Type *canonical, Type *type, CastType cast_type) { if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_NIL); - left->const_expr.type = CONST_FLOAT; - left->const_expr.f = left->const_expr.b ? 1.0 : 0.0; - left->type = type; - return true; - } - insert_cast(left, CAST_BOOLFP, canonical); + RETURN_NON_CONST_CAST(CAST_BOOLFP); + + assert(left->const_expr.kind == TYPE_BOOL); + expr_const_set_float(&left->const_expr, left->const_expr.b ? 1.0 : 0.0, canonical->type_kind); + left->type = type; return true; } -bool xibo(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +/** + * Convert from any into to bool. + * @return true for any implicit conversion except assign and assign add. + */ +bool xibo(Expr *left, Type *canonical, Type *type, CastType cast_type) { if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_INT); - left->const_expr.type = CONST_BOOL; - left->const_expr.b = left->const_expr.i != 0; - left->type = type; - return true; - } - insert_cast(left, CAST_INTBOOL, canonical); + RETURN_NON_CONST_CAST(CAST_INTBOOL); + + expr_const_set_bool(&left->const_expr, bigint_cmp_zero(&left->const_expr.i) != CMP_EQ); + left->type = type; return true; } -bool fpbo(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +/** + * Convert from any float to bool + * @return true for any implicit conversion except assign and assign add + */ +bool fpbo(Expr *left, Type *canonical, Type *type, CastType cast_type) { if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_FLOAT); - left->const_expr.type = CONST_BOOL; - left->const_expr.b = left->const_expr.f != 0; - left->type = type; - return true; - } - insert_cast(left, CAST_INTBOOL, canonical); + RETURN_NON_CONST_CAST(CAST_FPBOOL); + + expr_const_set_bool(&left->const_expr, left->const_expr.f != 0.0); + left->type = type; return true; } +/** + * Convert from any fp to fp + * @return true for all except implicit assign (but allowing assign add) + */ bool fpfp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) { bool is_narrowing = from->builtin.bytesize < canonical->builtin.bytesize && from->type_kind != TYPE_FXX; - // Is this correct? TODO if (is_narrowing && cast_type == CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_FLOAT); - if (type->type_kind == TYPE_F32) - { - left->const_expr.f = (float)left->const_expr.f; - } - left->type = type; - return true; - } - insert_cast(left, CAST_FPFP, canonical); + RETURN_NON_CONST_CAST(CAST_FPFP); + + expr_const_set_float(&left->const_expr, left->const_expr.f, canonical->type_kind); + left->type = type; return true; } -bool fpui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +/** + * Convert from any floating point to int + * @return true only on explicit conversions. + */ +bool fpxi(Expr *left, Type *canonical, Type *type, CastType cast_type) { if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) + RETURN_NON_CONST_CAST(CAST_FPUI); + + assert(canonical->type_kind >= TYPE_I8 && canonical->type_kind <= TYPE_U64); + long double d = left->const_expr.f; + BigInt temp; + if (canonical->type_kind >= TYPE_U8) { - assert(left->const_expr.type == CONST_FLOAT); - assert(canonical->type_kind >= TYPE_U8 && canonical->type_kind <= TYPE_U64); - left->const_expr.i = (uint64_t)left->const_expr.f; - left->type = type; - return true; + bigint_init_unsigned(&temp, (uint64_t)d); + bigint_truncate(&left->const_expr.i, &temp, canonical->builtin.bitsize, false); } - insert_cast(left, CAST_FPUI, canonical); + else + { + bigint_init_signed(&temp, (int64_t)d); + bigint_truncate(&left->const_expr.i, &temp, canonical->builtin.bitsize, true); + } + left->const_expr.kind = canonical->type_kind; + left->type = type; return true; } -bool fpsi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) + +/** + * Convert from compile time int to any signed or unsigned int + * @return true unless the conversion was lossy. + */ +bool ixxxi(Expr *left, Type *canonical, Type *type, CastType cast_type) { - if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) + bool is_signed = canonical->type_kind < TYPE_U8; + int bitsize = canonical->builtin.bitsize; + if (cast_is_implicit(cast_type) && !bigint_fits_in_bits(&left->const_expr.i, bitsize, is_signed)) { - assert(left->const_expr.type == CONST_FLOAT); - assert(canonical->type_kind >= TYPE_I8 && canonical->type_kind <= TYPE_I64); - left->const_expr.i = (uint64_t)((int64_t)left->const_expr.f); - left->type = type; - return true; + SEMA_ERROR(left, "'%s' does not fit into '%s'", expr_const_to_error_string(&left->const_expr), canonical->name); + return false; } - insert_cast(left, CAST_FPUI, canonical); + BigInt temp; + bigint_truncate(&temp, &left->const_expr.i, canonical->builtin.bitsize, is_signed); + left->const_expr.i = temp; + left->const_expr.kind = canonical->type_kind; + left->type = type; return true; } -static uint64_t type_mask[4] = { 0xFF, 0xFFFF, 0xFFFFFFFFU, 0xFFFFFFFFFFFFFFFFLLU}; -static int64_t int_type_max[4] = { INT8_MAX, INT16_MAX, INT32_MAX, INT64_MAX }; -static uint64_t uint_type_max[4] = { UINT8_MAX, UINT16_MAX, UINT32_MAX, UINT64_MAX }; -static int64_t int_type_min[4] = { INT8_MIN, INT16_MIN, INT32_MIN, INT64_MIN }; - +/** + * Cast signed int -> signed int + * @return true if this is a widening, an explicit cast or if it is an implicit assign add + */ bool sisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) { - bool is_narrowing = from->builtin.bytesize > canonical->builtin.bytesize && from->type_kind != TYPE_IXX; + bool is_narrowing = from->builtin.bytesize > canonical->builtin.bytesize; if (is_narrowing && (cast_type != CAST_TYPE_IMPLICIT_ASSIGN_ADD && cast_type != CAST_TYPE_EXPLICIT)) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) - { - int64_t i = (int64_t)left->const_expr.i; - int index = canonical->type_kind - TYPE_I8; - if (from->type_kind == TYPE_IXX) - { - if ((i > int_type_max[index]) || (i < int_type_min[index])) - { - SEMA_ERROR(left, "'%lld' does not fit into '%s'", i, canonical->name); - return false; - } - } - assert(left->const_expr.type == CONST_INT); - assert(canonical->type_kind >= TYPE_I8 && canonical->type_kind <= TYPE_I64); - uint64_t mask = type_mask[index]; - if (i < 0) - { - left->const_expr.i = ~((~((uint64_t)i)) & mask); - } - else - { - left->const_expr.i &= mask; - } - left->type = type; - return true; - } - assert(from->type_kind != TYPE_IXX); - insert_cast(left, CAST_SISI, canonical); - return true; -} - -bool siui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) -{ - bool is_narrowing = from->builtin.bytesize < canonical->builtin.bytesize && from->type_kind != TYPE_IXX; - if (is_narrowing && (cast_type != CAST_TYPE_IMPLICIT_ASSIGN_ADD && cast_type != CAST_TYPE_EXPLICIT)) EXIT_T_MISMATCH(); - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_INT); - assert(canonical->type_kind >= TYPE_U8 && canonical->type_kind <= TYPE_U64); - left->const_expr.i &= type_mask[canonical->type_kind - TYPE_U8]; - left->type = type; - return true; - } - insert_cast(left, CAST_SIUI, canonical); - return true; -} - -bool xiptr(Expr* left, Type *from, Type *canonical, Type *type) -{ - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_INT); - assert(canonical->type_kind >= TYPE_U8 && canonical->type_kind <= TYPE_U64); - assert(left->const_expr.i == 0); - left->const_expr.type = CONST_NIL; - left->type = type; - return true; - } - insert_cast(left, CAST_XIPTR, canonical); - return true; -} - -bool sifp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) -{ - // TODO - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_INT); - assert(canonical->type_kind >= TYPE_F32 && canonical->type_kind <= TYPE_FXX); - if (left->const_expr.i > (uint64_t)INT64_MAX) - { - left->const_expr.f = -((long double)(~left->const_expr.i)); - } - else - { - left->const_expr.f = left->const_expr.i; - } - left->const_expr.type = CONST_FLOAT; - left->type = type; - return true; - } - insert_cast(left, CAST_SIFP, canonical); - return true; -} - - -bool uisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) -{ - if (left->expr_kind == EXPR_CONST) - { - assert(canonical->type_kind >= TYPE_I8 && canonical->type_kind <= TYPE_I64); - int index = canonical->type_kind - TYPE_I8; - uint64_t mask = type_mask[index]; - if (cast_type != CAST_TYPE_EXPLICIT && from->type_kind == TYPE_UXX) - { - if (left->const_expr.i > (uint64_t)int_type_max[index]) - { - SEMA_ERROR(left, "Cannot implicitly convert value '%llu' into %s - it will not fit.", left->const_expr.i, type_to_error_string(type)); - return false; - } - } - if (left->const_expr.i > (uint64_t)INT64_MAX) - { - left->const_expr.i = ~((~left->const_expr.i) & mask); - } - else - { - left->const_expr.i &= mask; - } - left->type = type; - return true; - } - insert_cast(left, CAST_UISI, canonical); + + RETURN_NON_CONST_CAST(CAST_SISI); + + BigInt temp; + bigint_truncate(&temp, &left->const_expr.i, canonical->builtin.bitsize, true); + left->const_expr.i = temp; + left->const_expr.kind = canonical->type_kind; + left->type = type; return true; } +/** + * Cast unsigned int -> unsigned int + * @return true if this was not a narrowing implicit assign or narrowing implicit assign add + */ bool uiui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) { - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_INT && type_is_unsigned(from)); - assert(canonical->type_kind >= TYPE_U8 && canonical->type_kind <= TYPE_U64); - int index = canonical->type_kind - TYPE_U8; - if (cast_type != CAST_TYPE_EXPLICIT && from->type_kind == TYPE_UXX) - { - if (left->const_expr.i > uint_type_max[index]) - { - SEMA_ERROR(left, "Cannot implicitly convert value '%llu' into %s - it will not fit.", type_to_error_string(type)); - return false; - } - } - left->const_expr.i &= type_mask[index]; - left->type = type; - return true; - } - insert_cast(left, CAST_UIUI, canonical); + bool is_narrowing = from->builtin.bytesize > canonical->builtin.bytesize; + + if (is_narrowing && (cast_type != CAST_TYPE_IMPLICIT_ASSIGN_ADD && cast_type != CAST_TYPE_EXPLICIT)) EXIT_T_MISMATCH(); + + RETURN_NON_CONST_CAST(CAST_UIUI); + + BigInt temp; + bigint_truncate(&temp, &left->const_expr.i, canonical->builtin.bitsize, false); + left->const_expr.i = temp; + left->const_expr.kind = canonical->type_kind; + left->type = type; return true; } -bool uifp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +/** + * Cast unsigned int -> signed int + * @return true if this is an explicit cast or if it is an implicit assign add or if it is a widening cast. + */ +bool uisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) { - TODO - if (left->expr_kind == EXPR_CONST) - { - assert(left->const_expr.type == CONST_INT && type_is_unsigned(from)); - assert(canonical->type_kind >= TYPE_F32 && canonical->type_kind <= TYPE_F64); - left->const_expr.f = left->const_expr.i; - left->type = type; - return true; - } - insert_cast(left, CAST_UIFP, canonical); + bool is_widening = from->builtin.bytesize < canonical->builtin.bytesize; + + if (!is_widening && cast_type != CAST_TYPE_IMPLICIT_ASSIGN_ADD && cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH(); + + RETURN_NON_CONST_CAST(CAST_UISI); + + BigInt temp; + bigint_truncate(&temp, &left->const_expr.i, canonical->builtin.bitsize, true); + left->const_expr.i = temp; + left->const_expr.kind = canonical->type_kind; + left->type = type; return true; } +/* + * Cast signed int -> unsigned int + * @return true if this was an implicit add or or explicit cast. + */ +bool siui(Expr *left, Type *canonical, Type *type, CastType cast_type) +{ + if (cast_type != CAST_TYPE_IMPLICIT_ASSIGN_ADD && cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH(); + + RETURN_NON_CONST_CAST(CAST_SIUI); + + BigInt temp; + bigint_truncate(&temp, &left->const_expr.i, canonical->builtin.bitsize, false); + left->const_expr.i = temp; + left->const_expr.kind = canonical->type_kind; + left->type = type; + return true; +} + + +/** + * Cast a signed or unsigned integer -> floating point + * @return true always + */ +bool sifp(Expr *left, Type *canonical, Type *type) +{ + RETURN_NON_CONST_CAST(CAST_SIFP); + const_int_to_fp_cast(left, canonical, type); + return true; +} + +/** + * Cast a signed or unsigned integer -> floating point + * @return true always + */ +bool uifp(Expr *left, Type *canonical, Type *type) +{ + RETURN_NON_CONST_CAST(CAST_UIFP); + const_int_to_fp_cast(left, canonical, type); + return true; +} + +bool ixxfp(Expr *left, Type *canonical, Type *type, CastType cast_type) +{ + assert(type_is_float(canonical)); + assert(left->expr_kind == EXPR_CONST); + const_int_to_fp_cast(left, canonical, type); + return true; +} + +/** + * Convert a compile time into to a boolean. + * @return true always + */ +bool ixxbo(Expr *left, Type *type) +{ + assert(left->expr_kind == EXPR_CONST); + expr_const_set_bool(&left->const_expr, bigint_cmp_zero(&left->const_expr.i) != CMP_EQ); + left->type = type; + return true; +} + + bool ussi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) { if (type->type_kind != TYPE_ENUM) return sema_type_mismatch(left, type, CAST_TYPE_EXPLICIT); @@ -451,7 +433,7 @@ bool ussi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ // TODO Expr *value = left->identifier_expr.decl->enum_constant.expr; assert(value->expr_kind == EXPR_CONST); - assert(value->const_expr.type == CONST_INT); +// assert(value->const_expr.type == CONST_INT); left->const_expr.i = value->const_expr.i; // TODO narrowing } @@ -469,9 +451,21 @@ bool uius(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ TODO } -bool xipt(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +/** + * Cast comptime, signed or unsigned -> pointer. + * @return true unless the constant value evaluates to zero. + */ +bool xipt(Expr *left, Type *canonical, Type *type) { - TODO + RETURN_NON_CONST_CAST(CAST_XIPTR); + if (bigint_cmp_zero(&left->const_expr.i) != CMP_EQ) + { + SEMA_ERROR(left, "Only constants evaluating to zero can be cast to pointers."); + return false; + } + expr_const_set_nil(&left->const_expr); + left->type = type; + return true; } bool usus(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) @@ -547,8 +541,6 @@ bool cast_to_runtime(Expr *expr) { case TYPE_IXX: return cast(expr, type_long, CAST_TYPE_IMPLICIT); - case TYPE_UXX: - return cast(expr, type_ulong, CAST_TYPE_IMPLICIT); case TYPE_FXX: return cast(expr, type_double, CAST_TYPE_IMPLICIT); default: @@ -585,16 +577,15 @@ CastKind cast_to_bool_kind(Type *type) return CAST_ERROR; case TYPE_BOOL: UNREACHABLE + case TYPE_IXX: case TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: - case TYPE_IXX: case TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64: - case TYPE_UXX: return CAST_INTBOOL; case TYPE_F32: case TYPE_F64: @@ -618,44 +609,49 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type) case TYPE_META_TYPE: break; case TYPE_BOOL: - if (type_is_integer(canonical)) return boxi(expr, from_type, canonical, to_type, cast_type); - if (type_is_float(canonical)) return bofp(expr, from_type, canonical, to_type, cast_type); + // Bool may convert into integers and floats but only explicitly. + if (type_is_integer(canonical)) return boxi(expr, canonical, to_type, cast_type); + if (type_is_float(canonical)) return bofp(expr, canonical, to_type, cast_type); break; case TYPE_ERROR_UNION: TODO + case TYPE_IXX: + // Compile time integers may convert into ints, floats, bools + if (type_is_integer(canonical)) return ixxxi(expr, canonical, to_type, cast_type); + if (type_is_float(canonical)) return ixxfp(expr, canonical, to_type, cast_type); + if (canonical == type_bool) return ixxbo(expr, to_type); + if (canonical->type_kind == TYPE_POINTER) return xipt(expr, canonical, to_type); + break; case TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: - case TYPE_IXX: - if (type_is_unsigned_integer(canonical)) return siui(expr, from_type, canonical, to_type, cast_type); + if (type_is_unsigned_integer(canonical)) return siui(expr, canonical, to_type, cast_type); if (type_is_signed_integer(canonical)) return sisi(expr, from_type, canonical, to_type, cast_type); - if (type_is_float(canonical)) return sifp(expr, from_type, canonical, to_type, cast_type); - if (canonical == type_bool) return xibo(expr, from_type, canonical, to_type, cast_type); - if (canonical->type_kind == TYPE_POINTER) return ptxi(expr, from_type, canonical, to_type, cast_type); + if (type_is_float(canonical)) return sifp(expr, canonical, to_type); + if (canonical == type_bool) return xibo(expr, canonical, to_type, cast_type); + if (canonical->type_kind == TYPE_POINTER) return xipt(expr, canonical, to_type); break; case TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64: - case TYPE_UXX: if (type_is_unsigned_integer(canonical)) return uiui(expr, from_type, canonical, to_type, cast_type); if (type_is_signed_integer(canonical)) return uisi(expr, from_type, canonical, to_type, cast_type); - if (type_is_float(canonical)) return uifp(expr, from_type, canonical, to_type, cast_type); - if (canonical == type_bool) return xibo(expr, from_type, canonical, to_type, cast_type); - if (canonical->type_kind == TYPE_POINTER) return ptxi(expr, from_type, canonical, to_type, cast_type); + if (type_is_float(canonical)) return uifp(expr, canonical, to_type); + if (canonical == type_bool) return xibo(expr, canonical, to_type, cast_type); + if (canonical->type_kind == TYPE_POINTER) return xipt(expr, canonical, to_type); break; case TYPE_F32: case TYPE_F64: case TYPE_FXX: - if (type_is_unsigned_integer(canonical)) return fpui(expr, from_type, canonical, to_type, cast_type); - if (type_is_signed_integer(canonical)) return fpsi(expr, from_type, canonical, to_type, cast_type); - if (canonical == type_bool) return fpbo(expr, from_type, canonical, to_type, cast_type); + if (type_is_integer(canonical)) return fpxi(expr, canonical, to_type, cast_type); + if (canonical == type_bool) return fpbo(expr, canonical, to_type, cast_type); if (type_is_float(canonical)) return fpfp(expr, from_type, canonical, to_type, cast_type); break; case TYPE_POINTER: - if (type_is_integer(canonical)) return ptxi(expr, from_type, canonical, to_type, cast_type); - if (canonical->type_kind == TYPE_BOOL) return ptbo(expr, from_type, canonical, to_type, cast_type); + if (type_is_integer(canonical)) return ptxi(expr, canonical, to_type, cast_type); + if (canonical->type_kind == TYPE_BOOL) return ptbo(expr, canonical, to_type, cast_type); if (canonical->type_kind == TYPE_POINTER) return ptpt(expr, from_type, canonical, to_type, cast_type); if (canonical->type_kind == TYPE_FUNC) return ptfu(expr, from_type, canonical, to_type, cast_type); if (canonical->type_kind == TYPE_VARARRAY) return ptva(expr, from_type, canonical, to_type, cast_type); @@ -667,7 +663,7 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type) if (type_is_integer(canonical)) return erxi(expr, from_type, canonical, to_type, cast_type); break; case TYPE_FUNC: - if (type_is_integer(canonical)) return ptxi(expr, from_type, canonical, to_type, cast_type); + if (type_is_integer(canonical)) return ptxi(expr, canonical, to_type, cast_type); if (canonical->type_kind == TYPE_POINTER) return fupt(expr, from_type, canonical, to_type, cast_type); break; case TYPE_STRUCT: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index b97d8df4b..3307cba11 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -32,6 +32,33 @@ typedef struct _Type Type; typedef bool(*CastFunc)(Expr *, Type *, Type *, Type *, CastType cast_type); +typedef struct _BigInt +{ + unsigned digit_count; + bool is_negative; + union { + uint64_t digit; + uint64_t *digits; + }; +} BigInt; + + +typedef struct +{ + union + { + long double f; + BigInt i; + bool b; + struct + { + char* chars; + int len; + } string; + }; + TypeKind kind; +} ExprConst; + typedef struct { SourceLoc loc; @@ -132,18 +159,24 @@ typedef struct struct _Type { TypeKind type_kind : 8; - struct _Type *canonical; + Type *canonical; const char *name; - struct _Type **type_cache; + Type **type_cache; void *backend_type; void *backend_debug_type; union { + // Error, Struct, Union, Typedef Decl *decl; + // int, float, bool TypeBuiltin builtin; + // Type[], Type[*], Type[123] TypeArray array; + // func Type1(Type2, Type3, ...) throws Err1, Err2, ... TypeFunc func; + // Type* Type *pointer; + // Type.type Type *child; }; }; @@ -153,6 +186,7 @@ struct _TypeInfo ResolveStatus resolve_status : 2; Type *type; TypeInfoKind kind; + SourceRange span; union { TypeUnresolved unresolved; @@ -217,7 +251,6 @@ typedef struct typedef struct { - Decl *parent; Expr *expr; Expr **args; uint64_t ordinal; @@ -403,21 +436,7 @@ typedef struct PostUnaryOp operator; } ExprPostUnary; -typedef struct -{ - union - { - long double f; - uint64_t i; - bool b; - struct - { - char* chars; - int len; - } string; - }; - ConstType type : 3; -} ExprConst; + typedef struct { @@ -700,7 +719,6 @@ typedef struct _Ast { SourceRange span; AstKind ast_kind : 8; - ExitType exit : 3; union { AstAttribute attribute; @@ -872,7 +890,7 @@ extern Type *type_bool, *type_void, *type_string, *type_voidptr; extern Type *type_float, *type_double; extern Type *type_char, *type_short, *type_int, *type_long, *type_isize; extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; -extern Type *type_compint, *type_compuint, *type_compfloat; +extern Type *type_compint, *type_compfloat; extern Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong; extern Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong; @@ -899,7 +917,6 @@ static inline Ast *new_ast(AstKind kind, SourceRange range) memset(ast, 0, sizeof(Ast)); ast->span = range; ast->ast_kind = kind; - ast->exit = EXIT_NONE; return ast; } @@ -953,24 +970,22 @@ static inline bool builtin_may_bit_negate(Type *canonical) case TYPE_U16: case TYPE_U32: case TYPE_U64: - case TYPE_UXX: return true; default: return false; } } -static inline ConstType sign_from_type(Type *type) -{ - assert(type->canonical == type); - return (type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX) ? CONST_INT : CONST_INT; -} bool cast_implicit(Expr *expr, Type *to_type); bool cast(Expr *expr, Type *to_type, CastType cast_type); bool cast_binary_arithmetic(Expr *left, Expr *right, const char *action); CastKind cast_to_bool_kind(Type *type); bool cast_to_runtime(Expr *expr); +static inline bool cast_is_implicit(CastType cast_type) +{ + return cast_type == CAST_TYPE_IMPLICIT_ASSIGN_ADD || cast_type == CAST_TYPE_IMPLICIT || cast_type == CAST_TYPE_IMPLICIT_ASSIGN; +} void llvm_codegen(Context *context); void llvm_set_struct_size_alignment(Decl *decl); @@ -1027,6 +1042,14 @@ static inline void expr_replace(Expr *expr, Expr *replacement) *expr = *replacement; expr->span = loc; } +void expr_const_set_int(ExprConst *expr, uint64_t v, TypeKind kind); +void expr_const_set_float(ExprConst *expr, long double d, TypeKind kind); +void expr_const_set_bool(ExprConst *expr, bool b); +void expr_const_set_nil(ExprConst *expr); +void expr_const_fprint(FILE *__restrict file, ExprConst *expr); +bool expr_const_int_overflowed(const ExprConst *expr); +bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op); +const char *expr_const_to_error_string(const ExprConst *expr); void fprint_ast(FILE *file, Ast *ast); void fprint_decl(FILE *file, Decl *dec); @@ -1131,27 +1154,35 @@ void type_append_signature_name(Type *type, char *dst, size_t *offset); Type *type_find_max_type(Type *type, Type *other); static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; } -static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX; } -static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_UXX; } +static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_I64; } +static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind <= TYPE_U64; } +static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I64; } +static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64; } static inline bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; } static inline bool type_info_ok(TypeInfo *type_info) { return !type_info || type_info->kind != TYPE_INFO_POISON; } bool type_may_have_method_functions(Type *type); static inline bool type_is_integer(Type *type) { assert(type == type->canonical); - return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_UXX; + return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_U64; } -static inline bool type_is_signed_integer(Type *type) +static inline bool type_is_any_integer(Type *type) { assert(type == type->canonical); return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX; } +static inline bool type_is_signed_integer(Type *type) +{ + assert(type == type->canonical); + return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I64; +} + static inline bool type_is_unsigned_integer(Type *type) { assert(type == type->canonical); - return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_UXX; + return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64; } static inline bool type_info_poison(TypeInfo *type) @@ -1167,22 +1198,24 @@ static inline bool type_is_float(Type *type) return type->type_kind >= TYPE_F32 && type->type_kind <= TYPE_FXX; } -static inline TypeInfo *type_info_new(TypeInfoKind kind) +static inline TypeInfo *type_info_new(TypeInfoKind kind, SourceRange range) { TypeInfo *type_info = malloc_arena(sizeof(TypeInfo)); memset(type_info, 0, sizeof(TypeInfo)); type_info->kind = kind; + type_info->span = range; type_info->resolve_status = RESOLVE_NOT_DONE; return type_info; } -static inline TypeInfo *type_info_new_base(Type *type) +static inline TypeInfo *type_info_new_base(Type *type, SourceRange range) { TypeInfo *type_info = malloc_arena(sizeof(TypeInfo)); memset(type_info, 0, sizeof(TypeInfo)); type_info->kind = TYPE_INFO_IDENTIFIER; type_info->resolve_status = RESOLVE_DONE; type_info->type = type; + type_info->span = range; return type_info; } @@ -1210,6 +1243,7 @@ static inline bool type_is_number(Type *type) return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_FXX; } + #define TYPE_MODULE_UNRESOLVED(_module, _name) ({ Type *__type = type_new(TYPE_USER_DEFINED); \ __type->name_loc = _name; __type->unresolved.module = _module; __type; }) #define TYPE_UNRESOLVED(_name) ({ TypeInfo *__type = type_new(TYPE_USER_DEFINED); __type->name_loc = _name; __type; }) diff --git a/src/compiler/enums.h b/src/compiler/enums.h index fcd572385..565ae6e01 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -40,6 +40,7 @@ typedef enum BINARYOP_BIT_AND, BINARYOP_AND, BINARYOP_OR, + // Don't change the ordering for GT to EQ or things will break BINARYOP_GT, BINARYOP_GE, BINARYOP_LT, @@ -178,14 +179,6 @@ typedef enum _CastType CAST_TYPE_IMPLICIT_ASSIGN_ADD, } CastType; -typedef enum -{ - CONST_NIL, - CONST_BOOL, - CONST_INT, - CONST_FLOAT, - CONST_STRING, -} ConstType; typedef enum @@ -214,8 +207,9 @@ typedef enum // Ordering here is in priority if two branches should have the same exit. typedef enum { - EXIT_NONE, + EXIT_NONE = 0, EXIT_BREAK, + EXIT_NEXT, EXIT_GOTO, EXIT_CONTINUE, EXIT_THROW, @@ -507,12 +501,11 @@ typedef enum TYPE_I16, TYPE_I32, TYPE_I64, - TYPE_IXX, TYPE_U8, TYPE_U16, TYPE_U32, TYPE_U64, - TYPE_UXX, + TYPE_IXX, TYPE_F32, TYPE_F64, TYPE_FXX, @@ -532,6 +525,12 @@ typedef enum TYPE_LAST = TYPE_META_TYPE } TypeKind; +#define ALL_INTS TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: \ +case TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64: case TYPE_IXX +#define ALL_SIGNED_INTS TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64 +#define ALL_UNSIGNED_INTS TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64 +#define ALL_FLOATS TYPE_F32: case TYPE_F64: case TYPE_FXX + #define TYPE_KINDS (TYPE_LAST + 1) typedef enum @@ -540,6 +539,7 @@ typedef enum UNARYOP_DEREF, UNARYOP_ADDR, UNARYOP_NEG, + UNARYOP_NEGMOD, UNARYOP_BITNEG, UNARYOP_NOT, UNARYOP_INC, diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index b5447218c..e6f39acb4 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -318,11 +318,10 @@ static Token scan_binary(Lexer *lexer) static inline Token scan_hex(Lexer *lexer) { - char x = next(lexer); // skip the x if (!is_hex(next(lexer))) { - return error_token(lexer, "'0%c' starts a hexadecimal number, " - "but it was followed by '%c' which is not part of a hexadecimal number.", x, prev(lexer)); + return error_token(lexer, "'0x' starts a hexadecimal number, " + "but it was followed by '%c' which is not part of a hexadecimal number.", prev(lexer)); } while (is_hex_or_(peek(lexer))) next(lexer); bool is_float = false; diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index a7d538cdd..844462d06 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -107,7 +107,6 @@ LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type) { case TYPE_POISONED: case TYPE_IXX: - case TYPE_UXX: case TYPE_FXX: case TYPE_META_TYPE: UNREACHABLE diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index f138a0ade..4cacca9b4 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -4,6 +4,7 @@ #include "llvm_codegen_internal.h" #include "compiler_internal.h" +#include "bigint.h" static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *type, bool use_mod, LLVMValueRef left, LLVMValueRef right) { @@ -228,12 +229,20 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), LLVMConstInt(type_bool->backend_type, 1, 0), "not"); case UNARYOP_BITNEG: return LLVMBuildNot(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "bnot"); + case UNARYOP_NEGMOD: + return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "negmod"); case UNARYOP_NEG: + // TODO improve how unsigned numbers are negated. if (type_is_float(expr->unary_expr.expr->type->canonical)) { return LLVMBuildFNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "fneg"); } - return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg"); + if (type_is_unsigned(expr->unary_expr.expr->type->canonical)) + { + return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg"); + } + // TODO insert trap + return LLVMBuildNSWNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg"); case UNARYOP_ADDR: return gencontext_emit_address(context, expr->unary_expr.expr); case UNARYOP_DEREF: @@ -301,6 +310,128 @@ static inline LLVMValueRef gencontext_emit_struct_value_expr(GenContext *context } +static LLVMValueRef gencontext_emit_int_comparison(GenContext *context, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op) +{ + bool lhs_signed = type_is_signed(lhs_type); + bool rhs_signed = type_is_signed(rhs_type); + if (lhs_signed != rhs_signed) + { + // Swap sides if needed. + if (!lhs_signed) + { + Type *temp = lhs_type; + lhs_type = rhs_type; + rhs_type = temp; + LLVMValueRef temp_val = lhs_value; + lhs_value = rhs_value; + rhs_value = temp_val; + switch (binary_op) + { + case BINARYOP_GE: + binary_op = BINARYOP_LE; + break; + case BINARYOP_GT: + binary_op = BINARYOP_LT; + break; + case BINARYOP_LE: + binary_op = BINARYOP_GE; + break; + case BINARYOP_LT: + binary_op = BINARYOP_GT; + break; + default: + break; + } + lhs_signed = true; + rhs_signed = false; + } + } + if (!lhs_signed) + { + assert(lhs_signed == rhs_signed); + // Right and left side are both unsigned. + switch (binary_op) + { + case BINARYOP_EQ: + return LLVMBuildICmp(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq"); + case BINARYOP_NE: + return LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq"); + case BINARYOP_GE: + return LLVMBuildICmp(context->builder, LLVMIntUGE, lhs_value, rhs_value, "ge"); + case BINARYOP_GT: + return LLVMBuildICmp(context->builder, LLVMIntUGT, lhs_value, rhs_value, "gt"); + case BINARYOP_LE: + return LLVMBuildICmp(context->builder, LLVMIntULE, lhs_value, rhs_value, "le"); + case BINARYOP_LT: + return LLVMBuildICmp(context->builder, LLVMIntULT, lhs_value, rhs_value, "lt"); + default: + UNREACHABLE + } + } + + // Left side is signed. + LLVMValueRef comp_value; + LLVMValueRef check_value; + switch (binary_op) + { + case BINARYOP_EQ: + comp_value = LLVMBuildICmp(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq"); + break; + case BINARYOP_NE: + comp_value = LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq"); + break; + case BINARYOP_GE: + comp_value = LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, rhs_value, "ge"); + break; + case BINARYOP_GT: + comp_value = LLVMBuildICmp(context->builder, LLVMIntSGT, lhs_value, rhs_value, "gt"); + break; + case BINARYOP_LE: + comp_value = LLVMBuildICmp(context->builder, LLVMIntSLE, lhs_value, rhs_value, "le"); + break; + case BINARYOP_LT: + comp_value = LLVMBuildICmp(context->builder, LLVMIntSLT, lhs_value, rhs_value, "lt"); + break; + default: + UNREACHABLE + } + + // If right side is also signed then this is fine. + if (rhs_signed) return comp_value; + + // Otherwise, special handling for left side signed, right side unsigned. + LLVMValueRef zero = LLVMConstInt(llvm_type(lhs_type), 0, true); + switch (binary_op) + { + case BINARYOP_EQ: + // Only true if lhs >= 0 + check_value = LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, zero, "check"); + return LLVMBuildAnd(context->builder, check_value, comp_value, "siui-eq"); + case BINARYOP_NE: + // Always true if lhs < 0 + check_value = LLVMBuildICmp(context->builder, LLVMIntSLT, lhs_value, zero, "check"); + return LLVMBuildOr(context->builder, check_value, comp_value, "siui-ne"); + case BINARYOP_GE: + // Only true if rhs >= 0 when regarded as a signed integer + check_value = LLVMBuildICmp(context->builder, LLVMIntSGE, rhs_value, zero, "check"); + return LLVMBuildAnd(context->builder, check_value, comp_value, "siui-ge"); + case BINARYOP_GT: + // Only true if rhs >= 0 when regarded as a signed integer + check_value = LLVMBuildICmp(context->builder, LLVMIntSGE, rhs_value, zero, "check"); + return LLVMBuildAnd(context->builder, check_value, comp_value, "siui-gt"); + case BINARYOP_LE: + // Always true if rhs < 0 when regarded as a signed integer + check_value = LLVMBuildICmp(context->builder, LLVMIntSLT, rhs_value, zero, "check"); + return LLVMBuildOr(context->builder, check_value, comp_value, "siui-le"); + case BINARYOP_LT: + // Always true if rhs < 0 when regarded as a signed integer + check_value = LLVMBuildICmp(context->builder, LLVMIntSLT, rhs_value, zero, "check"); + return LLVMBuildOr(context->builder, check_value, comp_value, "siui-lt"); + default: + UNREACHABLE + } + +} static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVMValueRef lhs_addr, BinaryOp binary_op) { @@ -325,9 +456,12 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM } rhs_value = gencontext_emit_expr(context, rhs); - - bool is_float = type_is_float(type); - + Type *lhs_type = expr->binary_expr.left->type->canonical; + if (type_is_integer(lhs_type) && binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ) + { + return gencontext_emit_int_comparison(context, lhs_type, rhs->type->canonical, lhs_value, rhs_value, binary_op); + } + bool is_float = type_is_float(lhs_type); switch (binary_op) { case BINARYOP_ERROR: @@ -335,7 +469,7 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM case BINARYOP_MULT: if (is_float) return LLVMBuildFMul(context->builder, lhs_value, rhs_value, "fmul"); // TODO insert trap - if (type_is_unsigned_integer(lhs->type->canonical)) + if (type_is_unsigned_integer(lhs_type)) { return LLVMBuildNUWMul(context->builder, lhs_value, rhs_value, "umul"); } @@ -347,7 +481,7 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM return LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul"); case BINARYOP_SUB: case BINARYOP_SUB_MOD: - if (lhs->type->canonical->type_kind == TYPE_POINTER) + if (lhs_type->type_kind == TYPE_POINTER) { if (lhs->type->canonical == rhs->type->canonical) return LLVMBuildPtrDiff(context->builder, lhs_value, rhs_value, "ptrdiff"); rhs_value = LLVMBuildNeg(context->builder, rhs_value, ""); @@ -357,24 +491,24 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM return gencontext_emit_sub_int(context, lhs->type->canonical, binary_op == BINARYOP_SUB_MOD, lhs_value, rhs_value); case BINARYOP_ADD: case BINARYOP_ADD_MOD: - if (lhs->type->canonical->type_kind == TYPE_POINTER) + if (lhs_type->type_kind == TYPE_POINTER) { assert(type_is_integer(rhs->type->canonical)); - return LLVMBuildGEP2(context->builder, llvm_type(lhs->type), lhs_value, &rhs_value, 1, "ptradd"); + return LLVMBuildGEP2(context->builder, llvm_type(lhs_type), lhs_value, &rhs_value, 1, "ptradd"); } if (is_float) return LLVMBuildFAdd(context->builder, lhs_value, rhs_value, "fadd"); - return gencontext_emit_add_int(context, lhs->type->canonical, binary_op == BINARYOP_ADD_MOD, lhs_value, rhs_value); + return gencontext_emit_add_int(context, lhs_type, binary_op == BINARYOP_ADD_MOD, lhs_value, rhs_value); case BINARYOP_DIV: if (is_float) return LLVMBuildFDiv(context->builder, lhs_value, rhs_value, "fdiv"); - return type_is_unsigned(type) + return type_is_unsigned(lhs_type) ? LLVMBuildUDiv(context->builder, lhs_value, rhs_value, "udiv") : LLVMBuildSDiv(context->builder, lhs_value, rhs_value, "sdiv"); case BINARYOP_MOD: - return type_is_unsigned(type) + return type_is_unsigned(lhs_type) ? LLVMBuildURem(context->builder, lhs_value, rhs_value, "umod") : LLVMBuildSRem(context->builder, lhs_value, rhs_value, "smod"); case BINARYOP_SHR: - return type_is_unsigned(type) + return type_is_unsigned(lhs_type) ? LLVMBuildLShr(context->builder, lhs_value, rhs_value, "lshr") : LLVMBuildAShr(context->builder, lhs_value, rhs_value, "ashr"); case BINARYOP_SHL: @@ -386,33 +520,25 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM case BINARYOP_BIT_XOR: return LLVMBuildXor(context->builder, lhs_value, rhs_value, "xor"); case BINARYOP_EQ: + assert(!type_is_integer(lhs_type)); // Unordered? - if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq"); - return LLVMBuildICmp(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq"); + return LLVMBuildFCmp(context->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq"); case BINARYOP_NE: + assert(!type_is_integer(lhs_type)); // Unordered? - if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUNE, lhs_value, rhs_value, "neq"); - return LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq"); + return LLVMBuildFCmp(context->builder, LLVMRealUNE, lhs_value, rhs_value, "neq"); case BINARYOP_GE: - if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUGE, lhs_value, rhs_value, "ge"); - return type_is_unsigned(type) - ? LLVMBuildICmp(context->builder, LLVMIntUGE, lhs_value, rhs_value, "ge") - : LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, rhs_value, "ge"); + assert(!type_is_integer(lhs_type)); + return LLVMBuildFCmp(context->builder, LLVMRealUGE, lhs_value, rhs_value, "ge"); case BINARYOP_GT: - if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUGT, lhs_value, rhs_value, "gt"); - return type_is_unsigned(type) - ? LLVMBuildICmp(context->builder, LLVMIntUGT, lhs_value, rhs_value, "gt") - : LLVMBuildICmp(context->builder, LLVMIntSGT, lhs_value, rhs_value, "gt"); + assert(!type_is_integer(lhs_type)); + return LLVMBuildFCmp(context->builder, LLVMRealUGT, lhs_value, rhs_value, "gt"); case BINARYOP_LE: - if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "le"); - return type_is_unsigned(type) - ? LLVMBuildICmp(context->builder, LLVMIntULE, lhs_value, rhs_value, "le") - : LLVMBuildICmp(context->builder, LLVMIntSLE, lhs_value, rhs_value, "le"); + assert(!type_is_integer(lhs_type)); + return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "le"); case BINARYOP_LT: - if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt"); - return type_is_unsigned(type) - ? LLVMBuildICmp(context->builder, LLVMIntULT, lhs_value, rhs_value, "lt") - : LLVMBuildICmp(context->builder, LLVMIntSLT, lhs_value, rhs_value, "lt"); + assert(!type_is_integer(lhs_type)); + return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt"); case BINARYOP_AND: case BINARYOP_OR: UNREACHABLE @@ -543,17 +669,24 @@ static LLVMValueRef gencontext_emit_identifier_expr(GenContext *context, Expr *e LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) { LLVMTypeRef type = llvm_type(expr->type); - switch (expr->const_expr.type) + switch (expr->const_expr.kind) { - case CONST_INT: - return LLVMConstInt(type, expr->const_expr.i, type_is_unsigned(expr->type->canonical) ? false : true); - case CONST_FLOAT: + case ALL_INTS: + if (type_is_unsigned(expr->type->canonical)) + { + return LLVMConstInt(type, bigint_as_unsigned(&expr->const_expr.i), false); + } + else + { + return LLVMConstInt(type, (uint64_t)bigint_as_signed(&expr->const_expr.i), false); + } + case ALL_FLOATS: return LLVMConstReal(type, (double) expr->const_expr.f); - case CONST_NIL: + case TYPE_POINTER: return LLVMConstNull(type); - case CONST_BOOL: + case TYPE_BOOL: return LLVMConstInt(type, expr->const_expr.b ? 1 : 0, false); - case CONST_STRING: + case TYPE_STRING: { LLVMValueRef global_name = LLVMAddGlobal(context->module, type, "string"); LLVMSetLinkage(global_name, LLVMInternalLinkage); @@ -564,8 +697,9 @@ LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) 0)); return global_name; } + default: + UNREACHABLE } - UNREACHABLE } LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr) diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index f6948e227..289097275 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -96,6 +96,7 @@ void gencontext_emit_implicit_return(GenContext *context) void gencontext_emit_function_body(GenContext *context, Decl *decl) { + DEBUG_LOG("Generating function %s.", decl->external_name); assert(decl->func.backend_value); LLVMValueRef prev_function = context->function; @@ -143,7 +144,7 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl) gencontext_emit_stmt(context, decl->func.body->compound_stmt.stmts[i]); } - if (!LLVMGetFirstInstruction(context->current_block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block))) + if (context->current_block && !LLVMGetFirstInstruction(context->current_block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block))) { LLVMBasicBlockRef prev_block = LLVMGetPreviousBasicBlock(context->current_block); LLVMDeleteBasicBlock(context->current_block); @@ -151,9 +152,8 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl) LLVMPositionBuilderAtEnd(context->builder, context->current_block); } // Insert a return (and defer) if needed. - if (!LLVMGetBasicBlockTerminator(context->current_block)) + if (context->current_block && !LLVMGetBasicBlockTerminator(context->current_block)) { - assert(decl->func.function_signature.rtype->type->type_kind == TYPE_VOID); assert(decl->func.body->compound_stmt.defer_list.end == NULL); gencontext_emit_defer(context, decl->func.body->compound_stmt.defer_list.start, NULL); gencontext_emit_implicit_return(context); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index b5a586b7a..38df44c45 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -134,16 +134,18 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast) if (!ret_value) { gencontext_emit_implicit_return(context); - return; - } - if (context->return_out) - { - LLVMBuildStore(context->builder, ret_value, context->return_out); - gencontext_emit_implicit_return(context); } else { - LLVMBuildRet(context->builder, ret_value); + if (context->return_out) + { + LLVMBuildStore(context->builder, ret_value, context->return_out); + gencontext_emit_implicit_return(context); + } + else + { + LLVMBuildRet(context->builder, ret_value); + } } context->current_block = NULL; LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret"); diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 7f6deff30..b7cac074c 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -154,9 +154,6 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type) switch (type->type_kind) { case TYPE_POISONED: - case TYPE_IXX: - case TYPE_UXX: - case TYPE_FXX: case TYPE_META_TYPE: UNREACHABLE; case TYPE_TYPEDEF: @@ -172,6 +169,7 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type) case TYPE_VOID: return type->backend_type = LLVMVoidTypeInContext(context); case TYPE_F64: + case TYPE_FXX: return type->backend_type = LLVMDoubleTypeInContext(context); case TYPE_F32: return type->backend_type = LLVMFloatTypeInContext(context); @@ -180,6 +178,7 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type) return type->backend_type = LLVMIntTypeInContext(context, 64U); case TYPE_U32: case TYPE_I32: + case TYPE_IXX: return type->backend_type = LLVMIntTypeInContext(context, 32U); case TYPE_U16: case TYPE_I16: diff --git a/src/compiler/number.c b/src/compiler/number.c new file mode 100644 index 000000000..ee2be3aba --- /dev/null +++ b/src/compiler/number.c @@ -0,0 +1,265 @@ +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +#include "compiler_internal.h" +#include "bigint.h" + +#define CHECK_SI_KIND(_kind) assert(_kind >= TYPE_I8 && _kind <= TYPE_I64) +#define CHECK_IXX_KIND(_kind) assert(_kind == TYPE_IXX) +#define CHECK_UI_KIND(_kind) assert(_kind >= TYPE_U8 && _kind <= TYPE_U64) +#define CHECK_INT_KIND(_kind) assert(_kind >= TYPE_I8 && _kind <= TYPE_U64) +#define CHECK_CONVERSION(_kind) assert(i->kind != _kind && "Unnecessary conversion") +#define TYPE_MATCH assert(left->kind == right->kind && left != res && right != res) + +static uint64_t type_mask[TYPE_U64 + 1] = { + [TYPE_U8] = 0xFF, + [TYPE_I8] = 0xFF, + [TYPE_U16] = 0xFFFF, + [TYPE_I16] = 0xFFFF, + [TYPE_U32] = 0xFFFFFFFFU, + [TYPE_I32] = 0xFFFFFFFFU, + [TYPE_U64] = 0xFFFFFFFFFFFFFFFFLLU, + [TYPE_I64] = 0xFFFFFFFFFFFFFFFFLLU, +}; + +static int type_bits[TYPE_U64 + 1] = { + [TYPE_U8] = 8, + [TYPE_I8] = 8, + [TYPE_U16] = 16, + [TYPE_I16] = 16, + [TYPE_U32] = 32, + [TYPE_I32] = 32, + [TYPE_U64] = 64, + [TYPE_I64] = 64, +}; + +static int64_t int_type_max[TYPE_I64 + 1] = { + [TYPE_I8] = 0x7F, + [TYPE_I16] = 0x7FFF, + [TYPE_I32] = 0x7FFFFFFFLL, + [TYPE_I64] = 0x7FFFFFFFFFFFFFFFLL, + }; + +static int64_t int_type_min[TYPE_I64 + 1] = { + [TYPE_I8] = -0x80, + [TYPE_I16] = -0x8000L, + [TYPE_I32] = -0x80000000L, + [TYPE_I64] = -0x8000000000000000LL, +}; + +void expr_const_fprint(FILE *__restrict file, ExprConst *expr) +{ + switch (expr->kind) + { + case TYPE_POINTER: + fprintf(file, "nil"); + break; + case TYPE_BOOL: + fprintf(file, expr->b ? "true" : "false"); + break; + case TYPE_I8: + case TYPE_I16: + case TYPE_I32: + case TYPE_I64: + case TYPE_U8: + case TYPE_U16: + case TYPE_U32: + case TYPE_U64: + case TYPE_IXX: + bigint_fprint(file, &expr->i, 10); + break; + case TYPE_F32: + case TYPE_F64: + case TYPE_FXX: + fprintf(file, "%Lf", expr->f); + break; + default: + UNREACHABLE + } + +} + +void expr_const_set_int(ExprConst *expr, uint64_t v, TypeKind kind) +{ + if (type_kind_is_signed(kind)) + { + bigint_init_signed(&expr->i, (int64_t)v); + } + else + { + bigint_init_unsigned(&expr->i, v); + } + expr->kind = kind; +} + +void expr_const_set_bool(ExprConst *expr, bool b) +{ + expr->b = b; + expr->kind = TYPE_BOOL; +} + +void expr_const_set_nil(ExprConst *expr) +{ + expr->i.digit_count = 0; + expr->i.digit = 0; + expr->kind = TYPE_POINTER; +} + +static inline bool compare_bool(bool left, bool right, BinaryOp op) +{ + switch (op) + { + case BINARYOP_EQ: + return left == right; + case BINARYOP_NE: + return left != right; + case BINARYOP_GT: + return left > right; + case BINARYOP_LT: + return left < right; + case BINARYOP_GE: + return left >= right; + case BINARYOP_LE: + return left <= right; + default: + UNREACHABLE + } +} + +static inline bool compare_ints(const BigInt *left, const BigInt *right, BinaryOp op) +{ + CmpRes res = bigint_cmp(left, right); + switch (op) + { + case BINARYOP_GE: + return res != CMP_LT; + case BINARYOP_LE: + return res != CMP_GT; + case BINARYOP_NE: + return res != CMP_EQ; + case BINARYOP_GT: + return res == CMP_GT; + case BINARYOP_LT: + return res == CMP_LT; + case BINARYOP_EQ: + return res == CMP_EQ; + default: + UNREACHABLE + } +} + +static inline bool compare_fps(long double left, long double right, BinaryOp op) +{ + switch (op) + { + case BINARYOP_GE: + return left >= right; + case BINARYOP_LE: + return left <= right; + case BINARYOP_NE: + return left != right; + case BINARYOP_GT: + return left > right; + case BINARYOP_LT: + return left < right; + case BINARYOP_EQ: + return left == right; + default: + UNREACHABLE + } +} + +bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op) +{ + switch (left->kind) + { + case TYPE_BOOL: + return compare_bool(left->b, right->b, op); + case ALL_INTS: + return compare_ints(&left->i, &right->i, op); + case ALL_FLOATS: + return compare_fps(left->f, right->f, op); + case TYPE_POINTER: + return true; + case TYPE_STRING: + TODO + default: + UNREACHABLE + } +} + +bool expr_const_int_overflowed(const ExprConst *expr) +{ + switch (expr->kind) + { + case TYPE_I8: + return !bigint_fits_in_bits(&expr->i, 8, true); + case TYPE_I16: + return !bigint_fits_in_bits(&expr->i, 16, true); + case TYPE_I32: + return !bigint_fits_in_bits(&expr->i, 32, true); + case TYPE_I64: + return !bigint_fits_in_bits(&expr->i, 64, true); + case TYPE_U8: + return !bigint_fits_in_bits(&expr->i, 8, false); + case TYPE_U16: + return !bigint_fits_in_bits(&expr->i, 16, false); + case TYPE_U32: + return !bigint_fits_in_bits(&expr->i, 32, false); + case TYPE_U64: + return !bigint_fits_in_bits(&expr->i, 64, false); + case TYPE_IXX: + return false; + default: + UNREACHABLE + } +} +const char *expr_const_to_error_string(const ExprConst *expr) +{ + char *buff = NULL; + switch (expr->kind) + { + case TYPE_POINTER: + return "nil"; + case TYPE_BOOL: + return expr->b ? "true" : "false"; + case TYPE_I8: + case TYPE_I16: + case TYPE_I32: + case TYPE_I64: + case TYPE_U8: + case TYPE_U16: + case TYPE_U32: + case TYPE_U64: + case TYPE_IXX: + return bigint_to_error_string(&expr->i, 10); + case TYPE_F32: + case TYPE_F64: + case TYPE_FXX: + asprintf(&buff, "%Lf", expr->f); + return buff; + default: + UNREACHABLE + } +} + + +void expr_const_set_float(ExprConst *expr, long double d, TypeKind kind) +{ + switch (kind) + { + case TYPE_F32: + expr->f = (float)d; + break; + case TYPE_F64: + expr->f = (double)d; + break; + default: + expr->f = d; + } + expr->kind = kind; +} + + + diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index c4e6089fc..df4973570 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -4,6 +4,7 @@ #include "compiler_internal.h" #include "parser_internal.h" +#include "bigint.h" typedef Expr *(*ParseFn)(Context *context, Expr *); @@ -276,6 +277,7 @@ static Expr *parse_binary(Context *context, Expr *left_side) expr->binary_expr.operator = binaryop_from_token(operator_type); expr->binary_expr.left = left_side; expr->binary_expr.right = right_side; + expr->span.end_loc = right_side->span.end_loc; return expr; } @@ -389,7 +391,6 @@ static Expr *parse_integer(Context *context, Expr *left) Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); const char *string = context->tok.start; const char *end = string + source_range_len(context->tok.span); - uint64_t i = 0; if (string[0] == '\'') { union @@ -431,27 +432,39 @@ static Expr *parse_integer(Context *context, Expr *left) } bytes.b[pos++] = (unsigned)*string; } + switch (pos) { case 1: - expr_int->const_expr.i = bytes.u8; + expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U8); + expr_int->type = type_byte; break; case 2: - expr_int->const_expr.i = bytes.u16; + expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U16); + expr_int->type = type_ushort; break; case 4: - expr_int->const_expr.i = bytes.u32; + expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U32); + expr_int->type = type_uint; break; case 8: - expr_int->const_expr.i = bytes.u64; + expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U64); + expr_int->type = type_ulong; break; + default: + UNREACHABLE } - expr_int->const_expr.type = CONST_INT; - expr_int->type = i > INT64_MAX ? type_compuint : type_compint; expr_int->resolve_status = RESOLVE_DONE; advance(context); return expr_int; } + BigInt *i = &expr_int->const_expr.i; + bigint_init_unsigned(i, 0); + BigInt diff; + bigint_init_unsigned(&diff, 0); + BigInt ten; + bigint_init_unsigned(&ten, 10); + BigInt res; switch (source_range_len(context->tok.span) > 2 ? string[1] : '0') { case 'x': @@ -460,24 +473,20 @@ static Expr *parse_integer(Context *context, Expr *left) { char c = *(string++); if (c == '_') continue; - if (i > (UINT64_MAX >> 4U)) - { - SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); - return &poisoned_expr; - } - i <<= 4U; + bigint_shl_int(&res, i, 4); if (c < 'A') { - i += c - '0'; + bigint_init_unsigned(&diff, c - '0'); } else if (c < 'a') { - i += c - 'A' + 10; + bigint_init_unsigned(&diff, c - 'A' + 10); } else { - i += c - 'a' + 10; + bigint_init_unsigned(&diff, c - 'a' + 10); } + bigint_add(i, &res, &diff); } break; case 'o': @@ -486,13 +495,9 @@ static Expr *parse_integer(Context *context, Expr *left) { char c = *(string++); if (c == '_') continue; - if (i > (UINT64_MAX >> 3U)) - { - SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); - return &poisoned_expr; - } - i <<= (unsigned) 3; - i += c - '0'; + bigint_shl_int(&res, i, 4); + bigint_init_unsigned(&diff, c - '0'); + bigint_add(i, &res, &diff); } break; case 'b': @@ -501,13 +506,9 @@ static Expr *parse_integer(Context *context, Expr *left) { char c = *(string++); if (c == '_') continue; - if (i > (UINT64_MAX >> 1U)) - { - SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); - return &poisoned_expr; - } - i <<= (unsigned) 1; - i += c - '0'; + bigint_shl_int(&res, i, 1); + bigint_init_unsigned(&diff, c - '0'); + bigint_add(i, &res, &diff); } break; default: @@ -515,20 +516,14 @@ static Expr *parse_integer(Context *context, Expr *left) { char c = *(string++); if (c == '_') continue; - if (i > (UINT64_MAX / 10)) - { - SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); - return &poisoned_expr; - } - i *= 10; - i += c - '0'; + bigint_mul(&res, i, &ten); + bigint_init_unsigned(&diff, c - '0'); + bigint_add(i, &res, &diff); } break; - } - expr_int->const_expr.i = i; - expr_int->const_expr.type = CONST_INT; - expr_int->type = i > INT64_MAX ? type_compuint : type_compint; + expr_int->const_expr.kind = TYPE_IXX; + expr_int->type = type_compint; advance(context); return expr_int; } @@ -549,7 +544,7 @@ static Expr *parse_double(Context *context, Expr *left) advance(context); number->const_expr.f = fval; number->type = type_compfloat; - number->const_expr.type = CONST_FLOAT; + number->const_expr.kind = TYPE_FXX; return number; } @@ -678,7 +673,7 @@ static Expr *parse_string_literal(Context *context, Expr *left) expr_string->const_expr.string.chars = str; expr_string->const_expr.string.len = len; expr_string->type = type_string; - expr_string->const_expr.type = CONST_STRING; + expr_string->const_expr.kind = TYPE_STRING; return expr_string; } @@ -686,7 +681,7 @@ static Expr *parse_bool(Context *context, Expr *left) { assert(!left && "Had left hand side"); Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - number->const_expr = (ExprConst) { .b = context->tok.type == TOKEN_TRUE, .type = CONST_BOOL }; + number->const_expr = (ExprConst) { .b = context->tok.type == TOKEN_TRUE, .kind = TYPE_BOOL }; number->type = type_bool; number->resolve_status = RESOLVE_DONE; advance(context); @@ -697,7 +692,7 @@ static Expr *parse_nil(Context *context, Expr *left) { assert(!left && "Had left hand side"); Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - number->const_expr.type = CONST_NIL; + number->const_expr.kind = TYPE_POINTER; number->type = type_voidptr; advance(context); return number; @@ -734,7 +729,7 @@ ParseRule rules[TOKEN_EOF + 1] = { //[TOKEN_SIZEOF] = { parse_sizeof, NULL, PREC_NONE }, [TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL }, [TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, - [TOKEN_MINUS_MOD] = { NULL, parse_binary, PREC_ADDITIVE }, + [TOKEN_MINUS_MOD] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, [TOKEN_PLUS] = { NULL, parse_binary, PREC_ADDITIVE }, [TOKEN_PLUS_MOD] = { NULL, parse_binary, PREC_ADDITIVE }, [TOKEN_DIV] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 42322adbf..4bf2ddb26 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -436,7 +436,6 @@ static inline Ast *parse_return_stmt(Context *context) { advance_and_verify(context, TOKEN_RETURN); Ast *ast = AST_NEW_TOKEN(AST_RETURN_STMT, context->tok); - ast->exit = EXIT_RETURN; ast->return_stmt.defer = NULL; if (try_consume(context, TOKEN_EOS)) { diff --git a/src/compiler/parser.c b/src/compiler/parser.c index d14c4cd2f..e7f68c5da 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -312,13 +312,15 @@ Path *parse_path_prefix(Context *context) */ static inline TypeInfo *parse_base_type(Context *context) { + SourceRange range = context->tok.span; Path *path = parse_path_prefix(context); if (path) { - TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER); + TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER, range); type_info->unresolved.path = path; type_info->unresolved.name_loc = context->tok; if (!consume_type_name(context, "types")) return &poisoned_type_info; + RANGE_EXTEND_PREV(type_info); return type_info; } @@ -327,16 +329,17 @@ static inline TypeInfo *parse_base_type(Context *context) switch (context->tok.type) { case TOKEN_TYPE_IDENT: - type_info = type_info_new(TYPE_INFO_IDENTIFIER); + type_info = type_info_new(TYPE_INFO_IDENTIFIER, context->tok.span); type_info->unresolved.name_loc = context->tok; break; case TOKEN_TYPE: - type_info = type_info_new(TYPE_INFO_IDENTIFIER); + type_info = type_info_new(TYPE_INFO_IDENTIFIER, context->tok.span); advance_and_verify(context, TOKEN_TYPE); CONSUME_OR(TOKEN_LPAREN, &poisoned_type_info); type_info->resolve_status = RESOLVE_NOT_DONE; type_info->unresolved_type_expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_type_info); EXPECT_OR(TOKEN_RPAREN, &poisoned_type_info); + RANGE_EXTEND_PREV(type_info); break; case TOKEN_VOID: type_found = type_void; @@ -408,14 +411,15 @@ static inline TypeInfo *parse_base_type(Context *context) SEMA_TOKEN_ERROR(context->tok, "A type name was expected here."); return &poisoned_type_info; } - advance(context); if (type_found) { assert(!type_info); - type_info = type_info_new(TYPE_INFO_IDENTIFIER); + type_info = type_info_new(TYPE_INFO_IDENTIFIER, context->tok.span); type_info->resolve_status = RESOLVE_DONE; type_info->type = type_found; } + advance(context); + RANGE_EXTEND_PREV(type_info); return type_info; } @@ -437,21 +441,24 @@ static inline TypeInfo *parse_array_type_index(Context *context, TypeInfo *type) if (try_consume(context, TOKEN_PLUS)) { CONSUME_OR(TOKEN_RBRACKET, &poisoned_type_info); - TypeInfo *incr_array = type_info_new(TYPE_INFO_INC_ARRAY); + TypeInfo *incr_array = type_info_new(TYPE_INFO_INC_ARRAY, type->span); incr_array->array.base = type; + RANGE_EXTEND_PREV(incr_array); return incr_array; } if (try_consume(context, TOKEN_RBRACKET)) { - TypeInfo *array = type_info_new(TYPE_INFO_ARRAY); + TypeInfo *array = type_info_new(TYPE_INFO_ARRAY, type->span); array->array.base = type; array->array.len = NULL; + RANGE_EXTEND_PREV(array); return array; } - TypeInfo *array = type_info_new(TYPE_INFO_ARRAY); + TypeInfo *array = type_info_new(TYPE_INFO_ARRAY, type->span); array->array.base = type; array->array.len = TRY_EXPR_OR(parse_expr(context), &poisoned_type_info); CONSUME_OR(TOKEN_RBRACKET, &poisoned_type_info); + RANGE_EXTEND_PREV(array); return array; } @@ -478,10 +485,11 @@ TypeInfo *parse_type_expression(Context *context) case TOKEN_STAR: advance(context); { - TypeInfo *ptr_type = type_info_new(TYPE_INFO_POINTER); + TypeInfo *ptr_type = type_info_new(TYPE_INFO_POINTER, type_info->span); assert(type_info); ptr_type->pointer = type_info; type_info = ptr_type; + RANGE_EXTEND_PREV(type_info); } break; default: @@ -725,7 +733,7 @@ bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **type_ptr) CONSUME_OR(TOKEN_RPAREN, false); if (inner_expr) { - *type_ptr = type_info_new(TYPE_INFO_EXPRESSION); + *type_ptr = type_info_new(TYPE_INFO_EXPRESSION, inner_expr->span); (**type_ptr).unresolved_type_expr = inner_expr; return true; } @@ -863,7 +871,7 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili { TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), false); Decl *param = decl_new_var(context->tok, type, VARDECL_PARAM, parent_visibility); - + param->span = type->span; if (!try_consume(context, TOKEN_IDENT)) { param->name = NULL; @@ -873,7 +881,12 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili if (!name && !type_only) { - SEMA_TOKEN_ERROR(context->tok, "The function parameter must be named."); + if (context->tok.type != TOKEN_COMMA && context->tok.type != TOKEN_RPAREN) + { + SEMA_TOKEN_ERROR(context->tok, "Unexpected end of the parameter list, did you forget an ')'?"); + return false; + } + SEMA_ERROR(type, "The function parameter must be named."); return false; } if (name && try_consume(context, TOKEN_EQ)) @@ -882,6 +895,7 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili } *parameters = VECADD(*parameters, param); + RANGE_EXTEND_PREV(param); return true; } @@ -1365,7 +1379,6 @@ static inline bool parse_enum_spec(Context *context, TypeInfo **type_ref, Decl** { *type_ref = TRY_TYPE_OR(parse_base_type(context), false); if (!try_consume(context, TOKEN_LPAREN)) return true; - CONSUME_OR(TOKEN_LPAREN, false); while (!try_consume(context, TOKEN_RPAREN)) { if (!parse_param_decl(context, parent_visibility, parameters_ref, false)) return false; @@ -1415,11 +1428,10 @@ static inline Decl *parse_enum_declaration(Context *context, Visibility visibili CONSUME_OR(TOKEN_LBRACE, false); - decl->enums.type_info = type ? type : type_info_new_base(type_int); + decl->enums.type_info = type ? type : type_info_new_base(type_int, decl->span); while (!try_consume(context, TOKEN_RBRACE)) { Decl *enum_const = decl_new(DECL_ENUM_CONSTANT, context->tok, decl->visibility); - enum_const->enum_constant.parent = decl; VECEACH(decl->enums.values, i) { Decl *other_constant = decl->enums.values[i]; @@ -1491,12 +1503,14 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit Decl *func = decl_new(DECL_FUNC, context->tok, visibility); func->func.function_signature.rtype = return_type; + SourceRange start = context->tok.span; Path *path = parse_path_prefix(context); if (path || context->tok.type == TOKEN_TYPE_IDENT) { // Special case, actually an extension TRY_EXPECT_OR(TOKEN_TYPE_IDENT, "A type was expected after '::'.", &poisoned_decl); - TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER); + // TODO span is incorrect + TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER, start); type->unresolved.path = path; type->unresolved.name_loc = context->tok; func->func.type_parent = type; @@ -1963,15 +1977,17 @@ static Expr *parse_type_access(Context *context, TypeInfo *type) */ Expr *parse_type_identifier_with_path(Context *context, Path *path) { - TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER); + TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER, path ? path->span : context->tok.span ); type->unresolved.path = path; type->unresolved.name_loc = context->tok; advance_and_verify(context, TOKEN_TYPE_IDENT); + RANGE_EXTEND_PREV(type); if (context->tok.type == TOKEN_LBRACE) { Expr *expr = EXPR_NEW_TOKEN(EXPR_STRUCT_VALUE, context->tok); expr->struct_value_expr.type = type; expr->struct_value_expr.init_expr = TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr); + return expr; } EXPECT_OR(TOKEN_DOT, &poisoned_expr); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index b7401b142..da96088b1 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -3,6 +3,7 @@ // a copy of which can be found in the LICENSE file. #include "sema_internal.h" +#include "bigint.h" static inline bool sema_analyse_error(Context *context __unused, Decl *decl) @@ -241,41 +242,81 @@ static inline bool sema_analyse_typedef(Context *context, Decl *decl) static inline bool sema_analyse_enum(Context *context, Decl *decl) { + // Resolve the type of the enum. if (!sema_resolve_type_info(context, decl->enums.type_info)) return false; - uint64_t value = 0; + Type *type = decl->enums.type_info->type; + Type *canonical = decl->enums.type_info->type; + + // Require an integer type + if (!type_is_integer(canonical)) + { + SEMA_ERROR(decl->enums.type_info, "The enum type must be an integer type not '%s'.", type_to_error_string(type)); + return false; + } + + DEBUG_LOG("* Enum type resolved to %s.", type->name); bool success = true; - VECEACH(decl->enums.values, i) + unsigned enums = vec_size(decl->enums.values); + BigInt value; + BigInt add; + bigint_init_unsigned(&add, 1); + bigint_init_unsigned(&value, 0); + + for (unsigned i = 0; i < enums; i++) { Decl *enum_value = decl->enums.values[i]; + enum_value->type = decl->type; + DEBUG_LOG("* Checking enum constant %s.", enum_value->name); enum_value->enum_constant.ordinal = i; + DEBUG_LOG("* Ordinal: %d", i); assert(enum_value->resolve_status == RESOLVE_NOT_DONE); assert(enum_value->decl_kind == DECL_ENUM_CONSTANT); + + // Start evaluating the constant enum_value->resolve_status = RESOLVE_RUNNING; Expr *expr = enum_value->enum_constant.expr; + + // Create a "fake" expression. + // This will be evaluated later to catch the case if (!expr) { - expr = expr_new(EXPR_CONST, INVALID_RANGE); + expr = expr_new(EXPR_CONST, enum_value->name_span); expr->type = type; - expr->resolve_status = RESOLVE_DONE; - expr->const_expr.type = CONST_INT; - expr->const_expr.i = value; + expr->resolve_status = RESOLVE_NOT_DONE; + bigint_init_bigint(&expr->const_expr.i, &value); + expr->const_expr.kind = TYPE_IXX; + expr->type = type_compint; enum_value->enum_constant.expr = expr; } + + // We try to convert to the desired type. if (!sema_analyse_expr(context, type, expr)) { success = false; enum_value->resolve_status = RESOLVE_DONE; + decl_poison(enum_value); + // Reset! + bigint_init_unsigned(&value, 0); continue; } + assert(type_is_integer(expr->type->canonical)); + + // Here we might have a non-constant value, if (expr->expr_kind != EXPR_CONST) { - SEMA_ERROR(expr, "Expected a constant expression for enum"); + SEMA_ERROR(expr, "Expected a constant expression for enum."); + decl_poison(enum_value); success = false; + // Skip one value. + continue; } + + // Update the value + bigint_add(&value, &expr->const_expr.i, &add); + DEBUG_LOG("* Value: %s", expr_const_to_error_string(&expr->const_expr)); enum_value->resolve_status = RESOLVE_DONE; - enum_value->type = decl->type; } return success; } @@ -393,7 +434,7 @@ bool sema_analyse_decl(Context *context, Decl *decl) { if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl); - DEBUG_LOG("Analyse %s", decl->name); + DEBUG_LOG(">>> Analysing %s.", decl->name); if (decl->resolve_status == RESOLVE_RUNNING) { SEMA_ERROR(decl, "Recursive dependency on %s.", decl->name); @@ -454,5 +495,6 @@ bool sema_analyse_decl(Context *context, Decl *decl) UNREACHABLE } decl->resolve_status = RESOLVE_DONE; + DEBUG_LOG("<<< Analysis of %s successful.", decl->name); return true; } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 9d5bb7619..0b674e414 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3,6 +3,7 @@ // a copy of which can be found in the LICENSE file. #include "sema_internal.h" +#include "bigint.h" /* * TODOs @@ -14,8 +15,8 @@ #define CONST_PROCESS(_op) \ if (both_const(left, right)) { \ switch (left->const_expr.type) { \ -case CONST_INT: expr->const_expr.i = left->const_expr.i _op right->const_expr.i; break; \ -case CONST_FLOAT: expr->const_expr.f = left->const_expr.f _op right->const_expr.f; break; \ +case CONST_INT: int_##_op(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); break; \ +case CONST_FLOAT: float_##_op(&expr->const_expr.f, &left->const_expr.f, &right->const_expr.f); break; \ default: UNREACHABLE } \ expr->expr_kind = EXPR_CONST; \ expr->const_expr.type = left->const_expr.type; \ @@ -299,8 +300,7 @@ static inline bool sema_expr_analyse_error_constant(Context *context, Expr *expr assert(error_constant->resolve_status == RESOLVE_DONE); expr->type = decl->type; expr->expr_kind = EXPR_CONST; - expr->const_expr.type = CONST_INT; - expr->const_expr.i = decl->error_constant.value; + expr_const_set_int(&expr->const_expr, decl->error_constant.value, TYPE_U32); return true; } } @@ -440,6 +440,7 @@ static Decl *sema_analyse_init_identifier(Context *context, Decl *strukt, Expr * expr->resolve_status = RESOLVE_DONE; return expr->identifier_expr.decl; } + static Decl *sema_analyse_init_access(Context *context, Decl *strukt, Expr *access_expr) { assert(access_expr->resolve_status == RESOLVE_NOT_DONE); @@ -823,6 +824,10 @@ static bool binary_arithmetic_promotion(Expr *left, Expr *right, Type *left_type return max && type_is_number(max) && cast_implicit(left, max) && cast_implicit(right, max); } +/** + * Analyse a - b + * @return true if analysis succeeded + */ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; @@ -848,7 +853,23 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR; - CONST_PROCESS(-); + if (both_const(left, right)) + { + switch (left->const_expr.kind) + { + case ALL_INTS: + bigint_sub(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + break; + case ALL_FLOATS: + // IMPROVE precision. + expr->const_expr.f = left->const_expr.f - right->const_expr.f; + break; + default: + UNREACHABLE + } + expr->expr_kind = EXPR_CONST; + expr->const_expr.kind = left->const_expr.kind; + } expr->type = left->type; return true; @@ -885,7 +906,22 @@ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr * if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR; - CONST_PROCESS(+); + if (both_const(left, right)) + { + switch (left->const_expr.kind) + { + case ALL_INTS: + bigint_add(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + break; + case ALL_FLOATS: + expr->const_expr.f = left->const_expr.f + right->const_expr.f; + break; + default: + UNREACHABLE + } + expr->expr_kind = EXPR_CONST; + expr->const_expr.kind = left->const_expr.kind; + } expr->type = left->type; return true; @@ -904,10 +940,40 @@ static bool sema_expr_analyse_mult(Context *context, Type *to, Expr *expr, Expr if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR; - CONST_PROCESS(*) - + bool is_mod = expr->binary_expr.operator == BINARYOP_MULT_MOD; expr->type = left->type; - return true; + + // Might have changed + left_type = left->type->canonical; + + if (is_mod && !type_is_any_integer(left_type)) + { + SEMA_ERROR(expr, "You can only use *% with integer types."); + return false; + } + + if (!both_const(left, right)) return true; + + expr->expr_kind = EXPR_CONST; + expr->const_expr.kind = left->const_expr.kind; + + switch (left->const_expr.kind) + { + case ALL_SIGNED_INTS: + // Do mod mult + if (is_mod && left_type != type_compint) + { + bigint_mul_wrap(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i, is_mod, left_type->builtin.bitsize); + return true; + } + bigint_mul(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + return true; + case ALL_FLOATS: + expr->const_expr.f = left->const_expr.f * right->const_expr.f; + return true; + default: + UNREACHABLE + } ERR: SEMA_ERROR(expr, "Cannot multiply '%s' and '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); @@ -923,19 +989,21 @@ static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr * if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR; + expr->type = left->type; + // Check null if (right->expr_kind == EXPR_CONST) { - switch (right->const_expr.type) + switch (right->const_expr.kind) { - case CONST_INT: - if (right->const_expr.i == 0) + case ALL_INTS: + if (bigint_cmp_zero(&right->const_expr.i) == CMP_EQ) { SEMA_ERROR(right, "Division by zero not allowed."); return false; } break; - case CONST_FLOAT: + case ALL_FLOATS: if (right->const_expr.f == 0) { SEMA_ERROR(right, "Division by zero not allowed."); @@ -947,10 +1015,21 @@ static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr * } } - CONST_PROCESS(/) - expr->type = left->type; - return true; + if (!both_const(left, right)) return true; + + switch (left->const_expr.kind) + { + case ALL_INTS: + bigint_div_floor(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + return true; + case ALL_FLOATS: + expr->const_expr.f = left->const_expr.f / right->const_expr.f; + return true; + default: + UNREACHABLE + } + ERR: SEMA_ERROR(expr, "Cannot divide '%s' by '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); @@ -964,7 +1043,7 @@ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr * if (!type_is_integer(right->type->canonical) || !type_is_integer(left->type->canonical)) return sema_type_error_on_binop(expr); - if (right->expr_kind == EXPR_CONST && right->const_expr.i == 0) + if (right->expr_kind == EXPR_CONST && bigint_cmp_zero(&right->const_expr.i) == CMP_EQ) { SEMA_ERROR(expr->binary_expr.right, "Cannot perform mod by zero."); return false; @@ -974,7 +1053,7 @@ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr * { // TODO negative expr_replace(expr, left); - expr->const_expr.i %= right->const_expr.i; + bigint_mod(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); return expr; } @@ -990,7 +1069,8 @@ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr * static bool sema_expr_analyse_mod_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { - TODO } + TODO +} static bool sema_expr_analyse_bit(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) @@ -1010,13 +1090,13 @@ static bool sema_expr_analyse_bit(Context *context, Type *to, Expr *expr, Expr * switch (expr->binary_expr.operator) { case TOKEN_AMP: - expr->const_expr.i &= right->const_expr.i; + bigint_and(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); break; case TOKEN_BIT_XOR: - expr->const_expr.i ^= right->const_expr.i; + bigint_xor(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); break; case TOKEN_BIT_OR: - expr->const_expr.i |= right->const_expr.i; + bigint_or(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); break; default: UNREACHABLE; @@ -1048,7 +1128,8 @@ static bool sema_expr_analyse_shr(Context *context, Type *to, Expr *expr, Expr * if (right->expr_kind == EXPR_CONST) { - if (right->const_expr.i > left->type->canonical->builtin.bitsize) + TODO + //if (int_exceeds(&right->const_expr.i, left->type->canonical->builtin.bitsize)) { SEMA_ERROR(right, "Rightshift exceeds bitsize of '%s'", type_to_error_string(left->type)); return false; @@ -1056,7 +1137,7 @@ static bool sema_expr_analyse_shr(Context *context, Type *to, Expr *expr, Expr * if (left->expr_kind == EXPR_CONST) { expr_replace(expr, left); - expr->const_expr.i >>= right->const_expr.i; + bigint_shr(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); return true; } } @@ -1083,7 +1164,8 @@ static bool sema_expr_analyse_shr_assign(Context *context, Type *to, Expr *expr, if (right->expr_kind == EXPR_CONST) { - if (right->const_expr.i > left->type->canonical->builtin.bitsize) + TODO + //if (int_exceeds(&right->const_expr.i, left->type->canonical->builtin.bitsize)) { SEMA_ERROR(right, "Rightshift exceeds bitsize of '%s'", type_to_error_string(left->type)); return false; @@ -1107,9 +1189,11 @@ static bool sema_expr_analyse_shl(Context *context, Type *to, Expr *expr, Expr * // Next, cast to runtime types, this might be needed for runtime constants, e.g. x << 2 if (!cast_to_runtime(left) || !cast_to_runtime(right)) return false; + expr->type = left->type; if (right->expr_kind == EXPR_CONST) { - if (right->const_expr.i > left->type->canonical->builtin.bitsize) + TODO + //if (int_int_exceeds(&right->const_expr.i, left->type->canonical->builtin.bitsize)) { SEMA_ERROR(right, "Leftshift exceeds bitsize of '%s'", type_to_error_string(left->type)); return false; @@ -1117,12 +1201,20 @@ static bool sema_expr_analyse_shl(Context *context, Type *to, Expr *expr, Expr * if (left->expr_kind == EXPR_CONST) { expr_replace(expr, left); - expr->const_expr.i <<= right->const_expr.i; + if (left->const_expr.kind == TYPE_IXX) + { + bigint_shl(&expr->const_expr.i, &expr->const_expr.i, &right->const_expr.i); + } + else + { + int bit_count = left->type->canonical->builtin.bitsize; + bool is_signed = !type_kind_is_unsigned(left->const_expr.kind); + bigint_shl_trunc(&expr->const_expr.i, &expr->const_expr.i, &right->const_expr.i, bit_count, is_signed); + } return true; } } - expr->type = left->type; return true; ERR: @@ -1139,6 +1231,8 @@ static bool sema_expr_analyse_shl_assign(Context *context, Type *to, Expr *expr, return false; } + expr->type = left->type; + // Check that right side is integer and cast to a runtime type if needed. if (!type_is_integer(right->type->canonical)) return sema_type_error_on_binop(expr); @@ -1146,14 +1240,20 @@ static bool sema_expr_analyse_shl_assign(Context *context, Type *to, Expr *expr, if (right->expr_kind == EXPR_CONST) { - if (right->const_expr.i > left->type->canonical->builtin.bitsize) + if (right->const_expr.i.is_negative) { - SEMA_ERROR(right, "Leftshift exceeds bitsize of '%s'", type_to_error_string(left->type)); + SEMA_ERROR(right, "Leftshift constant was negative (%s). It must be zero or more.", expr_const_to_error_string(&right->const_expr)); return false; } + BigInt max; + bigint_init_unsigned(&max, left->type->canonical->builtin.bitsize); + if (bigint_cmp(&right->const_expr.i, &max) == CMP_GT) + { + // IMPROVE suppress in macro? + SEMA_ERROR(right, "Leftshift exceeds bitsize of '%s', it cannot be higher.", type_to_error_string(left->type)); + return expr_poison(right); + } } - - expr->type = left->type; return true; } @@ -1190,69 +1290,76 @@ static bool sema_expr_analyse_and_assign(Context *context, Type *to, Expr *expr, static bool sema_expr_analyse_or_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { TODO } +static void cast_to_max_bit_size(Expr *left, Expr *right, Type *left_type, Type *right_type) +{ + int bit_size_left = left_type->builtin.bitsize; + int bit_size_right = right_type->builtin.bitsize; + assert(bit_size_left && bit_size_right); + if (bit_size_left == bit_size_right) return; + if (bit_size_left < bit_size_right) + { + Type *to = left->type->type_kind < TYPE_U8 + ? type_signed_int_by_bitsize(bit_size_right) + : type_unsigned_int_by_bitsize(bit_size_right); + bool success = cast_implicit(left, to); + assert(success); + return; + } + Type *to = right->type->type_kind < TYPE_U8 + ? type_signed_int_by_bitsize(bit_size_right) + : type_unsigned_int_by_bitsize(bit_size_right); + bool success = cast_implicit(right, to); + assert(success); +} + static bool sema_expr_analyse_comp(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; Type *left_type = left->type->canonical; Type *right_type = right->type->canonical; - Type *max = type_find_max_type(left_type, right_type); - bool success = max && cast_implicit(left, max) && cast_implicit(right, max); - if (!success) + + // Handle the case of signed comparisons. + if ((type_is_unsigned(left_type) && type_is_signed(right_type)) + || (type_is_signed(left_type) && type_is_unsigned(right_type))) { - SEMA_ERROR(expr, "Cannot implicitly convert types to evaluate '%s' %s '%s'", type_to_error_string(left_type), token_type_to_string(binaryop_to_token(expr->binary_expr.operator)), type_to_error_string(right_type)); - return false; + cast_to_max_bit_size(left, right, left_type, right_type); } + else + { + // Alternatively, find and promote to max + Type *max = type_find_max_type(left_type, right_type); + bool success = max && cast_implicit(left, max) && cast_implicit(right, max); + if (!success) + { + SEMA_ERROR(expr, "Cannot implicitly convert types to evaluate '%s' %s '%s'", type_to_error_string(left_type), token_type_to_string(binaryop_to_token(expr->binary_expr.operator)), type_to_error_string(right_type)); + return false; + } + } + if (both_const(left, right)) { -#define COMP(_op_) \ -switch (left->const_expr.type) { \ -case CONST_FLOAT: expr->const_expr.b = left->const_expr.f _op_ right->const_expr.f; break; \ -case CONST_BOOL: expr->const_expr.b = left->const_expr.b _op_ right->const_expr.b; break; \ -case CONST_INT: expr->const_expr.b = left->const_expr.i _op_ right->const_expr.i; break; \ -default: UNREACHABLE } break; - switch (expr->binary_expr.operator) + switch (left->const_expr.kind) { - case BINARYOP_GT: - COMP(>) - case BINARYOP_GE: - COMP(>=) - case BINARYOP_LT: - COMP(<) - case BINARYOP_LE: - COMP(<=) - case BINARYOP_EQ: - // TODO elsewhere - COMP(==) - case BINARYOP_NE: - // TODO elsewhere - COMP(!=) + case TYPE_BOOL: + SEMA_ERROR(expr, "Cannot compare booleans, convert them into integers first."); + return false; + case ALL_FLOATS: + case ALL_INTS: + expr->const_expr.b = expr_const_compare(&left->const_expr, &right->const_expr, expr->binary_expr.operator); + return true; + case TYPE_STRING: + SEMA_ERROR(expr, "Cannot compare strings."); + return false; default: UNREACHABLE } -#undef COMP - expr->const_expr.type = CONST_BOOL; + expr->const_expr.kind = TYPE_BOOL; expr->expr_kind = EXPR_CONST; } expr->type = type_bool; return true; } -#define SEMA_ANALYSE_CMP(op) { \ -if (!cast_arithmetic(left, right, #op)) return false;\ -if (!cast_arithmetic(right, left, #op)) return false;\ -if (both_const(left, right)) { \ -switch (left->const_expr.type) { \ -case CONST_FLOAT: expr->const_expr.b = left->const_expr.f op right->const_expr.f; break; \ -case CONST_BOOL: expr->const_expr.b = left->const_expr.b op right->const_expr.b; break; \ -case CONST_INT: expr->const_expr.b = left->const_expr.i op right->const_expr.i; break; \ -default: UNREACHABLE }\ -expr->const_expr.type = CONST_BOOL;\ -expr->expr_kind = EXPR_CONST;\ -}\ -if (!cast_to_runtime(left) || !cast_to_runtime(right)) return false;\ -expr->type = type_bool;\ -return true; } - static bool sema_expr_analyse_elvis(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { TODO } @@ -1298,41 +1405,74 @@ static bool sema_expr_analyse_neg(Context *context, Type *to, Expr *expr, Expr * expr->type = inner->type; return true; } - // TODO UXX CAP + bool is_negmod = expr->unary_expr.operator == UNARYOP_NEGMOD; expr_replace(expr, inner); - switch (expr->const_expr.type) + switch (expr->const_expr.kind) { - case CONST_INT: - expr->const_expr.i = ~expr->const_expr.i; - break; - case CONST_FLOAT: - expr->const_expr.f = -expr->const_expr.i; + case ALL_INTS: + if (is_negmod) + { + if (inner->expr_kind != TYPE_IXX) + { + SEMA_ERROR(expr, "Cannot use –% on compile time integers, you need to first cast it to an integer type e.g. -%cast(-128, char)."); + + // Continue parsing, pretending this is a - + bigint_negate(&expr->const_expr.i, &inner->const_expr.i); + return true; + } + bigint_negate_wrap(&expr->const_expr.i, + &inner->const_expr.i, + inner->type->canonical->builtin.bitsize); + return true; + } + bigint_negate(&expr->const_expr.i, &inner->const_expr.i); + if (expr_const_int_overflowed(&expr->const_expr)) + { + SEMA_ERROR(expr, "Negating %s overflows '%s'.", expr_const_to_error_string(&expr->const_expr), type_to_error_string(expr->type)); + return false; + } + return true; + case ALL_FLOATS: + expr->const_expr.f = -expr->const_expr.f; break; default: UNREACHABLE } return true; } + +/** + * Analyse ~x and ~123 + * + * @return + */ static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Expr *inner) { Type *canonical = inner->type->canonical; - if (!type_is_integer(canonical) && canonical != type_bool) + if (!type_is_any_integer(canonical) && canonical != type_bool) { - SEMA_ERROR(expr, "Cannot bit negate %s.", type_to_error_string(inner->type)); + SEMA_ERROR(expr, "Cannot bit negate '%s'.", type_to_error_string(inner->type)); + return false; } + + // The simple case, non-const. if (inner->expr_kind != EXPR_CONST) { expr->type = inner->type; return true; } + expr_replace(expr, inner); - // TODO UXX CAP - switch (expr->const_expr.type) + switch (expr->const_expr.kind) { - case CONST_INT: - expr->const_expr.i = ~expr->const_expr.i; + case ALL_SIGNED_INTS: + case ALL_UNSIGNED_INTS: + bigint_negate_wrap(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize); break; - case CONST_BOOL: + case TYPE_IXX: + bigint_negate(&expr->const_expr.i, &inner->const_expr.i); + break; + case TYPE_BOOL: expr->const_expr.b = !expr->const_expr.b; break; default: @@ -1340,30 +1480,30 @@ static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Ex } return true; } + static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr *inner) { expr->type = type_bool; if (inner->expr_kind == EXPR_CONST) { - switch (expr->const_expr.type) + switch (expr->const_expr.kind) { - case CONST_NIL: - expr->const_expr.b = true; + case ALL_INTS: + expr->const_expr.b = bigint_cmp_zero(&inner->const_expr.i) == CMP_EQ; break; - case CONST_BOOL: + case TYPE_BOOL: expr->const_expr.b = !inner->const_expr.b; break; - case CONST_INT: - expr->const_expr.b = inner->const_expr.i == 0; + case ALL_FLOATS: + expr->const_expr.b = inner->const_expr.f == 0.0; break; - case CONST_FLOAT: - expr->const_expr.b = inner->const_expr.f == 0; - break; - case CONST_STRING: + case TYPE_STRING: expr->const_expr.b = !inner->const_expr.string.len; break; + default: + UNREACHABLE } - expr->const_expr.type = CONST_BOOL; + expr->const_expr.kind = TYPE_BOOL; expr->expr_kind = EXPR_CONST; return true; } @@ -1372,7 +1512,6 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr * { case TYPE_POISONED: case TYPE_IXX: - case TYPE_UXX: case TYPE_FXX: case TYPE_TYPEDEF: case TYPE_ERROR_UNION: @@ -1411,12 +1550,12 @@ static inline bool sema_expr_analyse_incdec(Context *context, Type *to, Expr *ex { if (!expr_is_ltype(inner)) { - SEMA_ERROR(inner, "Expression cannot be assigned to"); + SEMA_ERROR(inner, "Expression cannot be assigned to."); return false; } if (!type_is_number(inner->type->canonical) && inner->type->canonical->type_kind != TYPE_POINTER) { - SEMA_ERROR(inner, "Expression must be a number or a pointer"); + SEMA_ERROR(inner, "Expression must be a number or a pointer."); return false; } expr->type = inner->type; @@ -1436,7 +1575,6 @@ static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *ex return sema_expr_analyse_assign(context, to, expr, left, right); case BINARYOP_MULT: case BINARYOP_MULT_MOD: - // Todo treat mod differently return sema_expr_analyse_mult(context, to, expr, left, right); case BINARYOP_MULT_ASSIGN: case BINARYOP_MULT_MOD_ASSIGN: @@ -1518,6 +1656,7 @@ static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *exp case UNARYOP_ADDR: return sema_expr_analyse_addr(context, to, expr, inner); case UNARYOP_NEG: + case UNARYOP_NEGMOD: return sema_expr_analyse_neg(context, to, expr, inner); case UNARYOP_BITNEG: return sema_expr_analyse_bit_not(context, to, expr, inner); @@ -1923,7 +2062,7 @@ static inline bool sema_expr_analyse_macro_call2(Context *context, Type *to, Exp TODO return success; -}; +} static inline bool sema_expr_analyse_macro_expr(Context *context, Type *to, Expr *expr) { diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 650f53a81..0456c08d7 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -3,6 +3,7 @@ // a copy of which can be found in the LICENSE file. #include "sema_internal.h" +#include "bigint.h" #pragma mark --- Context help functions @@ -48,10 +49,7 @@ static inline void context_pop_defers_to(Context *context, DeferList *list) static inline void context_add_exit(Context *context, ExitType exit) { - if (context->current_scope->exit < exit) - { - context->current_scope->exit = exit; - } + if (!context->current_scope->exit) context->current_scope->exit = exit; } void context_pop_scope(Context *context) @@ -61,7 +59,7 @@ void context_pop_scope(Context *context) ExitType exit_type = context->current_scope->exit; assert (context->current_scope->defers.end == context->current_scope->defers.start); context->current_scope--; - if (context->current_scope->exit < exit_type) + if (!context->current_scope->exit && exit_type) { context->current_scope->exit = exit_type; } @@ -125,7 +123,6 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) } UPDATE_EXIT(EXIT_RETURN); - context->current_scope->exit = EXIT_RETURN; Type *expected_rtype = context->rtype; Expr *return_expr = statement->return_stmt.expr; @@ -145,6 +142,11 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) } return true; } + if (expected_rtype == type_void) + { + SEMA_ERROR(statement, "You can't return a value from a void function, you need to add a return type."); + return false; + } if (!sema_analyse_expr(context, expected_rtype, return_expr)) return false; if (!expected_rtype) { @@ -171,14 +173,6 @@ static inline bool sema_analyse_var_decl(Context *context, Decl *decl) return true; } -static inline Ast *convert_expr_to_ast(Expr *expr) -{ - Ast *ast = AST_NEW(AST_EXPR_STMT, expr->span); - ast->expr_stmt = expr; - return ast; -} - - static inline bool sema_analyse_decl_expr_list(Context *context, Ast *stmt) { @@ -305,11 +299,6 @@ static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement) return true; } -static inline bool sema_analyse_default_stmt(Context *context __unused, Ast *statement) -{ - SEMA_ERROR(statement, "Unexpected 'default' outside of switch"); - return false; -} static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) { @@ -398,11 +387,17 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) success = false; } } + ExitType prev_exit = context->current_scope->exit; success = success && sema_analyse_statement(context, statement->if_stmt.then_body); + ExitType if_exit = context->current_scope->exit; + ExitType else_exit = prev_exit; if (statement->if_stmt.else_body) { + context->current_scope->exit = prev_exit; success = success && sema_analyse_statement(context, statement->if_stmt.else_body); + else_exit = context->current_scope->exit; } + context->current_scope->exit = else_exit < if_exit ? else_exit : if_exit; context_pop_defers_and_replace_ast(context, statement); context_pop_scope(context); return success; @@ -423,6 +418,7 @@ static inline bool sema_analyse_label(Context *context, Ast *statement) return false; } } + context->current_scope->exit = EXIT_NONE; vec_add(context->labels, statement); VECEACH(context->gotos, i) { @@ -456,6 +452,7 @@ static bool sema_analyse_break_stmt(Context *context, Ast *statement) SEMA_ERROR(statement, "'break' is not allowed here."); return false; } + UPDATE_EXIT(EXIT_BREAK); DynamicScope *scope = context->current_scope; statement->break_stmt.defers.start = scope->defers.start; while (!(scope->flags_created & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) @@ -473,6 +470,7 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement) SEMA_ERROR(statement, "'next' is not allowed here."); return false; } + UPDATE_EXIT(EXIT_NEXT); DynamicScope *scope = context->current_scope; statement->next_stmt.defers.start = scope->defers.start; while (!(scope->flags_created & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise) @@ -483,11 +481,6 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement) return true; } -static bool sema_analyse_case_stmt(Context *context, Ast *statement) -{ - SEMA_ERROR(statement, "Unexpected 'case' outside of switch"); - return false; -} static bool sema_analyse_continue_stmt(Context *context, Ast *statement) { @@ -496,6 +489,7 @@ static bool sema_analyse_continue_stmt(Context *context, Ast *statement) SEMA_ERROR(statement, "'continue' is not allowed here."); return false; } + UPDATE_EXIT(EXIT_CONTINUE); DynamicScope *scope = context->current_scope; statement->continue_stmt.defers.start = scope->defers.start; while (!(scope->flags_created & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) @@ -551,24 +545,39 @@ static bool sema_analyse_ct_if_stmt(Context *context, Ast *statement) } -static bool sema_analyse_case_expr(Context *context, Ast *case_stmt) +static bool sema_analyse_case_expr(Context *context, Type* to_type, Ast *case_stmt) { Expr *case_expr = case_stmt->case_stmt.expr; // TODO handle enums - if (!sema_analyse_expr(context, NULL, case_expr)) return false; + // TODO string expr + if (!sema_analyse_expr(context, to_type, case_expr)) return false; if (case_expr->expr_kind != EXPR_CONST) { SEMA_ERROR(case_expr, "This must be a constant expression."); return false; } - if (case_expr->const_expr.type != CONST_INT) + + // As a special case, we handle bools, converting them to 0 / 1 + if (case_expr->const_expr.kind == TYPE_BOOL) { - SEMA_ERROR(case_expr, "The 'case' value must be an integer constant."); - return false; + case_stmt->case_stmt.value_type = CASE_VALUE_UINT; + case_stmt->case_stmt.val = case_expr->const_expr.b ? 1 : 0; + return true; } - assert(case_expr->const_expr.type == CONST_INT); + + // TODO check for int + /* + if (case_expr->const_expr.kind < TYPE__!= CONST_INT && case_expr->const_expr.type != CONST_BOOL) + { + SEMA_ERROR(case_expr, "The 'case' value must be a boolean or integer constant."); + return false; + }*/ + case_stmt->case_stmt.value_type = type_is_signed(case_expr->type->canonical) ? CASE_VALUE_INT : CASE_VALUE_UINT; - uint64_t val = case_expr->const_expr.i; + assert(case_expr->type->canonical->type_kind != TYPE_IXX); + // TODO this is incorrect + TODO + uint64_t val = (uint64_t)bigint_as_signed(&case_expr->const_expr.i); case_stmt->case_stmt.val = val; return true; } @@ -586,12 +595,6 @@ static inline bool sema_analyse_compound_statement_no_scope(Context *context, As } } context_pop_defers_to(context, &compound_statement->compound_stmt.defer_list); - - /* - if (parent->exit < compound_statement->exit) - { - parent->exit = compound_statement->exit; - }*/ return all_ok; } @@ -610,6 +613,7 @@ static inline Type *ast_cond_type(Ast *ast) } } + static bool sema_analyse_switch_stmt(Context *context, Ast *statement) { context_push_scope(context); @@ -617,20 +621,27 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) bool success = sema_analyse_cond(context, cond, false); Type *switch_type = ast_cond_type(cond)->canonical; - if (!type_is_integer(switch_type)) + if (switch_type == type_bool || !type_is_integer(switch_type)) { SEMA_ERROR(cond, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type)); return false; } Ast *default_case = NULL; assert(context->current_scope->defers.start == context->current_scope->defers.end); - VECEACH(statement->switch_stmt.cases, i) + + // TODO enum, exhaustive cases. + ExitType prev_exit = context->current_scope->exit; + bool exhaustive = false; + ExitType lowest_exit = EXIT_NONE; + unsigned cases = vec_size(statement->switch_stmt.cases); + for (unsigned i = 0; i < cases; i++) { + context->current_scope->exit = prev_exit; Ast *stmt = statement->switch_stmt.cases[i]; switch (stmt->ast_kind) { case AST_CASE_STMT: - if (!sema_analyse_case_expr(context, stmt)) + if (!sema_analyse_case_expr(context, switch_type, stmt)) { success = false; break; @@ -647,6 +658,7 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) } break; case AST_DEFAULT_STMT: + exhaustive = true; if (default_case) { SEMA_ERROR(stmt, "'default' may only appear once in a single 'switch', please remove one."); @@ -658,15 +670,41 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) default: UNREACHABLE; } - context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_NEXT); + if (i == cases - 1) + { + context_push_scope_with_flags(context, SCOPE_BREAK); + } + else + { + context_push_scope_with_flags(context, SCOPE_NEXT | SCOPE_BREAK); + } success = success && sema_analyse_compound_statement_no_scope(context, stmt->case_stmt.body); + ExitType case_exit = context->current_scope->exit; + if (case_exit != lowest_exit) + { + switch (case_exit) + { + case EXIT_NONE: + case EXIT_BREAK: + lowest_exit = EXIT_BREAK; + break; + case EXIT_NEXT: + // We ignore this completely + break; + default: + if (!lowest_exit || lowest_exit > case_exit) lowest_exit = case_exit; + break; + } + } context_pop_scope(context); } context_pop_defers_and_replace_ast(context, statement); + if (lowest_exit <= EXIT_BREAK) lowest_exit = prev_exit; + context->current_scope->exit = exhaustive ? lowest_exit : EXIT_NONE; context_pop_scope(context); if (!success) return false; // Is this a typeless switch value? - if (switch_type->type_kind == TYPE_UXX || switch_type->type_kind == TYPE_IXX) + if (switch_type->type_kind == TYPE_IXX) { TODO @@ -701,6 +739,7 @@ static bool sema_analyse_try_stmt(Context *context, Ast *statement) static bool sema_analyse_throw_stmt(Context *context, Ast *statement) { Expr *throw_value = statement->throw_stmt.throw_value; + UPDATE_EXIT(EXIT_THROW); if (!sema_analyse_expr(context, NULL, throw_value)) return false; Type *type = throw_value->type->canonical; if (type->type_kind != TYPE_ERROR) @@ -713,7 +752,7 @@ static bool sema_analyse_throw_stmt(Context *context, Ast *statement) SEMA_ERROR(statement, "This 'throw' is not handled, please add a 'throws %s' clause to the function signature or use try-catch.", type->name); return false; } - VECADD(context->errors, type->decl); + vec_add(context->errors, type->decl); return true; } @@ -749,7 +788,8 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement case AST_BREAK_STMT: return sema_analyse_break_stmt(context, statement); case AST_CASE_STMT: - return sema_analyse_case_stmt(context, statement); + SEMA_ERROR(statement, "Unexpected 'case' outside of switch"); + return false; case AST_CATCH_STMT: return sema_analyse_catch_stmt(context, statement); case AST_COMPOUND_STMT: @@ -761,7 +801,8 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement case AST_DECLARE_STMT: return sema_analyse_declare_stmt(context, statement); case AST_DEFAULT_STMT: - return sema_analyse_default_stmt(context, statement); + SEMA_ERROR(statement, "Unexpected 'default' outside of switch"); + return false; case AST_DEFER_STMT: return sema_analyse_defer_stmt(context, statement); case AST_DO_STMT: @@ -855,15 +896,18 @@ bool sema_analyse_function_body(Context *context, Decl *func) func->func.annotations = CALLOCS(*func->func.annotations); context_push_scope(context); Decl **params = func->func.function_signature.params; + assert(context->current_scope == &context->scopes[1]); VECEACH(params, i) { if (!sema_add_local(context, params[i])) return false; } if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false; - if (context->current_scope->exit != EXIT_RETURN) + assert(context->current_scope == &context->scopes[1]); + if (context->current_scope->exit != EXIT_RETURN && context->current_scope->exit != EXIT_THROW && context->current_scope->exit != EXIT_GOTO) { if (func->func.function_signature.rtype->type->canonical != type_void) { + // IMPROVE better pointer to end. SEMA_ERROR(func, "Missing return statement at the end of the function."); return false; } diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index b02ff3d17..5c3a2a98e 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -3,6 +3,8 @@ // a copy of which can be found in the LICENSE file. #include "sema_internal.h" +#include "compiler_internal.h" +#include "bigint.h" static inline bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info) @@ -23,6 +25,7 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type) { return type_info_poison(type); } + uint64_t len = 0; if (type->array.len) { if (!sema_analyse_expr(context, type_usize, type->array.len)) return type_info_poison(type); @@ -31,9 +34,10 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type) SEMA_ERROR(type->array.len, "Expected a constant value as array size."); return type_info_poison(type); } + len = bigint_as_unsigned(&type->array.len->const_expr.i); } assert(!type->array.len || type->array.len->expr_kind == EXPR_CONST); - type->type = type_get_array(type->array.base->type, type->array.len ? type->array.len->const_expr.i : 0); + type->type = type_get_array(type->array.base->type, len); type->resolve_status = RESOLVE_DONE; return true; } diff --git a/src/compiler/types.c b/src/compiler/types.c index ba91af6b5..bdcbb0b2e 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -70,7 +70,6 @@ const char *type_to_error_string(Type *type) case TYPE_U16: case TYPE_U32: case TYPE_U64: - case TYPE_UXX: case TYPE_F32: case TYPE_F64: case TYPE_FXX: @@ -197,7 +196,7 @@ size_t type_size(Type *canonical) case TYPE_F64: return canonical->builtin.bytesize; case TYPE_IXX: - case TYPE_UXX: + return 4; case TYPE_FXX: return 8; case TYPE_FUNC: @@ -409,8 +408,7 @@ type_create(#_name, &_shortname, &type_ ## _name, _type, _bits, target->align_mi create_type_cache(type_void); type_void->type_cache[0] = &t_voidstar; t_voidstar.pointer = type_void; - type_create("compint", &t_ixx, &type_compint, TYPE_IXX, 64, 0, 0); - type_create("compuint", &t_uxx, &type_compuint, TYPE_UXX, 64, 0, 0); + type_create("compint", &t_ixx, &type_compint, TYPE_IXX, 32, 0, 0); type_create("compfloat", &t_fxx, &type_compfloat, TYPE_FXX, 64, 0, 0); type_create_alias("usize", &t_usz, &type_usize, type_unsigned_int_by_bitsize(target->width_pointer)); @@ -471,46 +469,39 @@ bool type_may_have_method_functions(Type *type) typedef enum { L, - LS, R, - RS, FL, + X, } MaxType; Type *type_find_max_num_type(Type *num_type, Type *other_num) { - if (other_num->type_kind < TYPE_BOOL || other_num->type_kind > TYPE_FXX) return NULL; - assert(num_type->type_kind >= TYPE_BOOL && num_type->type_kind <= TYPE_FXX); - static MaxType max_conv[TYPE_FXX - TYPE_BOOL + 1][TYPE_FXX - TYPE_BOOL + 1] = { - //Bool I8 I16 I32 I64 IXX U8 U16 U32 U64 UXX F32 F64 FXX - { L, R, R, R, R, L, R, R, R, R, L, R, R, FL }, // Bool - { L, L, R, R, R, L, L, RS, RS, RS, L, R, R, FL }, // I8 - { L, L, L, R, R, L, L, L, RS, RS, L, R, R, FL }, // I16 - { L, L, L, L, R, L, L, L, L, RS, L, R, R, FL }, // I32 - { L, L, L, L, L, L, L, L, L, L, L, R, R, FL }, // I64 - { R, R, R, R, R, L, RS, RS, RS, RS, L, R, R, R }, // IXX - { L, R, R, R, R, LS, L, R, R, R, L, R, R, FL }, // U8 - { L, LS, R, R, R, LS, L, L, R, R, L, R, R, FL }, // U16 - { L, LS, LS, R, R, L, L, L, L, R, L, R, R, FL }, // U32 - { L, LS, LS, LS, R, L, L, L, L, L, L, R, R, FL }, // U64 - { R, R, R, R, R, R, R, R, R, R, L, R, R, R }, // UXX - { L, L, L, L, L, L, L, L, L, L, L, L, R, L }, // F32 - { L, L, L, L, L, L, L, L, L, L, L, L, L, L }, // F32 - { FL, FL, FL, FL, FL, FL, FL, FL, FL, FL, L, R, R, L }, // FXX + if (other_num->type_kind < TYPE_I8 || other_num->type_kind > TYPE_FXX) return NULL; + assert(num_type->type_kind >= TYPE_I8 && num_type->type_kind <= TYPE_FXX); + static MaxType max_conv[TYPE_FXX - TYPE_I8 + 1][TYPE_FXX - TYPE_BOOL + 1] = { + // I8 I16 I32 I64 U8 U16 U32 U64 IXX F32 F64 FXX + { L, R, R, R, X, X, X, X, L, R, R, FL }, // I8 + { L, L, R, R, L, X, X, X, L, R, R, FL }, // I16 + { L, L, L, R, L, L, X, X, L, R, R, FL }, // I32 + { L, L, L, L, L, L, L, X, L, R, R, FL }, // I64 + { X, R, R, R, L, R, R, R, L, R, R, FL }, // U8 + { X, X, R, R, L, L, R, R, L, R, R, FL }, // U16 + { X, X, X, R, L, L, L, R, L, R, R, FL }, // U32 + { X, X, X, X, L, L, L, L, L, R, R, FL }, // U64 + { R, R, R, R, R, R, R, R, L, R, R, R }, // IXX + { L, L, L, L, L, L, L, L, L, L, R, L }, // F32 + { L, L, L, L, L, L, L, L, L, L, L, L }, // F64 + { FL, FL, FL, FL, FL, FL, FL, FL, FL, R, R, L }, // FXX }; - MaxType conversion = max_conv[num_type->type_kind - TYPE_BOOL][other_num->type_kind - TYPE_BOOL]; + MaxType conversion = max_conv[num_type->type_kind - TYPE_I8][other_num->type_kind - TYPE_I8]; switch (conversion) { + case X: + return NULL; case L: - assert (num_type->type_kind != TYPE_FXX); return num_type; case R: - assert (other_num->type_kind != TYPE_FXX); return other_num; - case LS: - return type_signed_int_by_bitsize(num_type->builtin.bytesize * 8); - case RS: - return type_signed_int_by_bitsize(other_num->builtin.bytesize * 8); case FL: return type_double; default: @@ -619,8 +610,8 @@ Type *type_find_max_type(Type *type, Type *other) { case TYPE_POISONED: case TYPE_VOID: - return NULL; case TYPE_BOOL: + return NULL; case TYPE_I8: case TYPE_I16: case TYPE_I32: @@ -630,7 +621,6 @@ Type *type_find_max_type(Type *type, Type *other) case TYPE_U16: case TYPE_U32: case TYPE_U64: - case TYPE_UXX: case TYPE_F32: case TYPE_F64: case TYPE_FXX: