diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b4c33b12..cdc92238b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.13) project(c3c) + +#set(CMAKE_BUILD_TYPE Release) #set(CMAKE_CXX_FLAGS_RELEASE "-O3") find_package(LLVM REQUIRED CONFIG) diff --git a/resources/testfragments/conversion.c3 b/resources/testfragments/conversion.c3 new file mode 100644 index 000000000..09172de81 --- /dev/null +++ b/resources/testfragments/conversion.c3 @@ -0,0 +1,48 @@ +module conversion; + +func int test(int a0, uint b0, ushort c0, short d0) +{ + int a = 0; + uint b = 0; + ushort c = 0; + short d = 0; + a = a0; + b = b0; + c = c0; + d = d0; + b += 1; + a += 2; + // a += b; Error + a += c; + b += c; + a += d; + // b += d; Error + b = b + b; + a = c + c; + a = c - c; + a = c * c; + a = c / c; + a = c % c; + a = c +% c; + a = c -% c; + a = c *% c; + a = d + cast(c + c, ushort); + // a = c << 2; TODO + //a = c << c; + //a <<= c; + //a >>= c; + //a = c >> c; + // a = 2 << c; TODO + return a; +} + + +func int test_top_down(int a0, uint b0, ushort c0, short d0) +{ + int a = 0; + uint b = 0; + ushort c = 0; + short d = 0; + a = d + cast(c +% c, ushort); + return a; +} \ No newline at end of file diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index fb6a3730a..693bf7240 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -423,6 +423,21 @@ func int xxxx(int x) return 1; } +func int testPointers(int x) +{ + 1 ? 1 : 2; + int y = 0; + int* z = &y; + int d = *(z + y); + isize e = z - &y; + int* eff = &y + 1; + short x1 = 2; + float f = x1 +% x1 + 1.0; + float f2 = x1 -% x1 + 1.0; + usize ef = z - &y > 0 ? 1 : z - &y; + z - &y > 0 ? 1 : z - &y; + return 1; +} func int main(int x) { int efd = 9; @@ -447,9 +462,12 @@ func int main(int x) } } next; + case 1000 >> 2: + printf("case 1000 >> 2\n"); + case (1 << 200) >> 197: + printf("case 1 << 3\n"); default: printf("default\n"); - break; } int aa = x++; int bb = x--; @@ -489,12 +507,12 @@ func int main(int x) if (ef == 4) printf("Works5!\n"); if (ef == 4) printf("Works1!\n"); ef = 0; -/* + byte a = 2; short b = 3; int c = 4; bool eok = true; - long deee = (eok ? a : b) + (eok ? b : c);*/ + long deee = (eok ? a : b) + (eok ? b : c); int i = 0; JUMP: diff --git a/resources/tests/arithmetics.c3 b/resources/tests/arithmetics.c3 new file mode 100644 index 000000000..97cf11bdc --- /dev/null +++ b/resources/tests/arithmetics.c3 @@ -0,0 +1,38 @@ +module arithmetics; + +func void testAdd(int a, int b) +{ + a = a + b; + a = a +% b; + a +%= b; + a += b; + a += 1; + a +%= 1; +} + +func void testSub(int a, int b) +{ + a = a - b; + a = a -% b; + a -%= b; + a -= b; + a -%= 1; + a -= 1; +} + +func void testMult(int a, int b) +{ + a = a * b; + a = a *% b; + a *%= b; + /*a *= b; + a *%= 1; + a *= 1;*/ +} + +func void testDiv(int a, int b) +{ + a = a / b; + a /= b; + a /= 1; +} \ No newline at end of file diff --git a/src/build/build_options.h b/src/build/build_options.h index a28f1ff70..7c1f9645a 100644 --- a/src/build/build_options.h +++ b/src/build/build_options.h @@ -114,6 +114,7 @@ typedef enum TARGET_TYPE_EXECUTABLE, TARGET_TYPE_STATIC_LIB, TARGET_TYPE_DYNAMIC_LIB, + TARGET_TYPE_TEST } TargetType; typedef struct diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 25b0c6a4e..d2be7fee5 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -236,8 +236,6 @@ static BinaryOp assign_binop[BINARYOP_LAST + 1] = { [BINARYOP_SUB_MOD_ASSIGN] = BINARYOP_SUB_MOD, [BINARYOP_DIV_ASSIGN] = BINARYOP_DIV, [BINARYOP_MOD_ASSIGN] = BINARYOP_MOD, - [BINARYOP_AND_ASSIGN] = BINARYOP_AND, - [BINARYOP_OR_ASSIGN] = BINARYOP_OR, [BINARYOP_BIT_AND_ASSIGN] = BINARYOP_BIT_AND, [BINARYOP_BIT_OR_ASSIGN] = BINARYOP_BIT_OR, [BINARYOP_BIT_XOR_ASSIGN] = BINARYOP_BIT_XOR, diff --git a/src/compiler/bigint.c b/src/compiler/bigint.c index 383bb520c..604d3118d 100644 --- a/src/compiler/bigint.c +++ b/src/compiler/bigint.c @@ -61,7 +61,7 @@ static char digit_to_char(uint8_t digit, bool upper) } if (digit <= 35) { - return (char) (digit + (upper ? 'A' : 'a')); + return (char) (digit + (upper ? 'A' : 'a') - 10); } FATAL_ERROR("Can't reach"); } @@ -1579,12 +1579,15 @@ void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift) bigint_init_bigint(dest, op1); return; } + if (op1->digit_count == 0) { bigint_init_unsigned(dest, 0); return; } + const uint64_t *op1_digits = bigint_ptr(op1); + if (op1->digit_count == 1 && shift < 64) { dest->digit = op1_digits[0] << shift; @@ -1596,18 +1599,17 @@ void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift) } } - unsigned digit_shift_count = (unsigned int) (shift / 64); - unsigned leftover_shift_count = (unsigned int) (shift % 64); + uint64_t digit_shift_count = shift / 64; + uint64_t leftover_shift_count = shift % 64; dest->digits = alloc_digits(op1->digit_count + digit_shift_count + 1); dest->digit_count = digit_shift_count; uint64_t carry = 0; for (size_t i = 0; i < op1->digit_count; i += 1) { - uint64_t - digit = op1_digits[i]; + uint64_t digit = op1_digits[i]; dest->digits[dest->digit_count] = carry | (digit << leftover_shift_count); - dest->digit_count += 1; + dest->digit_count++; if (leftover_shift_count > 0) { carry = digit >> (64 - leftover_shift_count); @@ -1654,15 +1656,15 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) if (op1->digit_count == 1) { - dest->digit = op1_digits[0] >> shift_amt; + dest->digit = shift_amt < 64 ? op1_digits[0] >> shift_amt : 0; dest->digit_count = 1; dest->is_negative = op1->is_negative; normalize(dest); return; } - unsigned digit_shift_count = (unsigned int) (shift_amt / 64); - unsigned leftover_shift_count = (unsigned int) (shift_amt % 64); + uint64_t digit_shift_count = shift_amt / 64; + uint64_t leftover_shift_count = shift_amt % 64; if (digit_shift_count >= op1->digit_count) { @@ -1670,19 +1672,26 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) } dest->digit_count = op1->digit_count - digit_shift_count; - dest->digits = alloc_digits(dest->digit_count); - uint64_t - carry = 0; + uint64_t *digits; + if (dest->digit_count == 1) + { + digits = &dest->digit; + } + else + { + digits = alloc_digits(dest->digit_count); + dest->digits = digits; + } + + uint64_t carry = 0; for (size_t op_digit_index = op1->digit_count - 1;;) { - uint64_t - digit = op1_digits[op_digit_index]; + uint64_t digit = op1_digits[op_digit_index]; size_t dest_digit_index = op_digit_index - digit_shift_count; - dest->digits[dest_digit_index] = carry | (digit >> leftover_shift_count); + digits[dest_digit_index] = carry | (digit >> leftover_shift_count); carry = digit << (64 - leftover_shift_count); - if (dest_digit_index == 0) - { break; } + if (dest_digit_index == 0) break; op_digit_index -= 1; } dest->is_negative = op1->is_negative; @@ -1851,7 +1860,7 @@ void bigint_print(BigInt *bigint, uint64_t base) bigint_init_bigint(a, bigint); BigInt base_bi = { 0 }; - bigint_init_unsigned(&base_bi, 10); + bigint_init_unsigned(&base_bi, base); for (;;) { @@ -1903,7 +1912,7 @@ const char *bigint_to_error_string(const BigInt *bigint, uint64_t base) bigint_init_bigint(a, bigint); BigInt base_bi = { 0 }; - bigint_init_unsigned(&base_bi, 10); + bigint_init_unsigned(&base_bi, base); for (;;) { @@ -1934,7 +1943,7 @@ const char *bigint_to_error_string(const BigInt *bigint, uint64_t base) *(current++) = *ptr; } *(current++) = '\0'; - return current; + return out; } void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base) @@ -1966,7 +1975,7 @@ void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base) bigint_init_bigint(a, bigint); BigInt base_bi = { 0 }; - bigint_init_unsigned(&base_bi, 10); + bigint_init_unsigned(&base_bi, base); for (;;) { diff --git a/src/compiler/casts.c b/src/compiler/casts.c index 07b06642b..fb97ea067 100644 --- a/src/compiler/casts.c +++ b/src/compiler/casts.c @@ -7,7 +7,7 @@ #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) +#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) { @@ -34,11 +34,8 @@ static bool sema_type_mismatch(Expr *expr, Type *type, CastType cast_type) case CAST_TYPE_IMPLICIT: action = "implicitly cast"; break; - case CAST_TYPE_IMPLICIT_ASSIGN: - case CAST_TYPE_IMPLICIT_ASSIGN_ADD: - action = "assign"; - break; - + case CAST_TYPE_OPTIONAL_IMPLICIT: + UNREACHABLE } SEMA_ERROR(expr, "Cannot %s '%s' to '%s'.", action, type_to_error_string(expr->type), type_to_error_string(type)); return false; @@ -52,7 +49,11 @@ bool erro(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ bool ptxi(Expr *left, Type *canonical, Type *type, CastType cast_type) { - if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH(); + if (cast_type != CAST_TYPE_EXPLICIT) + { + if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true; + EXIT_T_MISMATCH(); + } RETURN_NON_CONST_CAST(CAST_PTRXI); assert(left->const_expr.kind == TYPE_POINTER); @@ -62,9 +63,9 @@ bool ptxi(Expr *left, Type *canonical, Type *type, CastType cast_type) return true; } + bool ptbo(Expr *left, Type *canonical, Type *type, CastType cast_type) { - if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); RETURN_NON_CONST_CAST(CAST_PTRBOOL); assert(left->const_expr.kind == TYPE_POINTER); @@ -196,7 +197,7 @@ bool boxi(Expr *left, Type *canonical, Type *type, CastType cast_type) */ bool bofp(Expr *left, Type *canonical, Type *type, CastType cast_type) { - if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); +// if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); RETURN_NON_CONST_CAST(CAST_BOOLFP); assert(left->const_expr.kind == TYPE_BOOL); @@ -211,7 +212,7 @@ bool bofp(Expr *left, Type *canonical, Type *type, CastType cast_type) */ bool xibo(Expr *left, Type *canonical, Type *type, CastType cast_type) { - if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); +// if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); RETURN_NON_CONST_CAST(CAST_INTBOOL); expr_const_set_bool(&left->const_expr, bigint_cmp_zero(&left->const_expr.i) != CMP_EQ); @@ -225,7 +226,7 @@ bool xibo(Expr *left, Type *canonical, Type *type, CastType cast_type) */ bool fpbo(Expr *left, Type *canonical, Type *type, CastType cast_type) { - if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); +// if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); RETURN_NON_CONST_CAST(CAST_FPBOOL); expr_const_set_bool(&left->const_expr, left->const_expr.f != 0.0); @@ -242,7 +243,7 @@ bool fpfp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ { bool is_narrowing = from->builtin.bytesize < canonical->builtin.bytesize && from->type_kind != TYPE_FXX; - if (is_narrowing && cast_type == CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); +// if (is_narrowing && cast_type == CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); RETURN_NON_CONST_CAST(CAST_FPFP); @@ -287,8 +288,9 @@ bool ixxxi(Expr *left, Type *canonical, Type *type, CastType cast_type) { 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)) + if (cast_type != CAST_TYPE_EXPLICIT && !bigint_fits_in_bits(&left->const_expr.i, bitsize, is_signed)) { + if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true; SEMA_ERROR(left, "'%s' does not fit into '%s'", expr_const_to_error_string(&left->const_expr), canonical->name); return false; } @@ -308,7 +310,11 @@ bool sisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ { 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 (is_narrowing && cast_type != CAST_TYPE_EXPLICIT) + { + if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true; + EXIT_T_MISMATCH(); + } RETURN_NON_CONST_CAST(CAST_SISI); @@ -328,7 +334,11 @@ bool uiui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ { 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 (is_narrowing && cast_type != CAST_TYPE_EXPLICIT) + { + if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true; + EXIT_T_MISMATCH(); + } RETURN_NON_CONST_CAST(CAST_UIUI); @@ -349,7 +359,11 @@ bool uisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ { 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(); + if (!is_widening && cast_type != CAST_TYPE_EXPLICIT) + { + if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true; + EXIT_T_MISMATCH(); + } RETURN_NON_CONST_CAST(CAST_UISI); @@ -367,7 +381,7 @@ bool uisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ */ 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(); + if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH(); RETURN_NON_CONST_CAST(CAST_SIUI); @@ -455,17 +469,20 @@ bool uius(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ * Cast comptime, signed or unsigned -> pointer. * @return true unless the constant value evaluates to zero. */ -bool xipt(Expr *left, Type *canonical, Type *type) +bool xipt(Expr *left, Type *from, Type *canonical, Type *type, CastType cast_type) { - RETURN_NON_CONST_CAST(CAST_XIPTR); - if (bigint_cmp_zero(&left->const_expr.i) != CMP_EQ) + if (cast_type == CAST_TYPE_EXPLICIT && left->expr_kind == EXPR_CONST) { - SEMA_ERROR(left, "Only constants evaluating to zero can be cast to pointers."); - return false; + RETURN_NON_CONST_CAST(CAST_XIPTR); + if (bigint_cmp_zero(&left->const_expr.i) != CMP_EQ) + { + SEMA_ERROR(left, "Cannot cast non zero constants into pointers."); + return false; + } + expr_const_set_nil(&left->const_expr); + left->type = type; } - expr_const_set_nil(&left->const_expr); - left->type = type; - return true; + return cast(left, type_is_unsigned(from) ? type_usize : type_isize, cast_type); } bool usus(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) @@ -548,6 +565,24 @@ bool cast_to_runtime(Expr *expr) } } +void cast_to_smallest_runtime(Expr *expr) +{ + Type *canonical = expr->type->canonical; + int success; + switch (canonical->type_kind) + { + case TYPE_IXX: + success = cast(expr, type_long, CAST_TYPE_IMPLICIT); + break; + case TYPE_FXX: + success = cast(expr, type_double, CAST_TYPE_IMPLICIT); + break; + default: + return; + } + assert(success && "This should always work"); +} + bool cast_implicit(Expr *expr, Type *to_type) { if (!to_type) return true; @@ -620,7 +655,7 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type) 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); + if (canonical->type_kind == TYPE_POINTER) return xipt(expr, from_type, canonical, to_type, cast_type); break; case TYPE_I8: case TYPE_I16: @@ -630,7 +665,7 @@ bool cast(Expr *expr, Type *to_type, CastType 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, 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); + if (canonical->type_kind == TYPE_POINTER) return xipt(expr, from_type, canonical, to_type, cast_type); break; case TYPE_U8: case TYPE_U16: @@ -640,7 +675,7 @@ bool cast(Expr *expr, Type *to_type, CastType 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, 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); + if (canonical->type_kind == TYPE_POINTER) return xipt(expr, from_type, canonical, to_type, cast_type); break; case TYPE_F32: case TYPE_F64: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 3307cba11..1dfaf6a3f 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -582,16 +582,7 @@ typedef struct typedef struct { - union - { - Expr *expr; - struct - { - uint64_t val; - CaseValueType value_type : 3; - bool has_next; - }; - }; + Expr *expr; // NULL == DEFAULT Ast *body; void *backend_value; } AstCaseStmt; @@ -982,15 +973,13 @@ 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 cast_to_smallest_runtime(Expr *expr); void llvm_codegen(Context *context); void llvm_set_struct_size_alignment(Decl *decl); +bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr); bool sema_analyse_expr(Context *context, Type *to, Expr *expr); bool sema_analyse_decl(Context *context, Decl *decl); @@ -1237,7 +1226,7 @@ static inline bool type_convert_will_trunc(Type *destination, Type *source) return (unsigned)destination->canonical->builtin.bitsize < (unsigned)source->canonical->builtin.bitsize; } -static inline bool type_is_number(Type *type) +static inline bool type_is_numeric(Type *type) { assert(type == type->canonical); return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_FXX; diff --git a/src/compiler/diagnostics.c b/src/compiler/diagnostics.c index 70a4c4ecd..3de6b778c 100644 --- a/src/compiler/diagnostics.c +++ b/src/compiler/diagnostics.c @@ -14,11 +14,6 @@ void diag_reset(void) diagnostics.warnings = 0; } -void reset_panic_mode(void) -{ - diagnostics.panic_mode = false; -} - typedef enum { PRINT_TYPE_ERROR, diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 565ae6e01..746625013 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -57,8 +57,6 @@ typedef enum BINARYOP_SUB_MOD_ASSIGN, BINARYOP_DIV_ASSIGN, BINARYOP_MOD_ASSIGN, - BINARYOP_AND_ASSIGN, - BINARYOP_OR_ASSIGN, BINARYOP_BIT_AND_ASSIGN, BINARYOP_BIT_OR_ASSIGN, BINARYOP_BIT_XOR_ASSIGN, @@ -113,12 +111,6 @@ typedef enum ATTR_UNRESOLVED, } AttrKind; -typedef enum -{ - CASE_VALUE_INT, - CASE_VALUE_UINT, - CASE_VALUE_DEFAULT -} CaseValueType; typedef enum { @@ -175,8 +167,7 @@ typedef enum _CastType { CAST_TYPE_EXPLICIT, CAST_TYPE_IMPLICIT, - CAST_TYPE_IMPLICIT_ASSIGN, - CAST_TYPE_IMPLICIT_ASSIGN_ADD, + CAST_TYPE_OPTIONAL_IMPLICIT, } CastType; @@ -355,7 +346,7 @@ typedef enum TOKEN_RPARBRA, // }) TOKEN_SCOPE, // :: TOKEN_SHR, // >> - TOKEN_SHL, // >> + TOKEN_SHL, // << // Three or more TOKEN_ELIPSIS, // ... @@ -459,16 +450,6 @@ typedef enum TOKEN_VOLATILE, TOKEN_WHILE, - TOKEN_AT_PARAM, // @param - TOKEN_AT_THROWS, // @throws - TOKEN_AT_RETURN, // @return - TOKEN_AT_ENSURE, // @ensure - TOKEN_AT_REQUIRE, // @require - TOKEN_AT_PURE, // @pure - TOKEN_AT_CONST, // @const - TOKEN_AT_REQPARSE, // @reqparse - TOKEN_AT_DEPRECATED, // @deprecated - TOKEN_CT_CASE, // $case TOKEN_CT_DEFAULT, // $default TOKEN_CT_FOR, // $for diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index e6f39acb4..746335d4f 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -519,7 +519,7 @@ Token lexer_scan_token(Lexer *lexer) if (match(lexer, '+')) return parse_nested_comment(lexer); return match(lexer, '=') ? make_token(lexer, TOKEN_DIV_ASSIGN, "/=") : make_token(lexer, TOKEN_DIV, "/"); case '*': - if (match(lexer, '%')) return match(lexer, '=') ? make_token(lexer, TOKEN_MINUS_MOD_ASSIGN, "*%=") : make_token(lexer, TOKEN_MULT_MOD, "*%"); + if (match(lexer, '%')) return match(lexer, '=') ? make_token(lexer, TOKEN_MULT_MOD_ASSIGN, "*%=") : make_token(lexer, TOKEN_MULT_MOD, "*%"); return match(lexer, '=') ? make_token(lexer, TOKEN_MULT_ASSIGN, "*=") : make_token(lexer, TOKEN_STAR, "*"); case '=': return match(lexer, '=') ? make_token(lexer, TOKEN_EQEQ, "==") : make_token(lexer, TOKEN_EQ, "="); @@ -607,5 +607,15 @@ Token lexer_scan_ident_test(Lexer *lexer, const char *scan) lexer->current_file->end_id = 1000; lexer->current_file->name = "Foo"; + if (scan[0] == '$') + { + next(lexer); + return scan_prefixed_ident(lexer, TOKEN_CT_IDENT, TOKEN_DOLLAR, false, "$"); + } + if (scan[0] == '#') + { + next(lexer); + return scan_prefixed_ident(lexer, TOKEN_HASH_IDENT, TOKEN_HASH, false, "#"); + } return scan_ident(lexer); } diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 4cacca9b4..fdbb4d7a6 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -551,8 +551,6 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM case BINARYOP_SUB_MOD_ASSIGN: case BINARYOP_DIV_ASSIGN: case BINARYOP_MOD_ASSIGN: - case BINARYOP_AND_ASSIGN: - case BINARYOP_OR_ASSIGN: case BINARYOP_BIT_AND_ASSIGN: case BINARYOP_BIT_OR_ASSIGN: case BINARYOP_BIT_XOR_ASSIGN: diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 38df44c45..92bcc8310 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -426,7 +426,6 @@ void gencontext_emit_switch(GenContext *context, Ast *ast) // TODO check defer correctness if (ast->switch_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->switch_stmt.decl); LLVMValueRef switch_value = gencontext_emit_decl_expr_list(context, ast->switch_stmt.cond, false); - size_t cases = vec_size(ast->switch_stmt.cases); if (!cases) { @@ -438,7 +437,7 @@ void gencontext_emit_switch(GenContext *context, Ast *ast) VECEACH(ast->switch_stmt.cases, i) { Ast *case_stmt = ast->switch_stmt.cases[i]; - if (case_stmt->case_stmt.value_type == CASE_VALUE_DEFAULT) + if (!case_stmt->case_stmt.expr) { if (case_stmt->case_stmt.body) { @@ -494,7 +493,7 @@ void gencontext_emit_switch(GenContext *context, Ast *ast) LLVMBasicBlockRef block = case_stmt->case_stmt.backend_value; if (case_stmt != default_case) { - LLVMValueRef case_value = LLVMConstInt(LLVMTypeOf(switch_value), case_stmt->case_stmt.val, case_stmt->case_stmt.value_type == CASE_VALUE_INT); + LLVMValueRef case_value = gencontext_emit_expr(context, case_stmt->case_stmt.expr); LLVMAddCase(switch_stmt, case_value, block); } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index df4973570..1c2d0e1a8 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -162,6 +162,7 @@ static Expr *parse_unary_expr(Context *context, Expr *left) CHECK_EXPR(right_side); unary->unary_expr.expr = right_side; + unary->span.end_loc = right_side->span.end_loc; return unary; } diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 4bf2ddb26..3a364a74f 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -197,7 +197,7 @@ static inline Ast *parse_default_stmt(Context *context) TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' after 'default'.", &poisoned_ast); extend_ast_with_prev_token(context, ast); ast->case_stmt.body = TRY_AST(parse_case_stmts(context)); - ast->case_stmt.value_type = CASE_VALUE_DEFAULT; + ast->case_stmt.expr = NULL; return ast; } @@ -796,15 +796,6 @@ Ast *parse_stmt(Context *context) case TOKEN_UNTIL: case TOKEN_ATTRIBUTE: case TOKEN_VAR: - case TOKEN_AT_PARAM: - case TOKEN_AT_THROWS: - case TOKEN_AT_RETURN: - case TOKEN_AT_ENSURE: - case TOKEN_AT_REQUIRE: - case TOKEN_AT_PURE: - case TOKEN_AT_CONST: - case TOKEN_AT_REQPARSE: - case TOKEN_AT_DEPRECATED: case TOKEN_DOCS_START: case TOKEN_DOCS_END: case TOKEN_DOCS_EOL: diff --git a/src/compiler/parser.c b/src/compiler/parser.c index e7f68c5da..8b94f2a77 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -514,6 +514,7 @@ Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type) SEMA_TOKEN_ERROR(context->tok, "Expected '{'."); return &poisoned_decl; } + EXPECT_IDENT_FOR_OR("variable_name", &poisoned_decl); Token name = context->tok; @@ -607,6 +608,13 @@ static inline Decl *parse_global_declaration(Context *context, Visibility visibi Decl *decl = decl_new_var(context->tok, type, VARDECL_GLOBAL, visibility); + if (context->tok.type == TOKEN_FUNC) + { + SEMA_TOKEN_ERROR(context->tok, "'func' can't appear here, maybe you intended to put 'func' the type?"); + advance(context); + return false; + } + if (!consume_ident(context, "global variable")) return &poisoned_decl; if (try_consume(context, TOKEN_EQ)) diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index da96088b1..78450603c 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -121,7 +121,7 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo if (param->var.init_expr) { Expr *expr = param->var.init_expr; - if (!sema_analyse_expr(context, param->type, expr)) return false; + if (!sema_analyse_expr_of_required_type(context, param->type, expr)) return false; if (expr->expr_kind != EXPR_CONST) { SEMA_ERROR(expr, "Only constant expressions may be used as default values."); @@ -291,7 +291,7 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl) } // We try to convert to the desired type. - if (!sema_analyse_expr(context, type, expr)) + if (!sema_analyse_expr_of_required_type(context, type, expr)) { success = false; enum_value->resolve_status = RESOLVE_DONE; @@ -404,7 +404,7 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) if (!sema_resolve_type_info(context, decl->var.type_info)) return false; if (decl->var.init_expr) { - if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return false; + if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr)) return false; if (decl->var.init_expr->expr_kind != EXPR_CONST) { SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value."); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 0b674e414..cf9afc8da 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -9,22 +9,26 @@ * TODOs * - Check all returns correctly * - Disallow jumping in and out of an expression block. - * - Handle IXX FXX and UXX in a sane way. */ -#define CONST_PROCESS(_op) \ -if (both_const(left, right)) { \ -switch (left->const_expr.type) { \ -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; \ +static inline bool is_const(Expr *expr) +{ + return expr->expr_kind == EXPR_CONST; +} + +static inline bool both_const(Expr *left, Expr *right) +{ + return left->expr_kind == EXPR_CONST && right->expr_kind == EXPR_CONST; +} + +static inline bool both_any_integer(Expr *left, Expr *right) +{ + return type_is_any_integer(left->type->canonical) && type_is_any_integer(right->type->canonical); } int sema_check_comp_time_bool(Context *context, Expr *expr) { - if (!sema_analyse_expr(context, type_bool, expr)) return -1; + if (!sema_analyse_expr_of_required_type(context, type_bool, expr)) return -1; if (expr->expr_kind != EXPR_CONST) { SEMA_ERROR(expr, "$if requires a compile time constant value."); @@ -57,7 +61,8 @@ static inline bool sema_type_error_on_binop(Expr *expr) { const char *c = token_type_to_string(binaryop_to_token(expr->binary_expr.operator)); SEMA_ERROR(expr, - "Cannot perform '%s' %s '%s'.", + "%s is not defined in the expression '%s' %s '%s'.", + c, type_to_error_string(expr->binary_expr.left->type), c, type_to_error_string(expr->binary_expr.right->type)); @@ -150,9 +155,9 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr return true; } -static inline bool sema_expr_analyse_binary_sub_expr(Context *context, Expr *left, Expr *right) +static inline bool sema_expr_analyse_binary_sub_expr(Context *context, Type *to, Expr *left, Expr *right) { - return sema_analyse_expr(context, NULL, left) & sema_analyse_expr(context, NULL, right); + return sema_analyse_expr(context, to, left) & sema_analyse_expr(context, to, right); } static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr *expr) { TODO } @@ -173,7 +178,7 @@ static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr for (unsigned i = 0; i < num_args; i++) { Expr *arg = args[i]; - if (!sema_analyse_expr(context, func_params[i]->type, arg)) return false; + if (!sema_analyse_expr_of_required_type(context, func_params[i]->type, arg)) return false; } expr->type = decl->func.function_signature.rtype->type; return true; @@ -183,6 +188,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr { // TODO Expr *func_expr = expr->call_expr.function; + // TODO check if (!sema_analyse_expr(context, to, func_expr)) return false; Decl *decl; switch (func_expr->expr_kind) @@ -250,8 +256,14 @@ static inline bool sema_expr_analyse_subscript(Context *context, Type *to, Expr return false; } + if (!sema_analyse_expr(context, type_isize, expr->subscript_expr.index)) return false; + // Unless we already have type_usize, cast to type_isize; + if (expr->subscript_expr.index->type->canonical->type_kind != type_usize->canonical->type_kind) + { + if (!cast_implicit(expr->subscript_expr.index, type_isize)) return false; + } expr->type = inner_type; return true; } @@ -515,7 +527,7 @@ static InitSemaResult sema_expr_analyse_struct_named_initializer_list(Context *c } return INIT_SEMA_NOT_FOUND; } - if (!sema_analyse_expr(context, result->type, value)) return INIT_SEMA_ERROR; + if (!sema_analyse_expr_of_required_type(context, result->type, value)) return INIT_SEMA_ERROR; } return INIT_SEMA_OK; } @@ -545,7 +557,7 @@ static inline bool sema_expr_analyse_struct_initializer_list(Context *context, T } VECEACH(expr->initializer_expr, i) { - if (!sema_analyse_expr(context, members[i]->type, expr->initializer_expr[i])) return false; + if (!sema_analyse_expr_of_required_type(context, members[i]->type, expr->initializer_expr[i])) return false; } expr->type = assigned; return true; @@ -584,7 +596,7 @@ static inline bool sema_expr_analyse_expr_list(Context *context, Type *to, Expr size_t last = vec_size(expr->expression_list) - 1; VECEACH(expr->expression_list, i) { - success &= sema_analyse_expr(context, i == last ? to : NULL, expr->expression_list[i]); + success &= sema_analyse_expr_of_required_type(context, i == last ? to : NULL, expr->expression_list[i]); } return success; } @@ -593,7 +605,7 @@ static inline bool sema_expr_analyse_cast(Context *context, Type *to, Expr *expr { Expr *inner = expr->cast_expr.expr; if (!sema_resolve_type_info(context, expr->cast_expr.type_info)) return false; - if (!sema_analyse_expr(context, NULL, inner)) return false; + if (!sema_analyse_expr_of_required_type(context, NULL, inner)) return false; if (!cast(inner, expr->cast_expr.type_info->type, CAST_TYPE_EXPLICIT)) return false; @@ -606,222 +618,211 @@ static inline bool sema_expr_analyse_cast(Context *context, Type *to, Expr *expr return true; } - -static bool sema_expr_analyse_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +/** + * Analyse a = b + * @return true if analysis works + */ +static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, Expr *right) { + // 1. Evaluate left side if (!sema_analyse_expr(context, NULL, left)) return false; + // 2. Check assignability if (!expr_is_ltype(left)) { SEMA_ERROR(left, "Expression is not assignable."); return false; } - if (!sema_analyse_expr(context, left->type, right)) return false; + + // 3. Evaluate right side to required type. + if (!sema_analyse_expr_of_required_type(context, left->type, right)) return false; + + // 4. Set the result to the type on the right side. expr->type = right->type; + return true; } -static inline bool both_const(Expr *left, Expr *right) -{ - return left->expr_kind == EXPR_CONST && right->expr_kind == EXPR_CONST; -} -static bool sema_expr_analyse_bit_and_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +/** + * Analyse *%= *= /= %= ^= |= &= + * + * @return true if analysis worked. + */ +static bool sema_expr_analyse_common_assign(Context *context, Expr *expr, Expr *left, Expr *right, bool int_only) { + // 1. Analyse left side. if (!sema_analyse_expr(context, NULL, left)) return false; - if (!type_is_number(left->type)) + // 2. Verify that the left side is assignable. + if (!expr_is_ltype(left)) + { + SEMA_ERROR(left, "Expression is not assignable."); + return false; + } + + // 3. If this is only defined for ints (*%, ^= |= &= %=) verify that this is an int. + if (int_only && !type_is_any_integer(left->type)) + { + SEMA_ERROR(left, "Expected an integer here."); + return false; + } + + // 4. In any case, these ops are only defined on numbers. + if (!type_is_numeric(left->type)) { SEMA_ERROR(left, "Expected a numeric type here."); return false; } - if (!sema_analyse_expr(context, left->type->canonical, right)) return false; + // 5. Cast the right hand side to the one on the left + if (!sema_analyse_expr_of_required_type(context, left->type->canonical, right)) return false; - if (!type_is_number(right->type)) + // 6. Check for zero in case of div or mod. + if (right->expr_kind == EXPR_CONST) { - SEMA_ERROR(right, "Expected a numeric type here."); - return false; + if (expr->binary_expr.operator == BINARYOP_DIV_ASSIGN) + { + switch (right->const_expr.kind) + { + 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 ALL_FLOATS: + if (right->const_expr.f == 0) + { + SEMA_ERROR(right, "Division by zero not allowed."); + return false; + } + break; + default: + UNREACHABLE + } + } + else if (expr->binary_expr.operator == BINARYOP_MOD_ASSIGN) + { + switch (right->const_expr.kind) + { + case ALL_INTS: + if (bigint_cmp_zero(&right->const_expr.i) == CMP_EQ) + { + SEMA_ERROR(right, "% by zero not allowed."); + return false; + } + break; + default: + UNREACHABLE + } + } } + // 7. Assign type expr->type = left->type; - return cast(right, left->type, CAST_TYPE_IMPLICIT_ASSIGN_ADD); -} - -static bool sema_expr_analyse_bit_or_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) -{ - if (!sema_analyse_expr(context, NULL, left)) return false; - - if (!type_is_number(left->type)) - { - SEMA_ERROR(left, "Expected a numeric type here."); - return false; - } - - if (!sema_analyse_expr(context, left->type->canonical, right)) return false; - - if (!type_is_number(right->type)) - { - SEMA_ERROR(right, "Expected a numeric type here."); - return false; - } - - expr->type = left->type; - return cast(right, left->type, CAST_TYPE_IMPLICIT_ASSIGN_ADD); -} - -static bool sema_expr_analyse_bit_xor_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) -{ - if (!sema_analyse_expr(context, NULL, left)) return false; - - if (!type_is_number(left->type)) - { - SEMA_ERROR(left, "Expected a numeric type here."); - return false; - } - - if (!sema_analyse_expr(context, left->type->canonical, right)) return false; - - if (!type_is_number(right->type)) - { - SEMA_ERROR(right, "Expected a numeric type here."); - return false; - } - - expr->type = left->type; - return cast(right, left->type, CAST_TYPE_IMPLICIT_ASSIGN_ADD); - -} - -static bool sema_expr_analyse_div_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) -{ - if (!sema_analyse_expr(context, NULL, left)) return false; - - if (!type_is_number(left->type)) - { - SEMA_ERROR(left, "Expected a numeric type here."); - return false; - } - - if (!sema_analyse_expr(context, left->type->canonical, right)) return false; - - if (!type_is_number(right->type)) - { - SEMA_ERROR(right, "Expected a numeric type here."); - return false; - } - - expr->type = left->type; - return cast(right, left->type, CAST_TYPE_IMPLICIT_ASSIGN_ADD); -} - -static bool sema_expr_analyse_mult_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) -{ - if (!sema_analyse_expr(context, NULL, left)) return false; - - if (!type_is_number(left->type)) - { - SEMA_ERROR(left, "Expected a numeric type here."); - return false; - } - - if (!sema_analyse_expr(context, left->type->canonical, right)) return false; - - if (!type_is_number(right->type)) - { - SEMA_ERROR(right, "Expected a numeric type here."); - return false; - } - - expr->type = left->type; - return cast(right, left->type, CAST_TYPE_IMPLICIT_ASSIGN_ADD); + return true; } -static bool sema_expr_analyse_sub_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +static BinaryOp binary_mod_op_to_non_mod(BinaryOp op) { + switch (op) + { + case BINARYOP_MULT_MOD: + return BINARYOP_MULT; + case BINARYOP_MULT_MOD_ASSIGN: + return BINARYOP_MULT_ASSIGN; + case BINARYOP_SUB_MOD: + return BINARYOP_SUB; + case BINARYOP_SUB_MOD_ASSIGN: + return BINARYOP_SUB_ASSIGN; + case BINARYOP_ADD_MOD: + return BINARYOP_ADD; + case BINARYOP_ADD_MOD_ASSIGN: + return BINARYOP_ADD_ASSIGN; + default: + return op; + } +} +/** + * Handle a += b, a +%= b, a -= b, a -%= b + * @return true if analysis succeeded. + */ +static bool sema_expr_analyse_add_sub_assign(Context *context, Expr *expr, Expr *left, Expr *right) +{ + bool is_mod = expr->binary_expr.operator == BINARYOP_ADD_MOD_ASSIGN + || expr->binary_expr.operator == BINARYOP_SUB_MOD_ASSIGN; + + // 1. Analyse the left hand side if (!sema_analyse_expr(context, NULL, left)) return false; + // 2. Ensure the left hand side is assignable + if (!expr_is_ltype(left)) + { + SEMA_ERROR(left, "Expression is not assignable."); + return false; + } + Type *left_type_canonical = left->type->canonical; + expr->type = left->type; + // 3. Attempt to analyse and cast this side to the same type if possible. + if (!sema_analyse_expr(context, left->type, right)) return false; + + // 4. In the pointer case we have to treat this differently. if (left_type_canonical->type_kind == TYPE_POINTER) { - if (!sema_analyse_expr(context, NULL, right)) return false; - // Improve check if this should be usize. - if (!cast_to_runtime(right)) return false; - Type *right_type = right->type->canonical; - if (!type_is_integer(right_type)) + // 5. Prevent +%= and -%= + if (is_mod) { - SEMA_ERROR(right, "Expected an integer type instead."); + SEMA_ERROR(expr, "Cannot use %s with pointer arithmetics, use %s instead.", + token_type_to_string(binaryop_to_token(expr->binary_expr.operator)), + token_type_to_string(binaryop_to_token(binary_mod_op_to_non_mod(expr->binary_expr.operator)))); + return false; + } + + // 5. Convert any compile time values to runtime + cast_to_smallest_runtime(right); + + // 6. Finally, check that the right side is indeed an integer. + if (!type_is_integer(right->type->canonical)) + { + SEMA_ERROR(right, "The right side was '%s' but only integers are valid on the right side of %s when the left side is a pointer.", + type_to_error_string(right->type), + token_type_to_string(binaryop_to_token(expr->binary_expr.operator))); return false; } - expr->type = left->type; return true; } - if (!sema_analyse_expr(context, left->type->canonical, right)) return false; + // 5. Otherwise we cast rhs to lhs + if (!cast_implicit(right, left->type)) return false; - if (!type_is_number(left->type)) + // 6. We expect a numeric type on both left and right + if (!type_is_numeric(left->type)) { SEMA_ERROR(left, "Expected a numeric type here."); return false; } - if (!type_is_number(right->type)) + // 7. Prevent +%= and -%= on non integers + if (is_mod && !type_is_integer(left->type->canonical)) { - SEMA_ERROR(right, "Expected a numeric type here."); + SEMA_ERROR(expr, "%s can only be used for integer arithmetics, for other cases use %s instead.", + token_type_to_string(binaryop_to_token(expr->binary_expr.operator)), + token_type_to_string(binaryop_to_token(binary_mod_op_to_non_mod(expr->binary_expr.operator)))); return false; } - expr->type = left->type; - - return cast(right, left->type, CAST_TYPE_IMPLICIT_ASSIGN_ADD); -} - -static bool sema_expr_analyse_add_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) -{ - if (!sema_analyse_expr(context, NULL, left)) return false; - - Type *left_type_canonical = left->type->canonical; - - if (left_type_canonical->type_kind == TYPE_POINTER) - { - if (!sema_analyse_expr(context, NULL, right)) return false; - // Improve check if this should be usize. - if (!cast_to_runtime(right)) return false; - Type *right_type = right->type->canonical; - if (!type_is_integer(right_type)) - { - SEMA_ERROR(right, "Expected an integer type instead."); - return false; - } - expr->type = left->type; - return true; - } - - if (!sema_analyse_expr(context, left->type->canonical, right)) return false; - - if (!type_is_number(left->type)) - { - SEMA_ERROR(left, "Expected a numeric type here."); - return false; - } - - if (!type_is_number(right->type)) - { - SEMA_ERROR(right, "Expected a numeric type here."); - return false; - } - - expr->type = left->type; - - return cast(right, left->type, CAST_TYPE_IMPLICIT_ASSIGN_ADD); + return true; } static bool binary_arithmetic_promotion(Expr *left, Expr *right, Type *left_type, Type *right_type) { Type *max = type_find_max_type(left_type, right_type); - return max && type_is_number(max) && cast_implicit(left, max) && cast_implicit(right, max); + return max && type_is_numeric(max) && cast_implicit(left, max) && cast_implicit(right, max); } /** @@ -830,29 +831,65 @@ static bool binary_arithmetic_promotion(Expr *left, Expr *right, Type *left_type */ 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; + bool is_mod = expr->binary_expr.operator == BINARYOP_SUB_MOD; + + // 1. Analyse a and b. Do not push down if this is a -% + if (!sema_expr_analyse_binary_sub_expr(context, is_mod ? NULL : to, left, right)) return false; Type *left_type = left->type->canonical; Type *right_type = right->type->canonical; + // 2. Handle the ptr - x and ptr - other_pointer if (left_type->type_kind == TYPE_POINTER) { + // 3. Is this -%? That's not ok for pointer maths. + if (is_mod) + { + SEMA_ERROR(expr, "'-%%' is not valid for pointer maths, use '-' instead."); + return false; + } + + // 4. ptr - other pointer if (right_type->type_kind == TYPE_POINTER) { - Type *max = type_find_max_type(left_type, right_type); - bool success = max && cast_implicit(left, max) && cast_implicit(right, max); - if (!success) goto ERR; + // 5. Require that both types are the same. + if (left_type != right_type) + { + SEMA_ERROR(expr, "'%s' - '%s' is not allowed. Subtracting pointers of different types from each other is not possible.", type_to_error_string(left_type), type_to_error_string(right_type)); + return false; + } + // 5. usize only if that is the recipient + if (to && to->canonical->type_kind == type_usize->canonical->type_kind) + { + expr->type = to; + return true; + } expr->type = type_isize; return true; } - // No need to cast this, we just ensure it is an integer. - if (!type_is_integer(right_type) || !cast_to_runtime(right)) goto ERR; + + // 5. Cast any compile time int into smallest runtime version if we have a compile time constant. + cast_to_smallest_runtime(right); + + // 6. No need for further casts, just it is an integer. + if (!type_is_integer(right_type)) + { + SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); + return false; + } + expr->type = left->type; return true; } - if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR; + // 7. Attempt arithmetic promotion, to promote both to a common type. + if (!binary_arithmetic_promotion(left, right, left_type, right_type)) + { + SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); + return false; + } + // 8. Handle constant folding. if (both_const(left, right)) { switch (left->const_expr.kind) @@ -871,22 +908,35 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * expr->const_expr.kind = left->const_expr.kind; } + // 9. Is this -%? That's not ok unless we are adding integers. + if (is_mod && !type_is_any_integer(left->type->canonical)) + { + SEMA_ERROR(expr, "'-%%' is only valid for integer subtraction, use '-' instead."); + return false; + } + expr->type = left->type; return true; - ERR: - SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); - return false; } +/** + * Analyse a + b / a +% b + * @return true if it succeeds. + */ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; + bool is_mod = expr->binary_expr.operator == BINARYOP_ADD_MOD; + + // 1. Promote everything to the recipient type – if possible + // this is safe in the pointer case actually. + if (!sema_expr_analyse_binary_sub_expr(context, is_mod ? NULL : to, left, right)) return false; Type *left_type = left->type->canonical; Type *right_type = right->type->canonical; - // Reorder if needed + + // 2. To detect pointer additions, reorder if needed if (right_type->type_kind == TYPE_POINTER && left_type->type_kind != TYPE_POINTER) { Expr *temp = right; @@ -896,16 +946,46 @@ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr * left_type = left->type->canonical; } + // 4. The "left" will now always be the pointer. + // so check if we want to do the normal pointer add special handling. if (left_type->type_kind == TYPE_POINTER) { - // No need to cast this, we just ensure it is an integer. - if (!type_is_integer(right_type) || !cast_to_runtime(right)) goto ERR; + // 4a. Check that the other side is an integer of some sort. + if (!type_is_any_integer(right_type)) + { + SEMA_ERROR(right, "A value of type '%s' cannot be added to '%s', an integer was expected here.", + type_to_error_string(right->type), + type_to_error_string(left->type)); + return false; + } + + // 4b. Cast it to usize or isize depending on underlying type. + // Either is fine, but it looks a bit nicer if we actually do this and keep the sign. + bool success = cast_implicit(right, type_is_unsigned(right_type) ? type_usize : type_isize); + // No need to check the cast we just ensured it was an integer. + assert(success && "This should always work"); + + // 4c. Set the type. expr->type = left->type; + + // 4d. Is this +%? That's not ok for pointers! + if (is_mod) + { + SEMA_ERROR(expr, "You cannot use '+%%' with pointer addition, use '+' instead."); + return false; + } return true; } - if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR; + // 5. Do the binary arithmetic promotion (finding a common super type) + // If none can be find, send an error. + if (!binary_arithmetic_promotion(left, right, left_type, right_type)) + { + SEMA_ERROR(expr, "Cannot add '%s' to '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); + return false; + } + // 6. Handle the "both const" case. We should only see ints and floats at this point. if (both_const(left, right)) { switch (left->const_expr.kind) @@ -923,90 +1003,128 @@ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr * expr->const_expr.kind = left->const_expr.kind; } + // 7. Is this +%? That's not ok unless we are adding integers. + if (is_mod && !type_is_any_integer(left->type->canonical)) + { + SEMA_ERROR(expr, "'+%%' is only valid for integer addition, use '+' instead."); + return false; + } + + // 7. Set the type expr->type = left->type; return true; - ERR: - SEMA_ERROR(expr, "Cannot add '%s' to '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); - return false; } +/** + * Analyse a * b and a *% b + * + * Will analyse a and b and convert them to the "to" type if possible. + * It will then try to promote both to a common type, + * check that *% is only used on an integer and then perform constant folding. + * + * @return true if analysis worked. + */ static bool sema_expr_analyse_mult(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; + bool is_mod = expr->binary_expr.operator == BINARYOP_MULT_MOD; + + // 1. Analyse the sub expressions. + if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false; Type *left_type = left->type->canonical; Type *right_type = right->type->canonical; - if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR; + // 2. Perform promotion to a common type. + if (!binary_arithmetic_promotion(left, right, left_type, right_type)) + { + SEMA_ERROR(expr, "Cannot multiply '%s' with '%s'", type_to_error_string(left->type), type_to_error_string(right->type)); + return false; + } - bool is_mod = expr->binary_expr.operator == BINARYOP_MULT_MOD; + // 3. Set the type. expr->type = left->type; // Might have changed left_type = left->type->canonical; + // 4. Prevent *% use on non-integers. if (is_mod && !type_is_any_integer(left_type)) { - SEMA_ERROR(expr, "You can only use *% with integer types."); + SEMA_ERROR(expr, "*% can only be used with integer types, try * instead."); 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) + // 5. Handle constant folding. + if (both_const(left, right)) { - 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 + expr->expr_kind = EXPR_CONST; + expr->const_expr.kind = left->const_expr.kind; + + switch (left->const_expr.kind) + { + case ALL_INTS: + // 5a. Do mod mult if applicable. + 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); + break; + case ALL_FLOATS: + expr->const_expr.f = left->const_expr.f * right->const_expr.f; + break; + default: + UNREACHABLE + } } - ERR: - SEMA_ERROR(expr, "Cannot multiply '%s' and '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); - return false; + // 6. All done. + return true; } +/** + * Analyse a / b + * @return true if analysis completed ok. + */ static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; + // 1. Analyse sub expressions. + if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false; Type *left_type = left->type->canonical; Type *right_type = right->type->canonical; - if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR; + // 2. Perform promotion to a common type. + if (!binary_arithmetic_promotion(left, right, left_type, right_type)) + { + SEMA_ERROR(expr, "Cannot divide '%s' by '%s'.", type_to_error_string(left_type), type_to_error_string(right_type)); + return false; + } expr->type = left->type; - // Check null - if (right->expr_kind == EXPR_CONST) + // 3. Check for a constant 0 on the right hand side. + if (is_const(right)) { switch (right->const_expr.kind) { case ALL_INTS: if (bigint_cmp_zero(&right->const_expr.i) == CMP_EQ) { - SEMA_ERROR(right, "Division by zero not allowed."); + SEMA_ERROR(right, "This expression evaluates to zero and division by zero is not allowed."); return false; } break; case ALL_FLOATS: if (right->const_expr.f == 0) { - SEMA_ERROR(right, "Division by zero not allowed."); + SEMA_ERROR(right, "This expression evaluates to zero and division by zero is not allowed."); return false; } break; @@ -1015,252 +1133,240 @@ static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr * } } - - if (!both_const(left, right)) return true; - - switch (left->const_expr.kind) + // 4. Perform constant folding. + if (both_const(left, right)) { - 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 + switch (left->const_expr.kind) + { + case ALL_INTS: + bigint_div_floor(&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 + } } - - ERR: - SEMA_ERROR(expr, "Cannot divide '%s' by '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); - return false; + // 5. Done. + return true; } +/** + * Analyse a % b + * @return true if analysis succeeds. + */ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; + // 1. Analyse both sides. + if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false; - 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 && bigint_cmp_zero(&right->const_expr.i) == CMP_EQ) + // 2. Make sure we have some sort of integer on both sides. + if (!type_is_any_integer(right->type->canonical) || !type_is_any_integer(left->type->canonical)) { - SEMA_ERROR(expr->binary_expr.right, "Cannot perform mod by zero."); + return sema_type_error_on_binop(expr); + } + + // 3. a % 0 is not valid, so detect it. + if (is_const(right) && bigint_cmp_zero(&right->const_expr.i) == CMP_EQ) + { + SEMA_ERROR(expr->binary_expr.right, "Cannot perform % with a constant zero."); return false; } - // TODO Insert trap on negative right. - if (left->expr_kind == EXPR_CONST && right->expr_kind == EXPR_CONST) + + // 4. Constant fold + if (both_const(left, right)) { - // TODO negative expr_replace(expr, left); - bigint_mod(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); - return expr; + // 4a. Remember this is remainder. + bigint_rem(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); } - if (!cast_implicit(left, to)) return false; - - if (!cast_to_runtime(left) || !cast_to_runtime(right)) return false; - expr->type = left->type; return true; } -static bool sema_expr_analyse_mod_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) -{ - - TODO -} - - +/** + * Analyse a ^ b, a | b, a & b + * @return true if the analysis succeeded. + */ static bool sema_expr_analyse_bit(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; + // 1. Convert to top down type if possible. + if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false; - if (!type_is_integer(right->type->canonical) || !type_is_integer(left->type->canonical)) goto ERR; + // 2. Check that both are integers. + if (!both_any_integer(left, right)) + { + return sema_type_error_on_binop(expr); + } + + // 3. Promote to the same type. Type *left_type = left->type->canonical; Type *right_type = right->type->canonical; - if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR; + if (!binary_arithmetic_promotion(left, right, left_type, right_type)) + { + return sema_type_error_on_binop(expr); + } - if (left->expr_kind == EXPR_CONST && right->expr_kind == EXPR_CONST) + // 4. Do constant folding if both sides are constant. + if (both_const(left, right)) { expr_replace(expr, left); switch (expr->binary_expr.operator) { - case TOKEN_AMP: + case BINARYOP_BIT_AND: bigint_and(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); break; - case TOKEN_BIT_XOR: + case BINARYOP_BIT_XOR: bigint_xor(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); break; - case TOKEN_BIT_OR: + case BINARYOP_BIT_OR: bigint_or(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); break; default: UNREACHABLE; } - return expr; - } - - expr->type = left->type; - return true; - - ERR: - return sema_type_error_on_binop(expr); - -} - - -static bool sema_expr_analyse_shr(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) -{ - - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; - - if (!type_is_integer(right->type->canonical) || !type_is_integer(left->type->canonical)) goto ERR; - - // First, try to do assign type promotion. - if (!cast_implicit(left, to)) goto ERR; - - // 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)) goto ERR; - - if (right->expr_kind == EXPR_CONST) - { - 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; - } - if (left->expr_kind == EXPR_CONST) - { - expr_replace(expr, left); - bigint_shr(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); - return true; - } - } - - expr->type = left->type; - return true; - - ERR: - return sema_type_error_on_binop(expr); -} - -static bool sema_expr_analyse_shr_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) -{ - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; - - if (!expr_is_ltype(left)) - { - SEMA_ERROR(left, "Expression is not assignable."); - return false; - } - - // Check that right side is integer and cast to a runtime type if needed. - if (!type_is_integer(right->type->canonical) || !cast_to_runtime(right)) return false; - - if (right->expr_kind == EXPR_CONST) - { - 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; - } } + // 5. Assign the type expr->type = left->type; return true; } -static bool sema_expr_analyse_shl(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +/** + * Analyse >> and << operations. + * @return true if the analysis succeeded. + */ +static bool sema_expr_analyse_shift(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { + // 1. Analyze the two sub lhs & rhs *without coercion* + if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false; - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; - - if (!type_is_integer(right->type->canonical) || !type_is_integer(left->type->canonical)) goto ERR; - - // First, try to do assign type promotion. - if (!cast_implicit(left, to)) goto ERR; - - // 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) + // 2. Only integers may be shifted. + if (!both_any_integer(left, right)) { - TODO - //if (int_int_exceeds(&right->const_expr.i, left->type->canonical->builtin.bitsize)) + return sema_type_error_on_binop(expr); + } + + // 3. For a constant right hand side we will make a series of checks. + if (is_const(right)) + { + // 3a. Make sure the value does not exceed the bitsize of + // the left hand side. We ignore this check for lhs being a constant. + if (left->type->canonical->type_kind != TYPE_IXX) { - SEMA_ERROR(right, "Leftshift exceeds bitsize of '%s'", type_to_error_string(left->type)); + BigInt bitsize; + bigint_init_unsigned(&bitsize, left->type->canonical->builtin.bitsize); + if (bigint_cmp(&right->const_expr.i, &bitsize) == CMP_GT) + { + SEMA_ERROR(right, "The shift exceeds bitsize of '%s'.", type_to_error_string(left->type)); + return false; + } + } + // 3b. Make sure that the right hand side is positive. + if (bigint_cmp_zero(&right->const_expr.i) == CMP_LT) + { + SEMA_ERROR(right, "A shift must be a positive number."); return false; } - if (left->expr_kind == EXPR_CONST) + + // 2c. Cast the value to the smallest runtime type. + cast_to_smallest_runtime(right); + + // 4. Fold constant expressions. + if (is_const(left)) { + // 4a. For >> this is always an arithmetic shift. + if (expr->binary_expr.operator == BINARYOP_SHR) + { + expr_replace(expr, left); + bigint_shr(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + return true; + } + // 4b. The << case needs to behave differently for bigints and fixed bit integers. expr_replace(expr, left); if (left->const_expr.kind == TYPE_IXX) { - bigint_shl(&expr->const_expr.i, &expr->const_expr.i, &right->const_expr.i); + bigint_shl(&expr->const_expr.i, &left->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); + bigint_shl_trunc(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i, bit_count, is_signed); } return true; } } - return true; + // 5. We might have the case 2 << x. In that case we will to cast the left hand side to the receiving type. + if (!cast_implicit(left, to)) return false; - ERR: - return sema_type_error_on_binop(expr); + // 6. As a last out, we make sure that a comptime int has a real type. We pick i64 for this. + if (!cast_to_runtime(left)) return false; + + expr->type = left->type; + return true; } -static bool sema_expr_analyse_shl_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +/** + * Analyse a <<= b a >>= b + * @return true is the analysis succeeds, false otherwise. + */ +static bool sema_expr_analyse_shift_assign(Context *context, Expr *expr, Expr *left, Expr *right) { - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; + // 1. Analyze the two sub lhs & rhs *without coercion* + if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false; + // 2. Ensure the left hand side is assignable if (!expr_is_ltype(left)) { SEMA_ERROR(left, "Expression is not assignable."); return false; } - expr->type = left->type; + // 2. Only integers may be shifted. + if (!both_any_integer(left, right)) return sema_type_error_on_binop(expr); - // 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); - - if (!cast_to_runtime(right)) return false; - - if (right->expr_kind == EXPR_CONST) + // 3. For a constant right hand side we will make a series of checks. + if (is_const(right)) { - if (right->const_expr.i.is_negative) + // 3a. Make sure the value does not exceed the bitsize of + // the left hand side. + BigInt bitsize; + bigint_init_unsigned(&bitsize, left->type->canonical->builtin.bitsize); + if (bigint_cmp(&right->const_expr.i, &bitsize) == CMP_GT) { - SEMA_ERROR(right, "Leftshift constant was negative (%s). It must be zero or more.", expr_const_to_error_string(&right->const_expr)); + SEMA_ERROR(right, "The shift exceeds bitsize of '%s'.", type_to_error_string(left->type)); return false; } - BigInt max; - bigint_init_unsigned(&max, left->type->canonical->builtin.bitsize); - if (bigint_cmp(&right->const_expr.i, &max) == CMP_GT) + + // 3b. Make sure that the right hand side is positive. + if (bigint_cmp_zero(&right->const_expr.i) == CMP_LT) { - // 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); + SEMA_ERROR(right, "A shift must be a positive number."); + return false; } + + // 3c. Cast the rhs to the smallest runtime type. + cast_to_smallest_runtime(right); } + + // 4. Set the type + expr->type = left->type; return true; } -static bool sema_expr_analyse_and(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +static bool sema_expr_analyse_and(Context *context, Expr *expr, Expr *left, Expr *right) { - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; + if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false; if (!cast_implicit(left, type_bool) || !cast_implicit(right, type_bool)) return false; expr->type = type_bool; @@ -1272,9 +1378,9 @@ static bool sema_expr_analyse_and(Context *context, Type *to, Expr *expr, Expr * return true; } -static bool sema_expr_analyse_or(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +static bool sema_expr_analyse_or(Context *context, Expr *expr, Expr *left, Expr *right) { - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; + if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false; if (!cast_implicit(left, type_bool) || !cast_implicit(right, type_bool)) return false; if (both_const(left, right)) @@ -1286,8 +1392,6 @@ static bool sema_expr_analyse_or(Context *context, Type *to, Expr *expr, Expr *l return true; } -static bool sema_expr_analyse_and_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { TODO } -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) @@ -1312,30 +1416,84 @@ static void cast_to_max_bit_size(Expr *left, Expr *right, Type *left_type, Type assert(success); } -static bool sema_expr_analyse_comp(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +/** + * Analyze a == b, a != b, a > b, a < b, a >= b, a <= b + * @return + */ +static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Expr *right) { - if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false; + // 1. Analyse left and right side without any conversions. + if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false; + + bool is_equality_type_op = expr->binary_expr.operator == BINARYOP_NE || expr->binary_expr.operator == BINARYOP_EQ; + Type *left_type = left->type->canonical; Type *right_type = right->type->canonical; - // Handle the case of signed comparisons. + // 2. Handle the case of signed comparisons. + // This happens when either side has a definite integer type + // and those are either signed or unsigned. + // If either side is compint, then this does not happen. if ((type_is_unsigned(left_type) && type_is_signed(right_type)) || (type_is_signed(left_type) && type_is_unsigned(right_type))) { + // 2a. Resize so that both sides have the same bit width. This will always work. cast_to_max_bit_size(left, right, left_type, right_type); } else { - // Alternatively, find and promote to max + // 3. In the normal case, treat this as a binary op, finding the max type. Type *max = type_find_max_type(left_type, right_type); - bool success = max && cast_implicit(left, max) && cast_implicit(right, max); - if (!success) + + // 4. If no common type, then that's an error: + if (!max) goto ERR; + + // 5. Most types can do equality, but not all can do comparison, + // so we need to check that as well. + if (is_equality_type_op) { - 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; + switch (max->type_kind) + { + case TYPE_POISONED: + return false; + case TYPE_VOID: + case TYPE_TYPEDEF: + UNREACHABLE + case TYPE_BOOL: + case TYPE_ENUM: + case TYPE_ERROR: + case TYPE_FUNC: + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_ERROR_UNION: + case TYPE_STRING: + case TYPE_ARRAY: + case TYPE_VARARRAY: + case TYPE_SUBARRAY: + case TYPE_META_TYPE: + // Only != and == allowed. + goto ERR; + case ALL_INTS: + case ALL_FLOATS: + // All comparisons allowed + break; + case TYPE_POINTER: + // Only comparisons between the same type is allowed. Subtypes not allowed. + if (left_type != right_type) + { + SEMA_ERROR(expr, "Cannot compare pointers of different types."); + return false; + } + break; + } } + + // 6. Do the implicit cast. + if (!cast_implicit(left, max)) goto ERR; + if (!cast_implicit(right, max)) goto ERR; } + // 7. Do constant folding. if (both_const(left, right)) { switch (left->const_expr.kind) @@ -1356,38 +1514,58 @@ static bool sema_expr_analyse_comp(Context *context, Type *to, Expr *expr, Expr expr->const_expr.kind = TYPE_BOOL; expr->expr_kind = EXPR_CONST; } + + // 8. Set the type to bool expr->type = type_bool; return true; + + ERR: + SEMA_ERROR(expr, "Cannot 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; } - -static bool sema_expr_analyse_elvis(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { TODO } - -static bool sema_expr_analyse_deref(Context *context, Type *to, Expr *expr, Expr *inner) +/** + * Analyse *a + * @return true if analysis succeeds. + */ +static bool sema_expr_analyse_deref(Context *context, Expr *expr, Expr *inner) { Type *canonical = inner->type->canonical; + // 1. Check that we have a pointer, or dereference is not allowed. if (canonical->type_kind != TYPE_POINTER) { - SEMA_ERROR(inner, "Cannot take the dereference of a value of type '%s'", type_to_error_string(inner->type)); + SEMA_ERROR(inner, "Cannot dereference a value of type '%s'", type_to_error_string(inner->type)); return false; } + // 2. This could be a constant, in which case it is a nil which is an error. if (inner->expr_kind == EXPR_CONST) { SEMA_ERROR(inner, "Dereferencing nil is not allowed."); return false; } + // 3. Now the type might not be a pointer because of a typedef, + // otherwise we need to use the the canonical representation. Type *deref_type = inner->type->type_kind != TYPE_POINTER ? inner->type : canonical; + + // 4. And... set the type. expr->type = deref_type->pointer; return true; } -static bool sema_expr_analyse_addr(Context *context, Type *to, Expr *expr, Expr *inner) +/** + * Analyse &a + * @return true if analysis succeeds. + */ +static bool sema_expr_analyse_addr(Context *context, Expr *expr, Expr *inner) { + // 1. Check that it is an lvalue. if (!expr_is_ltype(inner)) { - SEMA_ERROR(inner, "Cannot take the address of a value of type '%s'", type_to_error_string(inner->type)); + SEMA_ERROR(inner, "It is not possible to take the address of values, only of variables and memory locations.", type_to_error_string(inner->type)); return false; } + + // 2. Get the pointer of the underlying type. expr->type = type_get_ptr(inner->type); return true; } @@ -1553,7 +1731,7 @@ static inline bool sema_expr_analyse_incdec(Context *context, Type *to, Expr *ex SEMA_ERROR(inner, "Expression cannot be assigned to."); return false; } - if (!type_is_number(inner->type->canonical) && inner->type->canonical->type_kind != TYPE_POINTER) + if (!type_is_numeric(inner->type->canonical) && inner->type->canonical->type_kind != TYPE_POINTER) { SEMA_ERROR(inner, "Expression must be a number or a pointer."); return false; @@ -1572,74 +1750,59 @@ static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *ex switch (expr->binary_expr.operator) { case BINARYOP_ASSIGN: - return sema_expr_analyse_assign(context, to, expr, left, right); + return sema_expr_analyse_assign(context, expr, left, right); case BINARYOP_MULT: case BINARYOP_MULT_MOD: return sema_expr_analyse_mult(context, to, expr, left, right); - case BINARYOP_MULT_ASSIGN: - case BINARYOP_MULT_MOD_ASSIGN: - return sema_expr_analyse_mult_assign(context, to, expr, left, right); case BINARYOP_ADD: case BINARYOP_ADD_MOD: - // TODO tread mod differently return sema_expr_analyse_add(context, to, expr, left, right); - case BINARYOP_ADD_MOD_ASSIGN: case BINARYOP_ADD_ASSIGN: - return sema_expr_analyse_add_assign(context, to, expr, left, right); + case BINARYOP_ADD_MOD_ASSIGN: + case BINARYOP_SUB_ASSIGN: + case BINARYOP_SUB_MOD_ASSIGN: + return sema_expr_analyse_add_sub_assign(context, expr, left, right); case BINARYOP_SUB: case BINARYOP_SUB_MOD: return sema_expr_analyse_sub(context, to, expr, left, right); - case BINARYOP_SUB_ASSIGN: - case BINARYOP_SUB_MOD_ASSIGN: - return sema_expr_analyse_sub_assign(context, to, expr, left, right); case BINARYOP_DIV: return sema_expr_analyse_div(context, to, expr, left, right); + case BINARYOP_MULT_ASSIGN: case BINARYOP_DIV_ASSIGN: - return sema_expr_analyse_div_assign(context, to, expr, left, right); + return sema_expr_analyse_common_assign(context, expr, left, right, false); + case BINARYOP_MULT_MOD_ASSIGN: + case BINARYOP_BIT_AND_ASSIGN: + case BINARYOP_BIT_OR_ASSIGN: + case BINARYOP_BIT_XOR_ASSIGN: + case BINARYOP_MOD_ASSIGN: + return sema_expr_analyse_common_assign(context, expr, left, right, true); case BINARYOP_MOD: return sema_expr_analyse_mod(context, to, expr, left, right); - case BINARYOP_MOD_ASSIGN: - return sema_expr_analyse_mod_assign(context, to, expr, left, right); case BINARYOP_AND: - return sema_expr_analyse_and(context, to, expr, left, right); - case BINARYOP_AND_ASSIGN: - return sema_expr_analyse_and_assign(context, to, expr, left, right); + return sema_expr_analyse_and(context, expr, left, right); case BINARYOP_OR: - return sema_expr_analyse_or(context, to, expr, left, right); - case BINARYOP_OR_ASSIGN: - return sema_expr_analyse_or_assign(context, to, expr, left, right); - case BINARYOP_BIT_AND_ASSIGN: - return sema_expr_analyse_bit_and_assign(context, to, expr, left, right); + return sema_expr_analyse_or(context, expr, left, right); case BINARYOP_BIT_OR: case BINARYOP_BIT_XOR: case BINARYOP_BIT_AND: return sema_expr_analyse_bit(context, to, expr, left, right); - case BINARYOP_BIT_OR_ASSIGN: - return sema_expr_analyse_bit_or_assign(context, to, expr, left, right); - case BINARYOP_BIT_XOR_ASSIGN: - return sema_expr_analyse_bit_xor_assign(context, to, expr, left, right); case BINARYOP_NE: - // TODO special handling - return sema_expr_analyse_comp(context, to, expr, left, right); case BINARYOP_EQ: - // TODO special handling - return sema_expr_analyse_comp(context, to, expr, left, right); case BINARYOP_GT: case BINARYOP_GE: case BINARYOP_LT: case BINARYOP_LE: - return sema_expr_analyse_comp(context, to, expr, left, right); + return sema_expr_analyse_comp(context, expr, left, right); case BINARYOP_SHR: - return sema_expr_analyse_shr(context, to, expr, left, right); - case BINARYOP_SHR_ASSIGN: - return sema_expr_analyse_shr_assign(context, to, expr, left, right); case BINARYOP_SHL: - return sema_expr_analyse_shl(context, to, expr, left, right); + return sema_expr_analyse_shift(context, to, expr, left, right); + case BINARYOP_SHR_ASSIGN: case BINARYOP_SHL_ASSIGN: - return sema_expr_analyse_shl_assign(context, to, expr, left, right); - default: - UNREACHABLE + return sema_expr_analyse_shift_assign(context, expr, left, right); + case BINARYOP_ERROR: + break; } + UNREACHABLE } static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *expr) @@ -1652,9 +1815,9 @@ static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *exp switch (expr->unary_expr.operator) { case UNARYOP_DEREF: - return sema_expr_analyse_deref(context, to, expr, inner); + return sema_expr_analyse_deref(context, expr, inner); case UNARYOP_ADDR: - return sema_expr_analyse_addr(context, to, expr, inner); + return sema_expr_analyse_addr(context, expr, inner); case UNARYOP_NEG: case UNARYOP_NEGMOD: return sema_expr_analyse_neg(context, to, expr, inner); @@ -2270,6 +2433,12 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * } +bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr) +{ + if (!sema_analyse_expr(context, to, expr)) return false; + return cast_implicit(expr, to); +} + bool sema_analyse_expr(Context *context, Type *to, Expr *expr) { switch (expr->resolve_status) @@ -2285,5 +2454,5 @@ bool sema_analyse_expr(Context *context, Type *to, Expr *expr) } if (!sema_analyse_expr_dispatch(context, to, expr)) return expr_poison(expr); expr->resolve_status = RESOLVE_DONE; - return cast_implicit(expr, to); + return to ? cast(expr, to, CAST_TYPE_OPTIONAL_IMPLICIT) : true; } \ No newline at end of file diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 0456c08d7..0220ef830 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -109,7 +109,13 @@ static inline bool sema_analyse_block_return_stmt(Context *context, Ast *stateme { assert(context->current_scope->flags & SCOPE_EXPR_BLOCK); UPDATE_EXIT(EXIT_RETURN); - if (statement->return_stmt.expr && !sema_analyse_expr(context, context->expected_block_type, statement->return_stmt.expr)) return false; + if (statement->return_stmt.expr + && !sema_analyse_expr_of_required_type(context, + context->expected_block_type, + statement->return_stmt.expr)) + { + return false; + } vec_add(context->returns, statement); return true; } @@ -147,7 +153,7 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) 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 (!sema_analyse_expr_of_required_type(context, expected_rtype, return_expr)) return false; if (!expected_rtype) { assert(context->evaluating_macro); @@ -167,7 +173,7 @@ static inline bool sema_analyse_var_decl(Context *context, Decl *decl) decl->type = decl->var.type_info->type; if (decl->var.init_expr) { - if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return decl_poison(decl); + if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr)) return decl_poison(decl); } if (!sema_add_local(context, decl)) return decl_poison(decl); return true; @@ -262,7 +268,7 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) context_pop_scope(context); if (!success) return false; context_push_scope(context); - success = sema_analyse_expr(context, type_bool, expr); + success = sema_analyse_expr_of_required_type(context, type_bool, expr); statement->do_stmt.expr = context_pop_defers_and_wrap_expr(context, expr); context_pop_scope(context); return success; @@ -315,7 +321,7 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) // Conditional scope start context_push_scope(context); Expr *cond = statement->for_stmt.cond; - success = sema_analyse_expr(context, type_bool, cond); + success = sema_analyse_expr_of_required_type(context, type_bool, cond); statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond); // Conditional scope end context_pop_scope(context); @@ -550,35 +556,22 @@ static bool sema_analyse_case_expr(Context *context, Type* to_type, Ast *case_st Expr *case_expr = case_stmt->case_stmt.expr; // TODO handle enums // TODO string expr - if (!sema_analyse_expr(context, to_type, case_expr)) return false; + if (!sema_analyse_expr_of_required_type(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; } - // As a special case, we handle bools, converting them to 0 / 1 - if (case_expr->const_expr.kind == TYPE_BOOL) - { - case_stmt->case_stmt.value_type = CASE_VALUE_UINT; - case_stmt->case_stmt.val = case_expr->const_expr.b ? 1 : 0; - return true; - } + if (!cast_to_runtime(case_expr)) return false; - // TODO check for int - /* - if (case_expr->const_expr.kind < TYPE__!= CONST_INT && case_expr->const_expr.type != CONST_BOOL) + if (case_expr->const_expr.kind == TYPE_BOOL) return true; + + if (!type_is_integer(case_expr->type)) { 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; - 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; } @@ -649,7 +642,7 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) for (unsigned j = 0; j < i; j++) { Ast *other = statement->switch_stmt.cases[j]; - if (other->ast_kind == AST_CASE_STMT && other->case_stmt.val == stmt->case_stmt.val) + if (other->ast_kind == AST_CASE_STMT && expr_const_compare(&other->case_stmt.expr->const_expr, &stmt->case_stmt.expr->const_expr, BINARYOP_EQ)) { SEMA_ERROR(stmt, "The same case value appears more than once."); SEMA_PREV(other, "Here is the previous use of that value."); diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 5c3a2a98e..7b23699f3 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -28,7 +28,7 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type) uint64_t len = 0; if (type->array.len) { - if (!sema_analyse_expr(context, type_usize, type->array.len)) return type_info_poison(type); + if (!sema_analyse_expr_of_required_type(context, type_usize, type->array.len)) return type_info_poison(type); if (type->array.len->expr_kind != EXPR_CONST) { SEMA_ERROR(type->array.len, "Expected a constant value as array size."); diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 13d3ab5bd..204008990 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -316,26 +316,6 @@ const char *token_type_to_string(TokenType type) case TOKEN_DOCS_LINE: return "DOCS_LINE"; - - case TOKEN_AT_CONST: - return "@const"; - case TOKEN_AT_DEPRECATED: - return "@deprecated"; - case TOKEN_AT_ENSURE: - return "@ensure"; - case TOKEN_AT_PARAM: - return "@param"; - case TOKEN_AT_PURE: - return "@pure"; - case TOKEN_AT_RETURN: - return "@return"; - case TOKEN_AT_REQUIRE: - return "@require"; - case TOKEN_AT_THROWS: - return "@throws"; - case TOKEN_AT_REQPARSE: - return "@reqparse"; - case TOKEN_CT_CASE: return "$case"; case TOKEN_CT_DEFAULT: diff --git a/src/compiler/types.c b/src/compiler/types.c index bdcbb0b2e..c5cf89e77 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -8,7 +8,7 @@ Type *type_bool, *type_void, *type_string, *type_voidptr; Type *type_float, *type_double; Type *type_char, *type_short, *type_int, *type_long, *type_isize; Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; -Type *type_compint, *type_compuint, *type_compfloat; +Type *type_compint, *type_compfloat; Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong; Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong; @@ -196,7 +196,7 @@ size_t type_size(Type *canonical) case TYPE_F64: return canonical->builtin.bytesize; case TYPE_IXX: - return 4; + return 8; case TYPE_FXX: return 8; case TYPE_FUNC: diff --git a/src/compiler_tests/tests.c b/src/compiler_tests/tests.c index 1f5892a95..f54ff9fdf 100644 --- a/src/compiler_tests/tests.c +++ b/src/compiler_tests/tests.c @@ -18,7 +18,7 @@ static void test_lexer(void) printf("Begin lexer testing.\n"); printf("-- Check number of keywords...\n"); int tokens_found = 0; - const int EXPECTED_TOKENS = 12 + 73 + 9; + const int EXPECTED_TOKENS = TOKEN_CT_SWITCH - TOKEN_ALIAS + 1 + TOKEN_C_ULONGLONG - TOKEN_VOID + 1; const char* tokens[TOKEN_EOF]; int len[TOKEN_EOF]; Lexer lexer; @@ -26,7 +26,7 @@ static void test_lexer(void) { const char* token = token_type_to_string((TokenType)i); tokens[i] = token; - len[i] = strlen(token); + len[i] = (int)strlen(token); TokenType lookup = TOKEN_IDENT; const char* interned = symtab_add(token, len[i], fnv1a(token, len[i]), &lookup); if (lookup != TOKEN_IDENT) @@ -48,7 +48,7 @@ static void test_lexer(void) printf("-> %d keywords found.\n", tokens_found); EXPECT("Keywords", tokens_found, EXPECTED_TOKENS); - const int BENCH_REPEATS = 100000; + const int BENCH_REPEATS = 10000; printf("-- Test keyword lexing speed...\n"); bench_begin(); @@ -56,7 +56,10 @@ static void test_lexer(void) { for (int i = 1; i < TOKEN_EOF; i++) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-variable" volatile TokenType t = lexer_scan_ident_test(&lexer, tokens[i]).type; +#pragma clang diagnostic pop } } @@ -95,7 +98,27 @@ static void test_lexer(void) void test_compiler(void) { - compiler_init(); + const char **files = NULL; + file_add_wildcard_files(&files, "tests", true); + if (!vec_size(files)) + { + error_exit("No test files could be found."); + } + + const char **single_file = VECNEW(const char *, 1); + vec_add(single_file, files[0]); + + VECEACH(files, i) + { + printf("Running %s...\n", files[i]); + char *res = NULL; + asprintf(&res, "tests/%s", files[i]); + single_file[0] = res; + BuildTarget target = { .type = TARGET_TYPE_EXECUTABLE, .sources = single_file, .name = "a.out" }; + compile_files(&target); + free(res); + } + } void test_file(void) diff --git a/src/main.c b/src/main.c index 29d009ca8..2d30e901c 100644 --- a/src/main.c +++ b/src/main.c @@ -10,6 +10,9 @@ int main(int argc, const char *argv[]) // First setup memory memory_init(); + // Init the compiler + compiler_init(); + // Parse arguments. parse_arguments(argc, argv);