diff --git a/.gitattributes b/.gitattributes index 5727d6143..51b6b4f6a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ $ cat .gitattributes -* text=auto +* text=lf *.c3 linguist-language=C *.c3t linguist-language=C diff --git a/CMakeLists.txt b/CMakeLists.txt index 426d6dde5..f1bbb5c2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,7 @@ add_executable(c3c src/compiler/sema_types.c src/compiler/sema_stmts.c src/compiler/number.c + src/compiler/float.c src/compiler/linker.c src/utils/vmem.c src/utils/vmem.h diff --git a/resources/lib/std/math.c3 b/resources/lib/std/math.c3 index 811b48a8c..1645a7f1d 100644 --- a/resources/lib/std/math.c3 +++ b/resources/lib/std/math.c3 @@ -50,7 +50,7 @@ const DOUBLE_MIN_10_EXP = -307; const DOUBLE_MAX_EXP = 1024; const DOUBLE_MIN_EXP = -1021; const DOUBLE_EPSILON = 2.22044604925031308085e-16; - +/* const QUAD_MAX = 1.18973149535723176508575932662800702e+4932; const QUAD_MIN = 3.36210314311209350626267781732175260e-4932; const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966; @@ -62,7 +62,7 @@ const QUAD_MIN_10_EXP = -4931; const QUAD_MAX_EXP = 16384; const QUAD_MIN_EXP = -16481; const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34; - +*/ private union DoubleLong { double f; @@ -107,13 +107,13 @@ func double log10(double x) hx += 0x3ff00000 - 0x3fe6a09e; k += (int)(hx >> 20) - 0x3ff; hx = (hx & 0x000fffff) + 0x3fe6a09e; - u.i = (ulong)(hx << 32) | (u.i & 0xffffffff); + u.i = (ulong)(hx << 32) | (u.i & 0xffffffffu64); x = u.f; hx += 0x3ff00000 - 0x3fe6a09e; k += (int)(hx >> 20) - 0x3ff; hx = (hx & 0x000fffff) + 0x3fe6a09e; - u.i = (ulong)(hx << 32) | (u.i & 0xffffffff); + u.i = (ulong)(hx << 32) | (u.i & 0xffffffffu64); x = u.f; @@ -131,7 +131,7 @@ func double log10(double x) double hi = f - hfsq; u.f = hi; // u.i &= (ulong)(-1) << 32; - u.i &= 0xFFFFFFFF00000000; + u.i &= 0xFFFFFFFF00000000u64; hi = u.f; double lo = f - hi - hfsq + s * (hfsq + r); diff --git a/src/compiler/ast.c b/src/compiler/ast.c index fa148aab8..110b5923d 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -223,7 +223,7 @@ Expr *expr_new(ExprKind kind, SourceSpan start) Expr *expr = expr_calloc(); expr->expr_kind = kind; expr->span = start; - expr->type = expr->original_type = NULL; + expr->type = NULL; return expr; } @@ -293,10 +293,6 @@ UnaryOp unary_op[TOKEN_LAST + 1] = { }; -PostUnaryOp post_unary_op[TOKEN_LAST + 1] = { - [TOKEN_PLUSPLUS] = POSTUNARYOP_INC, - [TOKEN_MINUSMINUS] = POSTUNARYOP_DEC, -}; @@ -319,11 +315,6 @@ UnaryOp unaryop_from_token(TokenType type) return unary_op[type]; } -PostUnaryOp post_unaryop_from_token(TokenType type) -{ - return post_unary_op[type]; -} - static Ast poison_ast = { .ast_kind = AST_POISONED }; Ast *poisoned_ast = &poison_ast; diff --git a/src/compiler/bigint.c b/src/compiler/bigint.c index c5ce795ef..c7b2dee2c 100644 --- a/src/compiler/bigint.c +++ b/src/compiler/bigint.c @@ -4,6 +4,7 @@ #include "compiler_internal.h" #include +#include static inline uint32_t u32_min(uint32_t a, uint32_t b) { @@ -20,2127 +21,882 @@ static inline unsigned unsigned_max(unsigned a, unsigned b) return a > b ? a : b; } -static inline const uint64_t *bigint_ptr(const BigInt *big_int) -{ - return big_int->digit_count == 1 ? &big_int->digit : big_int->digits; -} - - -#define ALLOC_DIGITS(_digits) ((_digits) ? malloc_arena(sizeof(uint64_t) * (_digits)) : NULL) - -static void normalize(BigInt *big_int) -{ - const uint64_t *digits = bigint_ptr(big_int); - unsigned last_non_zero = UINT32_MAX; - for (unsigned i = 0; i < big_int->digit_count; i++) - { - if (digits[i] != 0) - { - last_non_zero = i; - } - } - if (last_non_zero == UINT32_MAX) - { - big_int->is_negative = false; - big_int->digit_count = 0; - return; - } - big_int->digit_count = last_non_zero + 1; - if (!last_non_zero) - { - big_int->digit = digits[0]; - } -} static char digit_to_char(uint8_t digit, bool upper) { if (digit <= 9) { - return (char) (digit + '0'); + return (char)(digit + '0'); } if (digit <= 35) { - return (char) (digit + (upper ? 'A' : 'a') - 10); + return (char)(digit + (upper ? 'A' : 'a') - 10); } FATAL_ERROR("Can't reach"); } -static bool bit_at_index(const BigInt *big_int, size_t index) +#define HI32(_x) ((_x) >> 32) +#define LO32(_x) ((_x) & 0xffffffff) +#define ISNEG(_x) (((uint64_t)_x) >> 63) + +char *i128_to_string(Int128 op, uint64_t base, bool is_signed) { - size_t digit_index = index / 64; - if (digit_index >= big_int->digit_count) + assert(base >= 2 && base <= 16); + static char digits[16] = "0123456789ABCDEF"; + char buffer[130]; + char *loc = buffer; + bool add_minus = is_signed && ISNEG(op.high); + if (add_minus) op = i128_neg(op); + Int128 base_div = { 0, base }; + do { - return false; + Int128 rem = i128_urem(op, base_div); + *(loc++) = digits[rem.low]; + op = i128_udiv(op, base_div); + } while (!i128_is_zero(op)); + char *res = malloc(loc - buffer + 2); + char *c = res; + if (add_minus) *(c++) = '-'; + while (loc > buffer) + { + *(c++) = *(--loc); } - size_t digit_bit_index = index % 64; - const uint64_t *digits = bigint_ptr(big_int); - uint64_t digit = digits[digit_index]; - return ((digit >> digit_bit_index) & 0x1U) == 0x1U; + *c = 0; + return res; } -uint32_t bigint_hash(BigInt x) +char *int_to_str(Int i, int radix) { - if (x.digit_count == 0) return 0; - return (uint32_t) bigint_ptr(&x)[0]; -} - -static size_t bigint_bits_needed(const BigInt *big_int) -{ - size_t full_bits = big_int->digit_count * 64; - size_t leading_zero_count = bigint_clz(big_int, full_bits); - size_t bits_needed = full_bits - leading_zero_count; - return bits_needed + big_int->is_negative; -} - -void bigint_init_unsigned(BigInt *big_int, uint64_t value) -{ - if (value == 0) - { - big_int->digit_count = 0; - big_int->is_negative = false; - return; - } - big_int->digit_count = 1; - big_int->digit = value; - big_int->is_negative = false; -} - -void bigint_init_signed(BigInt *dest, int64_t value) -{ - if (value >= 0) - { - return bigint_init_unsigned(dest, (uint64_t)value); - } - dest->is_negative = true; - dest->digit_count = 1; - dest->digit = ((uint64_t) (-(value + 1))) + 1; -} - -void bigint_init_bigint(BigInt *dest, const BigInt *src) -{ - if (src->digit_count == 0) - { - return bigint_init_unsigned(dest, 0); - } - if (src->digit_count == 1) - { - dest->digit_count = 1; - dest->digit = src->digit; - dest->is_negative = src->is_negative; - return; - } - dest->is_negative = src->is_negative; - dest->digit_count = src->digit_count; - dest->digits = ALLOC_DIGITS(dest->digit_count); - memcpy(dest->digits, src->digits, sizeof(uint64_t) * dest->digit_count); -} - -void bigint_negate(BigInt *dest, const BigInt *source) -{ - bigint_init_bigint(dest, source); - dest->is_negative = !dest->is_negative; - normalize(dest); -} - -static void to_twos_complement(BigInt *dest, const BigInt *source, size_t bit_count) -{ - if (bit_count == 0 || source->digit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - if (source->is_negative) - { - BigInt negated = { 0 }; - bigint_negate(&negated, source); - - BigInt inverted = { 0 }; - bigint_not(&inverted, &negated, bit_count, false); - - BigInt one = { 0 }; - bigint_init_unsigned(&one, 1); - - bigint_add(dest, &inverted, &one); - return; - } - - dest->is_negative = false; - const uint64_t *source_digits = bigint_ptr(source); - if (source->digit_count == 1) - { - dest->digit = source_digits[0]; - if (bit_count < 64) - { - dest->digit &= (1ULL << bit_count) - 1; - } - dest->digit_count = 1; - normalize(dest); - return; - } - unsigned digits_to_copy = (unsigned int) (bit_count / 64); - unsigned leftover_bits = (unsigned int) (bit_count % 64); - dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); - if (dest->digit_count == 1 && leftover_bits == 0) - { - dest->digit = source_digits[0]; - if (dest->digit == 0) dest->digit_count = 0; - return; - } - dest->digits = malloc_arena(dest->digit_count * sizeof(uint64_t)); - for (size_t i = 0; i < digits_to_copy; i += 1) - { - uint64_t digit = (i < source->digit_count) ? source_digits[i] : 0; - dest->digits[i] = digit; - } - if (leftover_bits != 0) - { - uint64_t digit = (digits_to_copy < source->digit_count) ? source_digits[digits_to_copy] : 0; - dest->digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1); - } - normalize(dest); -} - -size_t bigint_clz(const BigInt *big_int, size_t bit_count) -{ - if (big_int->is_negative || bit_count == 0) - { - return 0; - } - if (big_int->digit_count == 0) - { - return bit_count; - } - size_t count = 0; - for (size_t i = bit_count - 1;;) - { - if (bit_at_index(big_int, i)) - { - return count; - } - count++; - if (i == 0) break; - i--; - } - return count; -} - -bool bigint_eql(BigInt a, BigInt b) -{ - return bigint_cmp(&a, &b) == CMP_EQ; -} - -static void from_twos_complement(BigInt *dest, const BigInt *src, size_t bit_count, bool is_signed) -{ - assert(!src->is_negative); - - if (bit_count == 0 || src->digit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - - if (is_signed && bit_at_index(src, bit_count - 1)) - { - BigInt negative_one = { 0 }; - bigint_init_signed(&negative_one, -1); - - BigInt minus_one = { 0 }; - bigint_add(&minus_one, src, &negative_one); - - BigInt inverted = { 0 }; - bigint_not(&inverted, &minus_one, bit_count, false); - - bigint_negate(dest, &inverted); - return; - - } - - bigint_init_bigint(dest, src); + return i128_to_string(i.i, radix, type_kind_is_signed(i.type)); } -void bigint_init_data(BigInt *dest, const uint64_t *digits, unsigned int digit_count, bool is_negative) +Int int_from_real(Real d, TypeKind type) { - if (digit_count == 0) return bigint_init_unsigned(dest, 0); - - if (digit_count == 1) - { - dest->digit_count = 1; - dest->digit = digits[0]; - dest->is_negative = is_negative; - normalize(dest); - return; - } - - dest->digit_count = digit_count; - dest->is_negative = is_negative; - dest->digits = ALLOC_DIGITS(digit_count); - memcpy(dest->digits, digits, sizeof(uint64_t) * digit_count); - - normalize(dest); + return (Int){ type_kind_is_unsigned(type) ? i128_from_float_unsigned(d) : i128_from_float_signed(d), + type }; } -/* TODO -void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) { - float128_t zero; - ui32_to_f128M(0, &zero); - - dest->is_negative = f128M_lt(&op->value, &zero); - float128_t abs_val; - if (dest->is_negative) { - f128M_sub(&zero, &op->value, &abs_val); - } else { - memcpy(&abs_val, &op->value, sizeof(float128_t)); - } - - float128_t max_u64; - ui64_to_f128M(UINT64_MAX, &max_u64); - if (f128M_le(&abs_val, &max_u64)) { - dest->digit_count = 1; - dest->data.digit = f128M_to_ui64(&op->value, softfloat_round_minMag, false); - bigint_normalize(dest); - return; - } - - float128_t amt; - f128M_div(&abs_val, &max_u64, &amt); - float128_t remainder; - f128M_rem(&abs_val, &max_u64, &remainder); - - dest->digit_count = 2; - dest->data.digits = allocate_nonzero(dest->digit_count); - dest->data.digits[0] = f128M_to_ui64(&remainder, softfloat_round_minMag, false); - dest->data.digits[1] = f128M_to_ui64(&amt, softfloat_round_minMag, false); - bigint_normalize(dest); -} -*/ - -bool bigint_fits_in_bits(const BigInt *big_int, size_t bit_count, bool is_signed) +Int128 i128_from_int(uint64_t i) { - assert(big_int->digit_count != 1 || big_int->digit != 0); - if (bit_count == 0) - { - return bigint_cmp_zero(big_int) == CMP_EQ; - } - if (big_int->digit_count == 0) - { - return true; - } - - if (!is_signed) - { - size_t full_bits = big_int->digit_count * 64; - size_t leading_zero_count = bigint_clz(big_int, full_bits); - return bit_count >= full_bits - leading_zero_count; - } - - BigInt one = { 0 }; - bigint_init_unsigned(&one, 1); - - BigInt shl_amt = { 0 }; - bigint_init_unsigned(&shl_amt, bit_count - 1); - - BigInt max_value_plus_one = { 0 }; - bigint_shl(&max_value_plus_one, &one, &shl_amt); - - BigInt max_value = { 0 }; - bigint_sub(&max_value, &max_value_plus_one, &one); - - BigInt min_value = { 0 }; - bigint_negate(&min_value, &max_value_plus_one); - - CmpRes min_cmp = bigint_cmp(big_int, &min_value); - CmpRes max_cmp = bigint_cmp(big_int, &max_value); - - - return (min_cmp == CMP_GT || min_cmp == CMP_EQ) && (max_cmp == CMP_LT || max_cmp == CMP_EQ); + return (Int128){ 0, i }; } -void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian) +Int128 i128_from_str(const char *str) { - if (bit_count == 0) + char c; + Int128 x = { 0, 0 }; + while ((c = *(str++)) != 0) { - return; - } - - BigInt twos_comp = { 0 }; - to_twos_complement(&twos_comp, big_int, bit_count); - - const uint64_t *twos_comp_digits = bigint_ptr(&twos_comp); - - size_t bits_in_last_digit = bit_count % 64; - if (bits_in_last_digit == 0) bits_in_last_digit = 64; - size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; - size_t unwritten_byte_count = 8 - bytes_in_last_digit; - - if (is_big_endian) - { - size_t last_digit_index = (bit_count - 1) / 64; - size_t digit_index = last_digit_index; - size_t buf_index = 0; - for (;;) - { - uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; - - for (size_t byte_index = 7;;) - { - uint8_t byte = (uint8_t) (x & 0xffU); - if (digit_index == last_digit_index) - { - buf[buf_index + byte_index - unwritten_byte_count] = byte; - if (byte_index == unwritten_byte_count) break; - } - else - { - buf[buf_index + byte_index] = byte; - } - - if (byte_index == 0) break; - byte_index -= 1; - x >>= 8U; - } - - if (digit_index == 0) break; - digit_index -= 1; - if (digit_index == last_digit_index) - { - buf_index += bytes_in_last_digit; - } - else - { - buf_index += 8; - } - } - } - else - { - size_t digit_count = (bit_count + 63) / 64; - size_t buf_index = 0; - for (size_t digit_index = 0; digit_index < digit_count; digit_index += 1) - { - uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; - - for (size_t byte_index = 0; - byte_index < 8 && (digit_index + 1 < digit_count || byte_index < bytes_in_last_digit); - byte_index += 1) - { - uint8_t byte = (uint8_t) (x & 0xffU); - buf[buf_index] = byte; - buf_index += 1; - x >>= 8U; - } - } + x = i128_add64(i128_mult64(x, 10), c - '0'); } + return x; } -uint64_t bigint_as_unsigned(const BigInt *bigint) +Int128 i128_from_strl(const char *str, const char *end) { - assert(!bigint->is_negative); - if (bigint->digit_count == 0) + char c; + Int128 x = { 0, 0 }; + while (str != end) { - return 0; + c = *(str++); + x = i128_add64(i128_mult64(x, 10), c - '0'); } - if (bigint->digit_count != 1) - { - FATAL_ERROR("Bigint exceeds u64"); - } - return bigint->digit; + return x; } -void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, - bool is_signed) +Int128 i128_from_hexstrl(const char *str, const char *end) { - if (bit_count == 0) + char c; + Int128 x = { 0, 0 }; + while (str != end) { - bigint_init_unsigned(dest, 0); - return; + c = *(str++); + x = i128_add64(i128_shl64(x, 4), hex_nibble(c)); } + return x; +} - dest->digit_count = (unsigned int) ((bit_count + 63) / 64); - uint64_t *digits; - if (dest->digit_count == 1) - { - digits = &dest->digit; - } - else - { - digits = ALLOC_DIGITS(dest->digit_count); - dest->digits = digits; - } +Int128 i128_add(Int128 op1, Int128 op2) +{ + uint64_t low = op1.low + op2.low; + uint64_t high = op1.high + op2.high; + if (low < op1.low) high++; + return (Int128){ high, low }; +} - size_t bits_in_last_digit = bit_count % 64; - if (bits_in_last_digit == 0) - { - bits_in_last_digit = 64; - } - size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; - size_t unread_byte_count = 8 - bytes_in_last_digit; +Int128 i128_add64(Int128 op1, uint64_t op2) +{ + uint64_t low = op1.low + op2; + return (Int128){ low < op1.low ? op1.high + 1 : op1.high, low }; +} - if (is_big_endian) - { - size_t buf_index = 0; - uint64_t digit = 0; - for (size_t byte_index = unread_byte_count; byte_index < 8; byte_index += 1) - { - uint8_t byte = buf[buf_index]; - buf_index += 1; - digit <<= 8U; - digit |= byte; - } - digits[dest->digit_count - 1] = digit; - for (size_t digit_index = 1; digit_index < dest->digit_count; digit_index += 1) - { - digit = 0; - for (size_t byte_index = 0; byte_index < 8; byte_index += 1) - { - uint8_t byte = buf[buf_index]; - buf_index += 1; - digit <<= 8; - digit |= byte; - } - digits[dest->digit_count - 1 - digit_index] = digit; - } - } - else - { - size_t buf_index = 0; - for (size_t digit_index = 0; digit_index < dest->digit_count; digit_index += 1) - { - uint64_t digit = 0; - size_t end_byte_index = (digit_index == dest->digit_count - 1) ? bytes_in_last_digit : 8; - for (size_t byte_index = 0; byte_index < end_byte_index; byte_index += 1) - { - uint64_t byte = buf[buf_index]; - buf_index += 1; +Int128 i128_sub(Int128 op1, Int128 op2) +{ + uint64_t low = op1.low - op2.low; + uint64_t high = op1.high - op2.high; - digit |= byte << (8 * byte_index); - } - digits[digit_index] = digit; - } - } + if (low > op1.low) high--; + return (Int128){ high, low }; +} +Int128 i128_sub64(Int128 op1, uint64_t op2) +{ + uint64_t low = op1.low - op2; + return (Int128){ low > op1.low ? op1.high - 1 : op1.high, low }; +} + +Int128 i128_extend(Int128 op, TypeKind type) +{ + int bits = type_kind_bitsize(type); + if (bits == 128) return op; + int shift = 128 - bits; + op = i128_shl64(op, shift); + bool is_signed = type_kind_is_signed(type); if (is_signed) { - normalize(dest); - BigInt tmp = { 0 }; - bigint_init_bigint(&tmp, dest); - from_twos_complement(dest, &tmp, bit_count, true); + return i128_ashr64(op, shift); } - else + return i128_lshr64(op, shift); +} + +Int128 i128_and(Int128 op1, Int128 op2) +{ + return (Int128){ op1.high & op2.high, op1.low & op2.low }; +} + +Int128 i128_or(Int128 op1, Int128 op2) +{ + return (Int128){ op1.high | op2.high, op1.low | op2.low }; +} + +Int128 i128_xor(Int128 op1, Int128 op2) +{ + return (Int128){ op1.high ^ op2.high, op1.low ^ op2.low }; +} + +Int128 i128_neg(Int128 op1) +{ + if (!op1.low && !op1.low) return op1; + return i128_add64(i128_not(op1), 1); +} + +Int128 i128_not(Int128 op1) +{ + return (Int128){ ~op1.high, ~op1.low }; +} + +static Int128 int64_mult(uint64_t u, uint64_t v) +{ + uint64_t u1 = LO32(u); + uint64_t v1 = LO32(v); + uint64_t t = u1 * v1; + uint64_t w3 = LO32(t); + uint64_t k = HI32(t); + + u >>= 32; + t = u * v1 + k; + k = LO32(t); + uint64_t w1 = HI32(t); + + v >>= 32; + t = u1 * v + k; + + return (Int128){ (u * v) + w1 + HI32(t), (t << 32) + w3 }; +} + +Int128 i128_mult(Int128 op1, Int128 op2) +{ + Int128 low_mult = int64_mult(op1.low, op2.low); + low_mult.high += op1.high * op2.low + op1.low * op2.high; + return low_mult; +} + +Int128 i128_mult64(Int128 op1, uint64_t op2) +{ + Int128 low_mult = int64_mult(op1.low, op2); + low_mult.high += op1.high * op2; + return low_mult; +} + + +CmpRes int128_scomp64(Int128 op1, int64_t op2) +{ + // Check all zero + if (!op2 && !op1.high && !op1.low) return CMP_EQ; + + bool lhs_sign = ISNEG(op1.high); + bool rhs_sign = ISNEG(op2); + if (lhs_sign != rhs_sign) { - dest->is_negative = false; - normalize(dest); + return lhs_sign ? CMP_LT : CMP_GT; } -} -#if defined(_MSC_VER) - -static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ - *result = op1 + op2; - return *result < op1 || *result < op2; -} - -static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ - *result = op1 - op2; - return *result > op1; -} - -bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ - *result = op1 * op2; - - if (op1 == 0 || op2 == 0) return false; - if (op1 > UINT64_MAX / op2) return true; - if (op2 > UINT64_MAX / op1) return true; - return false; -} - -#else - -static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ - return __builtin_uaddll_overflow((unsigned long long) op1, (unsigned long long) op2, - (unsigned long long *) result); -} - -static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ - return __builtin_usubll_overflow((unsigned long long) op1, (unsigned long long) op2, - (unsigned long long *) result); -} - -bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ - return __builtin_umulll_overflow((unsigned long long) op1, (unsigned long long) op2, - (unsigned long long *) result); -} - -#endif - -void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->digit_count == 0) + // Handle negative values + if (lhs_sign) { - return bigint_init_bigint(dest, op2); + // If this isn't a clean 11111... in the top bits, it's less than. + if (op1.high != UINT64_MAX) return CMP_LT; + if (op1.low == op2) return CMP_EQ; + return ((int64_t)op1.low) > op2 ? CMP_GT : CMP_LT; } - if (op2->digit_count == 0) - { - return bigint_init_bigint(dest, op1); - } - if (op1->is_negative == op2->is_negative) - { - dest->is_negative = op1->is_negative; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - bool overflow = add_u64_overflow(op1_digits[0], op2_digits[0], &dest->digit); - if (overflow == 0 && op1->digit_count == 1 && op2->digit_count == 1) + if (op1.high) return CMP_GT; + if (op1.low == op2) return CMP_EQ; + return op1.low > (uint64_t)op2 ? CMP_GT : CMP_LT; +} + +CmpRes int128_ucomp64(Int128 op1, uint64_t op2) +{ + if (op1.high) return CMP_GT; + if (op1.low == op2) return CMP_EQ; + return op1.low > op2 ? CMP_GT : CMP_LT; +} + +CmpRes i128_ucomp(Int128 op1, Int128 op2) +{ + if (op1.high > op2.high) return CMP_GT; + if (op1.high < op2.high) return CMP_LT; + if (op1.low == op2.low) return CMP_EQ; + return op1.low > op2.low ? CMP_GT : CMP_LT; +} + +Int128 i128_shl64(Int128 op1, uint64_t amount) +{ + if (amount == 0) return op1; + if (amount > 127) return (Int128){ 0, 0 }; + if (amount == 64) return (Int128){ op1.low, 0 }; + if (amount > 64) return (Int128){ op1.low << (amount - 64), 0 }; + return (Int128){ (op1.high <<= amount) | op1.low >> (64 - amount), op1.low << amount }; +} + +Int128 i128_shl(Int128 op1, Int128 op) +{ + if (op.high) return (Int128){ 0, 0 }; + return i128_shl64(op1, op.low); +} + +Int128 i128_lshr64(Int128 op1, uint64_t amount) +{ + if (amount == 0) return op1; + if (amount > 127) return (Int128){ 0, 0 }; + if (amount == 64) return (Int128){ 0, op1.high }; + if (amount > 64) return (Int128){ 0, op1.high >> (amount - 64) }; + op1.low >>= amount; + op1.low |= op1.high << (64 - amount); + op1.high >>= amount; + return op1; +} + + +Int128 i128_from_float_signed(Real d) +{ + return (Int128){ 0, (int64_t)d }; +} + +Int128 i128_from_float_unsigned(Real d) +{ + return (Int128){ 0, (uint64_t)d }; +} + +bool i128_get_bit(const Int128 *op, int bit) +{ + assert(bit < 128 && bit >= 0); + if (bit > 63) + { + return (op->high >> (bit - 64)) & 1; + } + return (op->low >> (64 - bit)) & 1; +} + +bool i128_is_zero(Int128 op) +{ + return op.high == 0 && op.low == 0; +} + +Int128 i128_lshr(Int128 op1, Int128 op2) +{ + if (op2.high != 0) return (Int128){ 0, 0 }; + return i128_lshr64(op1, op2.low); +} + +Int128 i128_ashr64(Int128 op1, uint64_t amount) +{ + if (!ISNEG(op1.high)) return i128_lshr64(op1, amount); + if (amount > 127) return (Int128){ UINT64_MAX, UINT64_MAX }; + if (amount == 64) return (Int128){ UINT64_MAX, op1.high }; + if (amount > 64) return (Int128){ UINT64_MAX, ((int64_t)op1.high) >> (amount - 64) }; + return (Int128){ ((int64_t)op1.high) >> amount, op1.low >> amount | (op1.high << (64 - amount)) }; +} + +Int128 i128_ashr(Int128 op1, Int128 op2) +{ + if (op2.high != 0) return ISNEG(op1.high) ? (Int128){ UINT64_MAX, UINT64_MAX } : (Int128){ 0, 0 }; + return i128_ashr64(op1, op2.low); +} + +Int128 i128_add_swrap64(Int128 op1, int64_t op2, bool *wrapped) +{ + Int128 res = i128_add64(op1, op2); + bool is_less = i128_scomp(res, op1) == CMP_LT; + *wrapped = op2 < 0 ? !is_less : is_less; + return res; +} + +Int128 i128_add_uwrap64(Int128 op1, uint64_t op2, bool *wrapped) +{ + Int128 res = i128_add64(op1, op2); + *wrapped = i128_ucomp(res, op1) == CMP_LT; + return res; +} + +bool i128_is_neg(Int128 op) +{ + return ISNEG(op.high); +} + +CmpRes i128_comp(Int128 op1, Int128 op2, Type *type) +{ + return type_is_signed(type) ? i128_scomp(op1, op2) : i128_ucomp(op1, op2); +} + +CmpRes i128_scomp(Int128 op1, Int128 op2) +{ + bool lhs_sign = op1.high & ((uint64_t)INT64_MIN); + bool rhs_sign = op2.high & ((uint64_t)INT64_MIN); + if (lhs_sign != rhs_sign) + { + return lhs_sign ? CMP_LT : CMP_GT; + } + if (op1.high > op2.high) return CMP_GT; + if (op1.high < op2.high) return CMP_LT; + if (op1.low == op2.low) return CMP_EQ; + return op1.low > op2.low ? CMP_GT : CMP_LT; +} + +static uint32_t popcnt64(uint64_t n) +{ + n -= ((n >> 1) & 0x5555555555555555); + n = (n & 0x3333333333333333) + ((n >> 2) & 0x3333333333333333); + return (((n + (n >> 4)) & 0xF0F0F0F0F0F0F0F) * 0x101010101010101) >> 56; +} + +uint32_t i128_popcnt(Int128 i) +{ + return popcnt64(i.high) + popcnt64(i.low); +} + +static uint32_t ctz64(uint64_t n) +{ + uint64_t i = ~n; + uint32_t c = ((i ^ (i + 1)) & i) >> 63; + + i = LO32(n) + 0xffffffff; + i = ((i & 0x100000000) ^ 0x100000000) >> 27; + c += i; + n >>= i; + + i = (n & 0xffff) + 0xffff; + i = ((i & 0x10000) ^ 0x10000) >> 12; + c += i; + n >>= i; + + i = (n & 0xff) + 0xff; + i = ((i & 0x100) ^ 0x100) >> 5; + c += i; + n >>= i; + + i = (n & 0xf) + 0xf; + i = ((i & 0x10) ^ 0x10) >> 2; + c += i; + n >>= i; + + i = (n & 3) + 3; + i = ((i & 4) ^ 4) >> 1; + c += i; + n >>= i; + + c += ((n & 1) ^ 1); + return c; +} + +uint32_t i128_ctz(const Int128 *n) +{ + return !n->low ? ctz64(n->high) + 64 : ctz64(n->low); +} + +static uint32_t clz64(uint64_t n) +{ + uint64_t neg_n = ~n; + uint32_t c = ((neg_n ^ (neg_n + 1)) & neg_n) >> 63; + + neg_n = (n >> 32) + 0xffffffff; + neg_n = ((neg_n & 0x100000000) ^ 0x100000000) >> 27; + c += neg_n; + n <<= neg_n; + + neg_n = (n >> 48) + 0xffff; + neg_n = ((neg_n & 0x10000) ^ 0x10000) >> 12; + c += neg_n; + n <<= neg_n; + + neg_n = (n >> 56) + 0xff; + neg_n = ((neg_n & 0x100) ^ 0x100) >> 5; + c += neg_n; + n <<= neg_n; + + neg_n = (n >> 60) + 0xf; + neg_n = ((neg_n & 0x10) ^ 0x10) >> 2; + c += neg_n; + n <<= neg_n; + + neg_n = (n >> 62) + 3; + neg_n = ((neg_n & 4) ^ 4) >> 1; + c += neg_n; + n <<= neg_n; + + c += (n >> 63) ^ 1; + + return c; +} + +uint32_t i128_clz(const Int128 *op) +{ + return op->high ? clz64(op->high) : clz64(op->low) + 64; +} + +int i128_lsb(const Int128 *op) +{ + return 127 - i128_ctz(op); +} + +int i128_msb(const Int128 *op) +{ + return 127 - i128_clz(op); +} + +Real i128_to_float(Int128 op) +{ + return (Real)op.low + (Real)ldexp(op.high, 64); +} + +Real i128_to_float_signed(Int128 op) +{ + if ((int64_t)op.high < 0 && (op.high != INT128_MIN.high || op.low != INT128_MIN.low)) + { + return -i128_to_float_signed(i128_neg(op)); + } + return (Real)op.low + (Real)ldexp(op.high, 64); +} + +void i128_udivrem(Int128 op1, Int128 op2, Int128 *div, Int128 *rem) +{ + *div = (Int128){ 0, 0 }; + int32_t shift = i128_clz(&op2) - i128_clz(&op1); + if (shift < 0) + { + *rem = op1; + return; + } + op2 = i128_shl64(op2, shift); + do + { + *div = i128_shl64(*div, 1); + if (i128_ucomp(op1, op2) != CMP_LT) { - dest->digit_count = 1; - normalize(dest); - return; + op1 = i128_sub(op1, op2); + div->low |= 1; } - unsigned i = 1; - uint64_t first_digit = dest->digit; - dest->digits = ALLOC_DIGITS(unsigned_max(op1->digit_count, op2->digit_count) + 1); - dest->digits[0] = first_digit; + op2 = i128_lshr64(op2, 1); - for (;;) - { - bool found_digit = false; - uint64_t x = (uint64_t) overflow; - overflow = 0; + } while (shift-- != 0); + rem->high = op1.high; + rem->low = op1.low; +} - if (i < op1->digit_count) - { - found_digit = true; - uint64_t digit = op1_digits[i]; - overflow += add_u64_overflow(x, digit, &x); - } +Int128 i128_udiv(Int128 op1, Int128 op2) +{ + Int128 div, rem; + i128_udivrem(op1, op2, &div, &rem); + return div; +} - if (i < op2->digit_count) - { - found_digit = true; - uint64_t digit = op2_digits[i]; - overflow += add_u64_overflow(x, digit, &x); - } +Int128 i128_urem(Int128 op1, Int128 op2) +{ + Int128 div, rem; + i128_udivrem(op1, op2, &div, &rem); + return rem; +} - dest->digits[i] = x; - i += 1; - - if (!found_digit) - { - dest->digit_count = i; - normalize(dest); - return; - } - } - } - const BigInt *op_pos; - const BigInt *op_neg; - if (op1->is_negative) +Int128 i128_srem(Int128 op1, Int128 op2) +{ + uint64_t topbit1 = op1.high & 0x8000000000000000; + uint64_t topbit2 = op2.high & 0x8000000000000000; + if (topbit1) op1 = i128_neg(op1); + if (topbit2) op2 = i128_neg(op2); + Int128 res = i128_urem(op1, op2); + if (topbit2 ^ topbit1) { - op_neg = op1; - op_pos = op2; - } - else - { - op_pos = op1; - op_neg = op2; + return i128_neg(res); } + return res; +} - BigInt op_neg_abs = { 0 }; - bigint_negate(&op_neg_abs, op_neg); - const BigInt *bigger_op; - const BigInt *smaller_op; - switch (bigint_cmp(op_pos, &op_neg_abs)) +Int128 i128_from_signed(int64_t i) +{ + return (Int128){ i < 0 ? UINT64_MAX : 0, i }; +} + +Int128 i128_from_unsigned(uint64_t i) +{ + return (Int128){ 0, i }; +} + +Int128 i128_sdiv(Int128 op1, Int128 op2) +{ + uint64_t topbit1 = op1.high & 0x8000000000000000; + uint64_t topbit2 = op2.high & 0x8000000000000000; + if (topbit1) op1 = i128_neg(op1); + if (topbit2) op2 = i128_neg(op2); + Int128 res = i128_udiv(op1, op2); + if (topbit2 ^ topbit1) { - case CMP_EQ: - bigint_init_unsigned(dest, 0); - return; - case CMP_LT: - bigger_op = &op_neg_abs; - smaller_op = op_pos; - dest->is_negative = true; + return i128_neg(res); + } + return res; +} + +static CmpRes int_icompare(Int op1, int64_t op2) +{ + if (type_kind_is_signed(op1.type)) + { + return i128_scomp(op1.i, i128_from_signed(op2)); + } + if (op1.i.high || op2 < 0 || op1.i.low > op2) return CMP_GT; + if (op1.i.low < op2) return CMP_LT; + return CMP_EQ; +} + +bool binary_op_matches_res(BinaryOp op, CmpRes res) +{ + switch (op) + { + case BINARYOP_GT: + return res == CMP_GT; + case BINARYOP_GE: + return res != CMP_LT; + case BINARYOP_LT: + return res == CMP_LT; + case BINARYOP_LE: + return res != CMP_GT; + case BINARYOP_NE: + return res != CMP_EQ; + case BINARYOP_EQ: + return res == CMP_EQ; + default: + UNREACHABLE + } +} + + +static CmpRes int_ucompare(Int op1, uint64_t op2) +{ + if (type_kind_is_signed(op1.type) && i128_is_neg(op1.i)) return CMP_LT; + if (op1.i.high || op1.i.low > op2) return CMP_GT; + if (op1.i.low < op2) return CMP_LT; + return CMP_EQ; +} + +static CmpRes int_compare(Int op1, Int op2) +{ + if (type_kind_is_signed(op1.type)) + { + if (type_kind_is_signed(op2.type)) return i128_scomp(op1.i, op2.i); + if (int_is_neg(op1)) return CMP_LT; + return i128_ucomp(op1.i, op2.i); + } + if (int_is_neg(op2)) return CMP_GT; + return i128_ucomp(op1.i, op2.i); +} + +bool int_comp(Int op1, Int op2, BinaryOp op) +{ + return binary_op_matches_res(op, int_compare(op1, op2)); +} + +bool int_icomp(Int op1, int64_t op2, BinaryOp op) +{ + return binary_op_matches_res(op, int_icompare(op1, op2)); +} + +bool int_ucomp(Int op1, uint64_t op2, BinaryOp op) +{ + return binary_op_matches_res(op, int_ucompare(op1, op2)); +} + +bool int_fits(Int op1, TypeKind kind) +{ + Int128 min; + Int128 max; + bool is_signed = false; + switch (kind) + { + case TYPE_I128: + min = INT128_MIN; + max = INT128_MAX; + is_signed = true; break; - case CMP_GT: - bigger_op = op_pos; - smaller_op = &op_neg_abs; - dest->is_negative = false; + case TYPE_I64: + min = i128_from_signed(INT64_MIN); + max = i128_from_signed(INT64_MAX); + is_signed = true; + break; + case TYPE_I32: + min = i128_from_signed(INT32_MIN); + max = i128_from_signed(INT32_MAX); + is_signed = true; + break; + case TYPE_I16: + min = i128_from_signed(INT16_MIN); + max = i128_from_signed(INT16_MAX); + is_signed = true; + break; + case TYPE_I8: + min = i128_from_signed(INT8_MIN); + max = i128_from_signed(INT8_MAX); + is_signed = true; + break; + case TYPE_U128: + max = UINT128_MAX; + break; + case TYPE_U64: + max = (Int128){ 0, UINT64_MAX }; + break; + case TYPE_U32: + max = (Int128){ 0, UINT32_MAX }; + break; + case TYPE_U16: + max = (Int128){ 0, UINT16_MAX }; + break; + case TYPE_U8: + max = (Int128){ 0, UINT8_MAX }; break; default: - FATAL_ERROR("UNREACHABLE"); + UNREACHABLE } - const uint64_t *bigger_op_digits = bigint_ptr(bigger_op); - const uint64_t *smaller_op_digits = bigint_ptr(smaller_op); - uint64_t overflow = (uint64_t)sub_u64_overflow(bigger_op_digits[0], smaller_op_digits[0], &dest->digit); - if (overflow == 0 && bigger_op->digit_count == 1 && smaller_op->digit_count == 1) - { - dest->digit_count = 1; - normalize(dest); - return; - } - uint64_t first_digit = dest->digit; - dest->digits = ALLOC_DIGITS(bigger_op->digit_count); - dest->digits[0] = first_digit; - unsigned i = 1; - - for (;;) - { - bool found_digit = false; - uint64_t x = bigger_op_digits[i]; - uint64_t prev_overflow = overflow; - overflow = 0; - - if (i < smaller_op->digit_count) - { - found_digit = true; - uint64_t digit = smaller_op_digits[i]; - overflow += sub_u64_overflow(x, digit, &x); - } - if (sub_u64_overflow(x, prev_overflow, &x)) - { - found_digit = true; - overflow += 1; - } - dest->digits[i] = x; - i += 1; - - if (!found_digit || i >= bigger_op->digit_count) - { - break; - } - } - assert(overflow == 0); - dest->digit_count = i; - normalize(dest); -} - -void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) -{ - BigInt unwrapped = { 0 }; - bigint_add(&unwrapped, op1, op2); - bigint_truncate(dest, &unwrapped, bit_count, is_signed); -} - -void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - BigInt op2_negated = { 0 }; - bigint_negate(&op2_negated, op2); - return bigint_add(dest, op1, &op2_negated); -} - -void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) -{ - BigInt op2_negated = { 0 }; - bigint_negate(&op2_negated, op2); - return bigint_add_wrap(dest, op1, &op2_negated, bit_count, is_signed); -} - -static void mul_overflow(uint64_t op1, uint64_t op2, uint64_t *lo, uint64_t *hi) -{ - uint64_t u1 = (op1 & 0xffffffff); - uint64_t v1 = (op2 & 0xffffffff); - uint64_t t = (u1 * v1); - uint64_t w3 = (t & 0xffffffff); - uint64_t k = (t >> 32); - - op1 >>= 32; - t = (op1 * v1) + k; - k = (t & 0xffffffff); - uint64_t w1 = (t >> 32); - - op2 >>= 32; - t = (u1 * op2) + k; - k = (t >> 32); - - *hi = (op1 * op2) + w1 + k; - *lo = (t << 32) + w3; -} - -static void mul_scalar(BigInt *dest, const BigInt *op, uint64_t scalar) -{ - bigint_init_unsigned(dest, 0); - - BigInt bi_64; - bigint_init_unsigned(&bi_64, 64); - - const uint64_t *op_digits = bigint_ptr(op); - size_t i = op->digit_count - 1; - - while (1) - { - BigInt shifted; - bigint_shl(&shifted, dest, &bi_64); - - uint64_t result_scalar; - uint64_t carry_scalar; - mul_overflow(scalar, op_digits[i], &result_scalar, &carry_scalar); - - BigInt result; - bigint_init_unsigned(&result, result_scalar); - - BigInt carry; - bigint_init_unsigned(&carry, carry_scalar); - - BigInt carry_shifted; - bigint_shl(&carry_shifted, &carry, &bi_64); - - BigInt tmp; - bigint_add(&tmp, &shifted, &carry_shifted); - - bigint_add(dest, &tmp, &result); - - if (i == 0) - { - break; - } - i -= 1; - } -} - -void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->digit_count == 0 || op2->digit_count == 0) - { - return bigint_init_unsigned(dest, 0); - } - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - - uint64_t carry; - mul_overflow(op1_digits[0], op2_digits[0], &dest->digit, &carry); - if (carry == 0 && op1->digit_count == 1 && op2->digit_count == 1) - { - dest->is_negative = (op1->is_negative != op2->is_negative); - dest->digit_count = 1; - normalize(dest); - return; - } - - bigint_init_unsigned(dest, 0); - - BigInt bi_64; - bigint_init_unsigned(&bi_64, 64); - - size_t i = op2->digit_count - 1; - for (;;) - { - BigInt shifted; - bigint_shl(&shifted, dest, &bi_64); - - BigInt scalar_result; - mul_scalar(&scalar_result, op1, op2_digits[i]); - - bigint_add(dest, &scalar_result, &shifted); - - if (i == 0) - { - break; - } - i -= 1; - } - - dest->is_negative = (op1->is_negative != op2->is_negative); - normalize(dest); -} - -void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) -{ - BigInt unwrapped = { 0 }; - bigint_mul(&unwrapped, op1, op2); - bigint_truncate(dest, &unwrapped, bit_count, is_signed); -} - -unsigned count_leading_zeros(uint32_t val) -{ - if (val == 0) return 32; - -#if defined(_MSC_VER) - unsigned long Index; - _BitScanReverse(&Index, Val); - return Index ^ 31; -#else - return __builtin_clz(val); -#endif -} - -/// Make a 64-bit integer from a high / low pair of 32-bit integers. -static inline uint64_t make_64(uint32_t hi, uint32_t lo) -{ - return (((uint64_t) hi) << 32) | ((uint64_t) lo); -} - -/// Return the high 32 bits of a 64 bit value. -static inline uint32_t hi_32(uint64_t value) -{ - return (uint32_t) (value >> 32); -} - -/// Return the low 32 bits of a 64 bit value. -static inline uint32_t lo_32(uint64_t val) -{ - return (uint32_t) val; -} - -/// Implementation of Knuth's Algorithm D (Division of nonnegative integers) -/// from "Art of Computer Programming, Volume 2", section 4.3.1, p. 272. The -/// variables here have the same names as in the algorithm. Comments explain -/// the algorithm and any deviation from it. -static void knuth_div(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t *r, unsigned m, unsigned n) -{ - assert(u && "Must provide dividend"); - assert(v && "Must provide divisor"); - assert(q && "Must provide quotient"); - assert(u != v && u != q && v != q && "Must use different memory"); - assert(n > 1 && "n must be > 1"); - - // b denotes the base of the number system. In our case b is 2^32. - const uint64_t b = ((uint64_t) 1) << 32; - - // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of - // u and v by d. Note that we have taken Knuth's advice here to use a power - // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of - // 2 allows us to shift instead of multiply and it is easy to determine the - // shift amount from the leading zeros. We are basically normalizing the u - // and v so that its high bits are shifted to the top of v's range without - // overflow. Note that this can require an extra word in u so that u must - // be of length m+n+1. - unsigned shift = count_leading_zeros(v[n - 1]); - uint32_t v_carry = 0; - uint32_t u_carry = 0; - if (shift) - { - for (unsigned i = 0; i < m + n; ++i) - { - uint32_t u_tmp = u[i] >> (32 - shift); - u[i] = (u[i] << shift) | u_carry; - u_carry = u_tmp; - } - for (unsigned i = 0; i < n; ++i) - { - uint32_t v_tmp = v[i] >> (32 - shift); - v[i] = (v[i] << shift) | v_carry; - v_carry = v_tmp; - } - } - u[m + n] = u_carry; - - // D2. [Initialize j.] Set j to m. This is the loop counter over the places. - int j = (int)m; - do - { - // D3. [Calculate q'.]. - // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q') - // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r') - // Now test if qp == b or qp*v[n-2] > b*rp + u[j+n-2]; if so, decrease - // qp by 1, increase rp by v[n-1], and repeat this test if rp < b. The test - // on v[n-2] determines at high speed most of the cases in which the trial - // value qp is one too large, and it eliminates all cases where qp is two - // too large. - uint64_t dividend = make_64(u[j + n], u[j + n - 1]); - uint64_t qp = dividend / v[n - 1]; - uint64_t rp = dividend % v[n - 1]; - if (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2]) - { - qp--; - rp += v[n - 1]; - if (rp < b && (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2])) - { - qp--; - } - } - - // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with - // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation - // consists of a simple multiplication by a one-place number, combined with - // a subtraction. - // The digits (u[j+n]...u[j]) should be kept positive; if the result of - // this step is actually negative, (u[j+n]...u[j]) should be left as the - // true value plus b**(n+1), namely as the b's complement of - // the true value, and a "borrow" to the left should be remembered. - int64_t borrow = 0; - for (unsigned i = 0; i < n; ++i) - { - uint64_t p = ((uint64_t) qp) * ((uint64_t) (v[i])); - int64_t subres = ((int64_t) (u[j + i])) - borrow - lo_32(p); - u[j + i] = lo_32((uint64_t) subres); - borrow = hi_32(p) - hi_32((uint64_t) subres); - } - bool is_neg = u[j + n] < borrow; - u[j + n] -= lo_32((uint64_t) borrow); - - // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was - // negative, go to step D6; otherwise go on to step D7. - q[j] = lo_32(qp); - if (is_neg) - { - // D6. [Add back]. The probability that this step is necessary is very - // small, on the order of only 2/b. Make sure that test data accounts for - // this possibility. Decrease q[j] by 1 - q[j]--; - // and add (0v[n-1]...v[1]v[0]) to (u[j+n]u[j+n-1]...u[j+1]u[j]). - // A carry will occur to the left of u[j+n], and it should be ignored - // since it cancels with the borrow that occurred in D4. - bool carry = false; - for (unsigned i = 0; i < n; i++) - { - uint32_t limit = u32_min(u[j + i], v[i]); - u[j + i] += v[i] + carry; - carry = u[j + i] < limit || (carry && u[j + i] == limit); - } - u[j + n] += carry; - } - - // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3. - } while (--j >= 0); - - // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired - // remainder may be obtained by dividing u[...] by d. If r is non-null we - // compute the remainder (urem uses this). - if (r) - { - // The value d is expressed by the "shift" value above since we avoided - // multiplication by d by using a shift left. So, all we have to do is - // shift right here. - if (shift) - { - uint32_t carry = 0; - for (int i = (int)n - 1; i >= 0; i--) - { - r[i] = (u[i] >> shift) | carry; - carry = u[i] << (32 - shift); - } - } - else - { - for (int i = (int)n - 1; i >= 0; i--) - { - r[i] = u[i]; - } - } - } -} - -static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigInt *Quotient, BigInt *Remainder) -{ - CmpRes cmp = bigint_cmp(op1, op2); - if (cmp == CMP_LT) - { - if (!Quotient) - { - bigint_init_unsigned(Quotient, 0); - } - if (!Remainder) - { - bigint_init_bigint(Remainder, op1); - } - return; - } - if (cmp == CMP_EQ) - { - if (!Quotient) - { - bigint_init_unsigned(Quotient, 1); - } - if (!Remainder) - { - bigint_init_unsigned(Remainder, 0); - } - return; - } - - const uint64_t *lhs = bigint_ptr(op1); - const uint64_t *rhs = bigint_ptr(op2); - unsigned lhsWords = op1->digit_count; - unsigned rhsWords = op2->digit_count; - - // First, compose the values into an array of 32-bit words instead of - // 64-bit words. This is a necessity of both the "short division" algorithm - // and the Knuth "classical algorithm" which requires there to be native - // operations for +, -, and * on an m bit value with an m*2 bit result. We - // can't use 64-bit operands here because we don't have native results of - // 128-bits. Furthermore, casting the 64-bit values to 32-bit values won't - // work on large-endian machines. - unsigned n = rhsWords * 2; - unsigned m = (lhsWords * 2) - n; - - // Allocate space for the temporary values we need either on the stack, if - // it will fit, or on the heap if it won't. - uint32_t space[128]; - uint32_t *U = NULL; - uint32_t *V = NULL; - uint32_t *Q = NULL; - uint32_t *R = NULL; - if ((Remainder ? 4 : 3) * n + 2 * m + 1 <= 128) - { - U = &space[0]; - V = &space[m + n + 1]; - Q = &space[(m + n + 1) + n]; - if (Remainder) - { - R = &space[(m + n + 1) + n + (m + n)]; - } - } - else - { - U = malloc_arena(sizeof(uint32_t) * (m + n + 1)); - V = malloc_arena(sizeof(uint32_t) * n); - Q = malloc_arena(sizeof(uint32_t) * (m + n)); - if (Remainder) - { - R = malloc_arena(sizeof(uint32_t) * n); - } - } - - // Initialize the dividend - memset(U, 0, (m + n + 1) * sizeof(uint32_t)); - for (unsigned i = 0; i < lhsWords; ++i) - { - uint64_t tmp = lhs[i]; - U[i * 2] = lo_32(tmp); - U[i * 2 + 1] = hi_32(tmp); - } - U[m + n] = 0; // this extra word is for "spill" in the Knuth algorithm. - - // Initialize the divisor - memset(V, 0, (n) * sizeof(uint32_t)); - for (unsigned i = 0; i < rhsWords; ++i) - { - uint64_t tmp = rhs[i]; - V[i * 2] = lo_32(tmp); - V[i * 2 + 1] = hi_32(tmp); - } - - // initialize the quotient and remainder - memset(Q, 0, (m + n) * sizeof(uint32_t)); - if (Remainder) memset(R, 0, n * sizeof(uint32_t)); - - // Now, adjust m and n for the Knuth division. n is the number of words in - // the divisor. m is the number of words by which the dividend exceeds the - // divisor (i.e. m+n is the length of the dividend). These sizes must not - // contain any zero words or the Knuth algorithm fails. - for (unsigned i = n; i > 0 && V[i - 1] == 0; i--) - { - n--; - m++; - } - for (unsigned i = m + n; i > 0 && U[i - 1] == 0; i--) - { - m--; - } - - // If we're left with only a single word for the divisor, Knuth doesn't work - // so we implement the short division algorithm here. This is much simpler - // and faster because we are certain that we can divide a 64-bit quantity - // by a 32-bit quantity at hardware speed and short division is simply a - // series of such operations. This is just like doing short division but we - // are using base 2^32 instead of base 10. - assert(n != 0 && "Divide by zero?"); - if (n == 1) - { - uint32_t divisor = V[0]; - uint32_t rem = 0; - for (int i = (int)m; i >= 0; i--) - { - uint64_t partial_dividend = make_64(rem, U[i]); - if (partial_dividend == 0) - { - Q[i] = 0; - rem = 0; - } - else if (partial_dividend < divisor) - { - Q[i] = 0; - rem = lo_32(partial_dividend); - } - else if (partial_dividend == divisor) - { - Q[i] = 1; - rem = 0; - } - else - { - Q[i] = lo_32(partial_dividend / divisor); - rem = lo_32(partial_dividend - (Q[i] * divisor)); - } - } - if (R) - { - R[0] = rem; - } - } - else - { - // Now we're ready to invoke the Knuth classical divide algorithm. In this - // case n > 1. - knuth_div(U, V, Q, R, m, n); - } - - // If the caller wants the quotient - if (Quotient) - { - Quotient->is_negative = false; - Quotient->digit_count = lhsWords; - if (lhsWords == 1) - { - Quotient->digit = make_64(Q[1], Q[0]); - } - else - { - Quotient->digits = ALLOC_DIGITS(lhsWords); - for (size_t i = 0; i < lhsWords; i += 1) - { - Quotient->digits[i] = make_64(Q[i * 2 + 1], Q[i * 2]); - } - } - } - - // If the caller wants the remainder - if (Remainder) - { - Remainder->is_negative = false; - Remainder->digit_count = rhsWords; - if (rhsWords == 1) - { - Remainder->digit = make_64(R[1], R[0]); - } - else - { - Remainder->digits = ALLOC_DIGITS(rhsWords); - for (size_t i = 0; i < rhsWords; i += 1) - { - Remainder->digits[i] = make_64(R[i * 2 + 1], R[i * 2]); - } - } - } -} - - -void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - assert(op2->digit_count != 0); // division by zero - if (op1->digit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - if (op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit = op1_digits[0] / op2_digits[0]; - dest->digit_count = 1; - dest->is_negative = op1->is_negative != op2->is_negative; - normalize(dest); - return; - } - if (op2->digit_count == 1 && op2_digits[0] == 1) - { - // X / 1 == X - bigint_init_bigint(dest, op1); - dest->is_negative = op1->is_negative != op2->is_negative; - normalize(dest); - return; - } - - const BigInt *op1_positive; - BigInt op1_positive_data; - if (op1->is_negative) - { - bigint_negate(&op1_positive_data, op1); - op1_positive = &op1_positive_data; - } - else - { - op1_positive = op1; - } - - const BigInt *op2_positive; - BigInt op2_positive_data; - if (op2->is_negative) - { - bigint_negate(&op2_positive_data, op2); - op2_positive = &op2_positive_data; - } - else - { - op2_positive = op2; - } - - bigint_unsigned_division(op1_positive, op2_positive, dest, NULL); - dest->is_negative = op1->is_negative != op2->is_negative; - normalize(dest); -} - -void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->is_negative != op2->is_negative) - { - bigint_div_trunc(dest, op1, op2); - BigInt mult_again = { 0 }; - bigint_mul(&mult_again, dest, op2); - mult_again.is_negative = op1->is_negative; - if (bigint_cmp(&mult_again, op1) != CMP_EQ) - { - BigInt tmp = { 0 }; - bigint_init_bigint(&tmp, dest); - BigInt neg_one = { 0 }; - bigint_init_signed(&neg_one, -1); - bigint_add(dest, &tmp, &neg_one); - } - normalize(dest); - } - else - { - bigint_div_trunc(dest, op1, op2); - } -} - -void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - assert(op2->digit_count != 0); // division by zero - if (op1->digit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - - if (op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit = op1_digits[0] % op2_digits[0]; - dest->digit_count = 1; - dest->is_negative = op1->is_negative; - normalize(dest); - return; - } - if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) - { - // special case this divisor - bigint_init_unsigned(dest, op1_digits[0]); - dest->is_negative = op1->is_negative; - normalize(dest); - return; - } - - if (op2->digit_count == 1 && op2_digits[0] == 1) - { - // X % 1 == 0 - bigint_init_unsigned(dest, 0); - return; - } - - const BigInt *op1_positive; - BigInt op1_positive_data; - if (op1->is_negative) - { - bigint_negate(&op1_positive_data, op1); - op1_positive = &op1_positive_data; - } - else - { - op1_positive = op1; - } - - const BigInt *op2_positive; - BigInt op2_positive_data; - if (op2->is_negative) - { - bigint_negate(&op2_positive_data, op2); - op2_positive = &op2_positive_data; - } - else - { - op2_positive = op2; - } - - bigint_unsigned_division(op1_positive, op2_positive, NULL, dest); - dest->is_negative = op1->is_negative; - normalize(dest); -} - -void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->is_negative) - { - BigInt first_rem; - bigint_rem(&first_rem, op1, op2); - first_rem.is_negative = !op2->is_negative; - BigInt op2_minus_rem; - bigint_add(&op2_minus_rem, op2, &first_rem); - bigint_rem(dest, &op2_minus_rem, op2); - dest->is_negative = false; - } - else - { - bigint_rem(dest, op1, op2); - dest->is_negative = false; - } -} - -void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->digit_count == 0) - { - return bigint_init_bigint(dest, op2); - } - if (op2->digit_count == 0) - { - return bigint_init_bigint(dest, op1); - } - if (op1->is_negative || op2->is_negative) - { - size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); - - BigInt twos_comp_op1 = { 0 }; - to_twos_complement(&twos_comp_op1, op1, big_bit_count); - - BigInt twos_comp_op2 = { 0 }; - to_twos_complement(&twos_comp_op2, op2, big_bit_count); - - BigInt twos_comp_dest = { 0 }; - bigint_or(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); - - from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); - } - else - { - dest->is_negative = false; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - if (op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit_count = 1; - dest->digit = op1_digits[0] | op2_digits[0]; - normalize(dest); - return; - } - dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - for (size_t i = 0; i < dest->digit_count; i += 1) - { - uint64_t digit = 0; - if (i < op1->digit_count) - { - digit |= op1_digits[i]; - } - if (i < op2->digit_count) - { - digit |= op2_digits[i]; - } - dest->digits[i] = digit; - } - normalize(dest); - } -} - -void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->digit_count == 0 || op2->digit_count == 0) - { - return bigint_init_unsigned(dest, 0); - } - if (op1->is_negative || op2->is_negative) - { - size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); - - BigInt twos_comp_op1 = { 0 }; - to_twos_complement(&twos_comp_op1, op1, big_bit_count); - - BigInt twos_comp_op2 = { 0 }; - to_twos_complement(&twos_comp_op2, op2, big_bit_count); - - BigInt twos_comp_dest = { 0 }; - bigint_and(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); - - from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); - } - else - { - dest->is_negative = false; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - if (op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit_count = 1; - dest->digit = op1_digits[0] & op2_digits[0]; - normalize(dest); - return; - } - - dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - - size_t i = 0; - for (; i < op1->digit_count && i < op2->digit_count; i += 1) - { - dest->digits[i] = op1_digits[i] & op2_digits[i]; - } - for (; i < dest->digit_count; i += 1) - { - dest->digits[i] = 0; - } - normalize(dest); - } -} - -void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->digit_count == 0) - { - return bigint_init_bigint(dest, op2); - } - if (op2->digit_count == 0) - { - return bigint_init_bigint(dest, op1); - } - if (op1->is_negative || op2->is_negative) - { - size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); - - BigInt twos_comp_op1 = { 0 }; - to_twos_complement(&twos_comp_op1, op1, big_bit_count); - - BigInt twos_comp_op2 = { 0 }; - to_twos_complement(&twos_comp_op2, op2, big_bit_count); - - BigInt twos_comp_dest = { 0 }; - bigint_xor(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); - - from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); - } - else - { - dest->is_negative = false; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - - assert(op1->digit_count > 0 && op2->digit_count > 0); - if (op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit_count = 1; - dest->digit = op1_digits[0] ^ op2_digits[0]; - normalize(dest); - return; - } - dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - size_t i = 0; - for (; i < op1->digit_count && i < op2->digit_count; i += 1) - { - dest->digits[i] = op1_digits[i] ^ op2_digits[i]; - } - for (; i < dest->digit_count; i += 1) - { - if (i < op1->digit_count) - { - dest->digits[i] = op1_digits[i]; - } - else if (i < op2->digit_count) - { - dest->digits[i] = op2_digits[i]; - } - else - { - FATAL_ERROR("Unreachable"); - } - } - normalize(dest); - } -} - -void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - assert(!op2->is_negative); - - if (op2->digit_count != 1) - { - FATAL_ERROR("Unsupported: shift left by amount greater than 64 bit integer"); - } - bigint_shl_int(dest, op1, bigint_as_unsigned(op2)); -} - -void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift) -{ - if (shift == 0) - { - 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; - if (dest->digit > op1_digits[0]) - { - dest->digit_count = 1; - dest->is_negative = op1->is_negative; - return; - } - } - - 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]; - dest->digits[dest->digit_count] = carry | (digit << leftover_shift_count); - dest->digit_count++; - if (leftover_shift_count > 0) - { - carry = digit >> (64 - leftover_shift_count); - } - else - { - carry = 0; - } - } - dest->digits[dest->digit_count] = carry; - dest->digit_count += 1; - dest->is_negative = op1->is_negative; - normalize(dest); -} - -void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) -{ - BigInt unwrapped = { 0 }; - bigint_shl(&unwrapped, op1, op2); - bigint_truncate(dest, &unwrapped, bit_count, is_signed); -} - -void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - assert(!op2->is_negative); - - if (op1->digit_count == 0) - { - return bigint_init_unsigned(dest, 0); - } - - if (op2->digit_count == 0) - { - return bigint_init_bigint(dest, op1); - } - - if (op2->digit_count != 1) - { - FATAL_ERROR("Unsupported: shift right by amount greater than 64 bit integer"); - } - - const uint64_t *op1_digits = bigint_ptr(op1); - uint64_t shift_amt = bigint_as_unsigned(op2); - - if (op1->digit_count == 1) - { - dest->digit = shift_amt < 64 ? op1_digits[0] >> shift_amt : 0; - dest->digit_count = 1; - dest->is_negative = op1->is_negative; - normalize(dest); - return; - } - - uint64_t digit_shift_count = shift_amt / 64; - uint64_t leftover_shift_count = shift_amt % 64; - - if (digit_shift_count >= op1->digit_count) - { - return bigint_init_unsigned(dest, 0); - } - - dest->digit_count = op1->digit_count - digit_shift_count; - 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]; - size_t dest_digit_index = op_digit_index - digit_shift_count; - digits[dest_digit_index] = carry | (digit >> leftover_shift_count); - carry = digit << (64 - leftover_shift_count); - - if (dest_digit_index == 0) break; - op_digit_index -= 1; - } - dest->is_negative = op1->is_negative; - normalize(dest); -} - - -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) -{ - BigInt zero; - bigint_init_unsigned(&zero, 0); - bigint_sub_wrap(dest, &zero, op, bit_count, true); -} - -void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) -{ - if (bit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - + bool op_is_signed = type_kind_is_signed(op1.type); if (is_signed) { - BigInt twos_comp = { 0 }; - to_twos_complement(&twos_comp, op, bit_count); - - BigInt inverted = { 0 }; - bigint_not(&inverted, &twos_comp, bit_count, false); - - from_twos_complement(dest, &inverted, bit_count, true); - return; - } - - assert(!op->is_negative); - - dest->is_negative = false; - const uint64_t *op_digits = bigint_ptr(op); - if (bit_count <= 64) - { - dest->digit_count = 1; - if (op->digit_count == 0) + if (op_is_signed) { - if (bit_count == 64) - { - dest->digit = UINT64_MAX; - } - else - { - dest->digit = (1ULL << bit_count) - 1; - } + if (i128_scomp(op1.i, min) == CMP_LT) return false; + if (i128_scomp(op1.i, max) == CMP_GT) return false; + return true; } - else if (op->digit_count == 1) - { - dest->digit = ~op_digits[0]; - if (bit_count != 64) - { - uint64_t - mask = (1ULL << bit_count) - 1; - dest->digit &= mask; - } - } - normalize(dest); - return; + // In the unsigned case, we don't need to test the lower limit. + return i128_ucomp(op1.i, max) != CMP_GT; } - dest->digit_count = (unsigned int) ((bit_count + 63) / 64); - assert(dest->digit_count >= op->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - size_t i = 0; - for (; i < op->digit_count; i += 1) + if (op_is_signed) { - dest->digits[i] = ~op_digits[i]; + if (i128_is_neg(op1.i)) return false; + if (i128_ucomp(op1.i, max) == CMP_GT) return false; + return true; } - for (; i < dest->digit_count; i += 1) - { - dest->digits[i] = 0xffffffffffffffffULL; - } - size_t digit_index = dest->digit_count - 1; - size_t digit_bit_index = bit_count % 64; - if (digit_bit_index != 0) - { - uint64_t - mask = (1ULL << digit_bit_index) - 1; - dest->digits[digit_index] &= mask; - } - normalize(dest); + // In the unsigned case, we don't need to test the lower limit. + return i128_ucomp(op1.i, max) != CMP_GT; } -void bigint_truncate(BigInt *dst, const BigInt *op, size_t bit_count, bool is_signed) +uint64_t int_to_u64(Int op) { - BigInt twos_comp; - to_twos_complement(&twos_comp, op, bit_count); - from_twos_complement(dst, &twos_comp, bit_count, is_signed); + return op.i.low; } -CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2) +int64_t int_to_i64(Int op) { - if (op1->is_negative && !op2->is_negative) return CMP_LT; - if (!op1->is_negative && op2->is_negative) return CMP_GT; - if (op1->digit_count > op2->digit_count) return op1->is_negative ? CMP_LT : CMP_GT; - if (op2->digit_count > op1->digit_count) return op1->is_negative ? CMP_GT : CMP_LT; - if (op1->digit_count == 0) return CMP_EQ; - - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - for (unsigned i = op1->digit_count - 1;; i--) - { - uint64_t op1_digit = op1_digits[i]; - uint64_t op2_digit = op2_digits[i]; - - if (op1_digit > op2_digit) - { - return op1->is_negative ? CMP_LT : CMP_GT; - } - if (op1_digit < op2_digit) - { - return op1->is_negative ? CMP_GT : CMP_LT; - } - if (i == 0) - { - return CMP_EQ; - } - } + return op.i.low; } -void bigint_print(BigInt *bigint, uint64_t base) +bool int_is_zero(Int op) { - if (bigint->digit_count == 0) - { - printf("0"); - return; - } - if (bigint->is_negative) - { - printf("-"); - } - if (bigint->digit_count == 1 && base == 10) - { - printf("%" PRIu64, bigint->digit); - return; - } - 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, base); - - 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 - - for (char *ptr = buf - 1; ptr >= start; ptr--) - { - printf("%c", *ptr); - } + return !op.i.high && !op.i.low; } -const char *bigint_to_error_string(const BigInt *bigint, uint64_t base) +Int int_add(Int op1, Int op2) { - if (bigint->digit_count == 0) - { - return "0"; - } - if (bigint->digit_count == 1 && base == 10) - { - char *res = NULL; - if (bigint->is_negative) - { - asprintf(&res, "-%" PRIu64, bigint->digit); - } - else - { - 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, base); - - 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 out; + assert(op1.type == op2.type); + return (Int){ i128_extend(i128_add(op1.i, op2.i), op1.type), op1.type }; } -void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base) +Int int_add64(Int op1, uint64_t op2) { - if (bigint->digit_count == 0) - { - fprintf(file, "0"); - return; - } - if (bigint->is_negative) - { - fprintf(file, "-"); - } - if (bigint->digit_count == 1 && base == 10) - { - fprintf(file, "%" PRIu64, bigint->digit); - return; - } - 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, base); - - 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 - - for (char *ptr = buf - 1; ptr >= start; ptr--) - { - fprintf(file, "%c", *ptr); - } + return (Int){ i128_extend(i128_add64(op1.i, op2), op1.type), op1.type }; } -size_t bigint_popcount_unsigned(const BigInt *big_int) + +Int int_sub(Int op1, Int op2) { - assert(!big_int->is_negative); - if (big_int->digit_count == 0) - { - return 0; - } - - unsigned count = 0; - size_t bit_count = big_int->digit_count * 64; - for (size_t i = 0; i < bit_count; i++) - { - if (bit_at_index(big_int, i)) - { - count += 1; - } - } - return count; + assert(op1.type == op2.type); + return (Int){ i128_extend(i128_sub(op1.i, op2.i), op1.type), op1.type }; } -size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) +Int int_sub64(Int op1, uint64_t op2) { - if (bit_count == 0) - { - return 0; - } - if (bi->digit_count == 0) - { - return 0; - } - - BigInt twos_comp = { 0 }; - to_twos_complement(&twos_comp, bi, bit_count); - - size_t count = 0; - for (size_t i = 0; i < bit_count; i += 1) - { - if (bit_at_index(&twos_comp, i)) - { - count += 1; - } - } - return count; + return (Int){ i128_extend(i128_sub64(op1.i, op2), op1.type), op1.type }; } -size_t bigint_ctz(const BigInt *bi, size_t bit_count) +Int int_mul(Int op1, Int op2) { - if (bit_count == 0) - { - return 0; - } - if (bi->digit_count == 0) - { - return bit_count; - } - - BigInt twos_comp = { 0 }; - to_twos_complement(&twos_comp, bi, bit_count); - - size_t count = 0; - for (size_t i = 0; i < bit_count; i += 1) - { - if (bit_at_index(&twos_comp, i)) - { - return count; - } - count += 1; - } - return count; + assert(op1.type == op2.type); + return (Int){ i128_extend(i128_mult(op1.i, op2.i), op1.type), op1.type }; } - -int64_t bigint_as_signed(const BigInt *bigint) +Int int_conv(Int op, TypeKind to_type) { - if (bigint->digit_count == 0) return 0; - if (bigint->digit_count != 1) - { - FATAL_ERROR("BigInt larger than i64"); - } + int bitsize = type_kind_bitsize(op.type); + int to_bitsize = type_kind_bitsize(to_type); + // Simple conversion, to 128 is a no op. + if (to_bitsize == bitsize || to_bitsize == 128) return (Int){ op.i, to_type }; - if (bigint->is_negative) + bool from_signed = type_kind_is_signed(op.type); + bool to_signed = type_kind_is_signed(to_type); + if (bitsize < to_bitsize) { - // TODO this code path is untested - if (bigint->digit <= 9223372036854775808ULL) + // Extending to a signed or with same signedness is a no-op. + if (to_signed || to_signed == from_signed) { - return (-((int64_t) (bigint->digit - 1))) - 1; + return (Int){ op.i, to_type }; } - FATAL_ERROR("BigInt does not fit in i64"); + // Extending from a signed to unsigned + int shift = 128 - to_bitsize; + // Cut off the top. + return (Int){ i128_lshr64(i128_shl64(op.i, shift), shift), to_type }; } - return (int64_t)bigint->digit; + int shift = 128 - to_bitsize; + // Cut off the top. + if (from_signed) + { + return (Int){ i128_ashr64(i128_shl64(op.i, shift), shift), to_type }; + } + return (Int){ i128_lshr64(i128_shl64(op.i, shift), shift), to_type }; } -CmpRes bigint_cmp_zero(const BigInt *op) +Int int_div(Int op1, Int op2) { - if (op->digit_count == 0) + assert(op1.type == op2.type); + Int128 res; + if (type_kind_is_signed(op1.type)) { - return CMP_EQ; + res = i128_sdiv(op1.i, op2.i); } - return op->is_negative ? CMP_LT : CMP_GT; + else + { + res = i128_udiv(op1.i, op2.i); + } + return (Int){ res, op1.type }; } - -void bigint_incr(BigInt *x) +Int int_rem(Int op1, Int op2) { - if (!x->digit_count) + assert(op1.type == op2.type); + Int128 res; + if (type_kind_is_signed(op1.type)) { - bigint_init_unsigned(x, 1); - return; + res = i128_srem(op1.i, op2.i); } - - if (x->digit_count == 1) + else { - if (x->is_negative && x->digit != 0) - { - x->digit -= 1; - return; - } - if (!x->is_negative && x->digit != UINT64_MAX) - { - x->digit += 1; - return; - } + res = i128_urem(op1.i, op2.i); } - - BigInt copy; - bigint_init_bigint(©, x); - - BigInt one; - bigint_init_unsigned(&one, 1); - - bigint_add(x, ©, &one); + return (Int){ res, op1.type }; } -Real bigint_as_float(const BigInt *bigint) +Int int_and(Int op1, Int op2) { - if (bigint_fits_in_bits(bigint, 64, bigint->is_negative)) - { - return bigint->is_negative ? (Real)bigint_as_signed(bigint) : (Real)bigint_as_unsigned(bigint); - } - BigInt div; - uint64_t mult = 0x100000000000ULL; - Real mul = 1; - bigint_init_unsigned(&div, mult); - BigInt current; - bigint_init_bigint(¤t, bigint); - Real f = 0; - do - { - BigInt temp; - bigint_mod(&temp, ¤t, &div); - f += bigint_as_signed(&temp) * mul; - mul *= mult; - bigint_div_trunc(&temp, ¤t, &div); - current = temp; - } - while (current.digit_count > 0); - return f; + assert(op1.type == op2.type); + return (Int){ i128_and(op1.i, op2.i), op1.type }; +} + +Int int_or(Int op1, Int op2) +{ + assert(op1.type == op2.type); + return (Int){ i128_or(op1.i, op2.i), op1.type }; +} + +Int int_xor(Int op1, Int op2) +{ + assert(op1.type == op2.type); + return (Int){ i128_xor(op1.i, op2.i), op1.type }; +} + +Int int_neg(Int op) +{ + return (Int){ i128_neg(op.i), op.type }; +} + +Int int_not(Int op) +{ + return (Int){ i128_extend(i128_not(op.i), op.type), op.type }; +} + +Int int_shr64(Int op1, uint64_t op2) +{ + if (type_kind_is_unsigned(op1.type)) + { + return (Int){ i128_extend(i128_lshr64(op1.i, op2), op1.type), op1.type }; + } + return (Int){ i128_extend(i128_ashr64(op1.i, op2), op1.type), op1.type }; +} + +Int int_shl64(Int op1, uint64_t op2) +{ + if (type_kind_is_unsigned(op1.type)) + { + return (Int){ i128_extend(i128_shl64(op1.i, op2), op1.type), op1.type }; + } + return (Int){ i128_extend(i128_shl64(op1.i, op2), op1.type), op1.type }; +} + +Real int_to_real(Int op) +{ + if (type_kind_is_signed(op.type)) + { + return i128_to_float_signed(op.i); + } + return i128_to_float(op.i); +} + +bool int_is_neg(Int op) +{ + if (type_kind_is_unsigned(op.type)) return false; + return i128_is_neg(op.i); +} + + +bool i128_can_convert_from_double(double x) +{ + return isfinite(x) + && x > -1 + && x < ldexp(1, 128); +} + +bool i128_can_convert_from_double_signed(double x) +{ + return isfinite(x) + && x >= -ldexp(1, 127) + && x < ldexp(1, 127); +} + +Int128 i128_from_double(double x) +{ + if (x >= ldexp(1, 64)) + { + uint64_t hi = (uint64_t)ldexp(x, -64); + uint64_t lo = (uint64_t)(x - ldexp(hi, 64)); + return (Int128){ hi, lo }; + } + return i128_from_int((uint64_t)x); +} + +Int128 i128_from_double_signed(double x) +{ + return x < 0 ? i128_neg(i128_from_signed(-x)) : i128_from_int(x); } diff --git a/src/compiler/c_abi_internal.h b/src/compiler/c_abi_internal.h index 8c78c64b3..6424c9c4d 100644 --- a/src/compiler/c_abi_internal.h +++ b/src/compiler/c_abi_internal.h @@ -40,6 +40,8 @@ typedef struct unsigned float_regs; } Regs; + + ABIArgInfo *c_abi_classify_return_type_default(Type *type); ABIArgInfo *c_abi_classify_argument_type_default(Type *type); void c_abi_func_create_win64(FunctionSignature *signature); @@ -49,6 +51,7 @@ void c_abi_func_create_aarch64(FunctionSignature *signature); void c_abi_func_create_riscv(FunctionSignature *signature); void c_abi_func_create_wasm(FunctionSignature *signature); + // Implementation static inline ABIArgInfo *abi_arg_by_reg_attr(ABIArgInfo *info) { diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index e242d720c..8a4ce1668 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -242,13 +242,19 @@ static void add_global_define(const char *name, Expr *value) stable_set(&dec->module->symbols, dec->name, dec); } -static void setup_int_define(const char *id, uint64_t i) +static void setup_int_define(const char *id, uint64_t i, Type *type) { TokenType token_type = TOKEN_CONST_IDENT; id = symtab_add(id, strlen(id), fnv1a(id, strlen(id)), &token_type); Expr *expr = expr_new(EXPR_CONST, INVALID_RANGE); - expr_const_set_int(&expr->const_expr, i, TYPE_IXX); - expr->original_type = expr->type = type_compint; + assert(type_is_integer(type)); + expr_const_set_int(&expr->const_expr, i, type->type_kind); + if (expr_const_will_overflow(&expr->const_expr, type->type_kind)) + { + error_exit("Integer define %s overflow.", id); + } + expr->type = type; + expr->const_expr.narrowable = true; expr->span = INVALID_RANGE; expr->resolve_status = RESOLVE_NOT_DONE; void *previous = stable_set(&global_context.compiler_defines, id, expr); @@ -264,7 +270,7 @@ static void setup_bool_define(const char *id, bool value) id = symtab_add(id, strlen(id), fnv1a(id, strlen(id)), &token_type); Expr *expr = expr_new(EXPR_CONST, INVALID_RANGE); expr_const_set_bool(&expr->const_expr, value); - expr->original_type = expr->type = type_bool; + expr->type = type_bool; expr->span = INVALID_RANGE; expr->resolve_status = RESOLVE_NOT_DONE; void *previous = stable_set(&global_context.compiler_defines, id, expr); @@ -275,15 +281,15 @@ static void setup_bool_define(const char *id, bool value) } void compiler_compile(void) { - setup_int_define("C_SHORT_SIZE", platform_target.width_c_short); - setup_int_define("C_INT_SIZE", platform_target.width_c_int); - setup_int_define("C_LONG_SIZE", platform_target.width_c_long); - setup_int_define("C_LONG_LONG_SIZE", platform_target.width_c_long_long); + setup_int_define("C_SHORT_SIZE", platform_target.width_c_short, type_long); + setup_int_define("C_INT_SIZE", platform_target.width_c_int, type_long); + setup_int_define("C_LONG_SIZE", platform_target.width_c_long, type_long); + setup_int_define("C_LONG_LONG_SIZE", platform_target.width_c_long_long, type_long); setup_bool_define("C_CHAR_IS_SIGNED", platform_target.signed_c_char); setup_bool_define("PLATFORM_BIG_ENDIAN", platform_target.big_endian); setup_bool_define("PLATFORM_I128_SUPPORTED", platform_target.int128); - setup_int_define("COMPILER_OPT_LEVEL", (int)active_target.optimization_level); - setup_int_define("COMPILER_SIZE_OPT_LEVEL", (int)active_target.size_optimization_level); + setup_int_define("COMPILER_OPT_LEVEL", (int)active_target.optimization_level, type_int); + setup_int_define("COMPILER_SIZE_OPT_LEVEL", (int)active_target.size_optimization_level, type_int); setup_bool_define("COMPILER_SAFE_MODE", active_target.feature.safe_mode); global_context_clear_errors(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 9750bc9dd..574a888c4 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -12,17 +12,8 @@ #include "utils/malloc.h" #include -#if DBL_DIG == LDBL_DIG -#define LONG_DOUBLE 0 -#else -#define LONG_DOUBLE 1 -#endif -#if LONG_DOUBLE -typedef long double Real; -#else typedef double Real; -#endif #define MAX_ARRAYINDEX INT64_MAX typedef uint64_t ByteSize; @@ -69,16 +60,27 @@ typedef struct Type_ Type; typedef unsigned AstId; -typedef struct BigInt_ +typedef struct Int128_ { - unsigned digit_count; - bool is_negative; - union { - uint64_t digit; - uint64_t *digits; - }; -} BigInt; + uint64_t high; + uint64_t low; +} Int128; +typedef struct +{ + Int128 i; + TypeKind type; +} Int; + +typedef struct +{ + Real f; + TypeKind type; +} Float; + +#define UINT128_MAX ((Int128) { UINT64_MAX, UINT64_MAX }) +#define INT128_MAX ((Int128) { INT64_MAX, UINT64_MAX }) +#define INT128_MIN ((Int128) { (uint64_t)INT64_MIN, 0 }) typedef enum { @@ -125,19 +127,12 @@ typedef struct ConstInitializer_ typedef struct { - ConstKind const_kind; + ConstKind const_kind : 8; + bool narrowable : 1; union { - struct - { - Real f; - TypeKind float_type; - }; - struct - { - BigInt i; - TypeKind int_type; - }; + Float fxx; + Int ixx; bool b; struct { @@ -266,7 +261,7 @@ typedef struct struct Type_ { - TypeKind type_kind : 8; + TypeKind type_kind; Type *canonical; const char *name; Type **type_cache; @@ -288,6 +283,8 @@ struct Type_ Type *pointer; // Type[<123>] or Type<[123]> TypeVector vector; + // Failable + Type *failable; }; }; @@ -295,6 +292,7 @@ struct TypeInfo_ { ResolveStatus resolve_status : 3; bool virtual_type : 1; + bool failable : 1; Type *type; TypeInfoKind kind; SourceSpan span; @@ -349,7 +347,6 @@ typedef struct VarDecl_ { VarDeclKind kind : 4; bool constant : 1; - bool failable : 1; bool unwrap : 1; bool vararg : 1; bool is_static : 1; @@ -420,7 +417,6 @@ typedef struct FunctionSignature_ CallABI call_abi : 4; Variadic variadic : 3; bool has_default : 1; - bool failable : 1; bool use_win64 : 1; TypeInfo *rtype; struct ABIArgInfo_ *ret_abi_info; @@ -493,7 +489,6 @@ typedef struct typedef struct { - bool failable : 1; Decl **parameters; TypeInfo *type_parent; // May be null TypeInfo *rtype; // May be null! @@ -663,12 +658,6 @@ typedef struct } ExprUnary; -typedef struct -{ - Expr* expr; - PostUnaryOp operator; -} ExprPostUnary; - @@ -797,6 +786,7 @@ typedef struct typedef struct { CastKind kind; + bool implicit; Expr *expr; TypeInfo *type_info; union @@ -919,12 +909,9 @@ typedef struct struct Expr_ { ExprKind expr_kind : 8; - ResolveStatus resolve_status : 3; - bool failable : 1; - bool pure : 1; + ResolveStatus resolve_status : 4; SourceSpan span; Type *type; - Type *original_type; union { Expr *group_expr; ExprLen len_expr; @@ -943,7 +930,6 @@ struct Expr_ ExprTernary ternary_expr; ExprUnary unary_expr; Expr** try_unwrap_chain_expr; - ExprPostUnary post_expr; ExprTryUnwrap try_unwrap_expr; ExprCall call_expr; ExprSlice slice_expr; @@ -1329,7 +1315,7 @@ typedef union const char *string; size_t strlen; }; - Real value; + Float value; struct { bool is_base64 : 1; @@ -1412,7 +1398,6 @@ typedef struct Context_ Ast **returns_cache; }; Type *rtype; - bool failable_return; int in_volatile_section; MacroScope macro_scope; struct { @@ -1550,10 +1535,10 @@ extern Type *type_ichar, *type_short, *type_int, *type_long, *type_isize; extern Type *type_char, *type_ushort, *type_uint, *type_ulong, *type_usize; extern Type *type_iptr, *type_uptr, *type_iptrdiff, *type_uptrdiff; extern Type *type_u128, *type_i128; -extern Type *type_compint, *type_compfloat; extern Type *type_typeid, *type_anyerr, *type_typeinfo; extern Type *type_virtual, *type_virtual_generic; extern Type *type_complist; +extern Type *type_anyfail; extern const char *attribute_list[NUMBER_OF_ATTRIBUTES]; @@ -1624,64 +1609,131 @@ static inline Ast *extend_ast_with_prev_token(Context *context, Ast *ast) typedef enum CmpRes_ { - CMP_LT, - CMP_GT, - CMP_EQ, + CMP_LT = -1, + CMP_EQ = 0, + CMP_GT = 1, } CmpRes; -void bigint_init_unsigned(BigInt *big_int, uint64_t value); -void bigint_init_signed(BigInt *big_int, int64_t value); -void bigint_init_bigint(BigInt *dest, const BigInt *src); -void bigint_init_data(BigInt *dest, const uint64_t *digits, unsigned int digit_count, bool is_negative); -void bigint_negate(BigInt *dest, const BigInt *source); -size_t bigint_clz(const BigInt *big_int, size_t bit_count); -size_t bigint_ctz(const BigInt *big_int, size_t bit_count); -bool bigint_fits_in_bits(const BigInt *big_int, size_t bit_count, bool is_signed); -void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian); -void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, bool is_signed); -void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift); -void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count); -void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); -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); -int64_t bigint_as_signed(const BigInt *bigint); -Real bigint_as_float(const BigInt *bigint); -void bigint_truncate(BigInt *dst, const BigInt *op, size_t bit_count, bool is_signed); -void bigint_incr(BigInt *x); -size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count); -size_t bigint_popcount_unsigned(const BigInt *big_int); +typedef enum +{ + FLOAT_NAN, + FLOAT_INFINITY, + FLOAT_NORMAL, + FLOAT_ZERO +} FloatCategory; + +typedef enum +{ + ROUNDING_TOWARD_ZERO, + ROUNDING_NEAREST_TIES_TO_EVEN, + ROUNDING_TOWARD_POSITIVE, + ROUNDING_TOWARD_NEGATIVE, + ROUNDING_NEAREST_TIES_TO_AWAY +} FloatRounding; + +typedef struct FloatXX +{ + union + { + double f64; + float f32; + }; + TypeKind type; +} FloatXX; + void type_setup(PlatformTarget *target); +Float float_add(Float op1, Float op2); +Float float_sub(Float op1, Float op2); +Float float_mul(Float op1, Float op2); +Float float_div(Float op1, Float op2); +Float float_neg(Float op); +Float float_from_string(const char *string, char **error); +Float float_from_hex(const char *string, char **error); +Int128 i128_from_double(double x); +bool int_ucomp(Int op1, uint64_t op2, BinaryOp op); +bool int_icomp(Int op1, int64_t op2, BinaryOp op); +bool int_comp(Int op1, Int op2, BinaryOp op); +uint64_t int_to_u64(Int op); +int64_t int_to_i64(Int op); +bool int_is_zero(Int op); +bool int_fits(Int op1, TypeKind kind); +Int int_conv(Int op, TypeKind to_type); +Int int_div(Int op1, Int op2); +Int int_mul(Int op1, Int op2); +Int int_sub(Int op1, Int op2); +Int int_sub64(Int op1, uint64_t op2); +Int int_add(Int op1, Int op2); +Int int_add64(Int op1, uint64_t op2); +Int int_rem(Int op1, Int op2); +Int int_and(Int op1, Int op2); +Int int_or(Int op1, Int op2); +Int int_xor(Int op1, Int op2); +Int int_neg(Int op); +Int int_not(Int op); +bool int_is_neg(Int op); +Int int_shr64(Int op, uint64_t); +Int int_shl64(Int op, uint64_t); +Real int_to_real(Int op); +Int int_from_real(Real d, TypeKind type); +char *int_to_str(Int i, int radix); +bool i128_can_convert_from_double(double x); +bool i128_can_convert_from_double_signed(double x); +Int128 i128_from_double(double x); +Int128 i128_from_double_signed(double x); +Int128 i128_extend(Int128 op, TypeKind type); +Int128 i128_add(Int128 op1, Int128 op2); +Int128 i128_add64(Int128 op1, uint64_t op2); +Int128 i128_add_swrap64(Int128 op1, int64_t op2, bool *wrapped); +Int128 i128_add_uwrap64(Int128 op1, uint64_t op2, bool *wrapped); +Int128 i128_sub(Int128 op1, Int128 op2); +Int128 i128_sub64(Int128 op1, uint64_t op2); +Int128 i128_and(Int128 op1, Int128 op2); +Int128 i128_or(Int128 op1, Int128 op2); +Int128 i128_xor(Int128 op1, Int128 op2); +Int128 i128_neg(Int128 op1); +Int128 i128_not(Int128 op1); +Int128 i128_mult(Int128 op1, Int128 op2); +Int128 i128_mult64(Int128 op1, uint64_t op2); +Int128 i128_from_str(const char *str); +char *i128_to_string(Int128 op, uint64_t base, bool is_signed); +bool i128_is_neg(Int128 op); +CmpRes i128_ucomp(Int128 op1, Int128 op2); +CmpRes i128_scomp(Int128 op1, Int128 op2); +CmpRes i128_comp(Int128 op1, Int128 op2, Type *type); +Int128 i128_shl64(Int128 op1, uint64_t amount); +Int128 i128_shl(Int128 op1, Int128 op); +Int128 i128_ashr64(Int128 op1, uint64_t amount); +Int128 i128_ashr(Int128 op1, Int128 op2); +Int128 i128_lshr64(Int128 op1, uint64_t amount); +Int128 i128_lshr(Int128 op1, Int128 op2); +Int128 i128_udiv(Int128 op1, Int128 op2); +Int128 i128_sdiv(Int128 op1, Int128 op2); +Int128 i128_urem(Int128 op1, Int128 op2); +Int128 i128_srem(Int128 op1, Int128 op2); +void i128_udivrem(Int128 op1, Int128 op2, Int128 *div, Int128 *rem); +Real i128_to_float(Int128 op); +Real i128_to_float_signed(Int128 op); +bool i128_is_zero(Int128 op); +uint32_t i128_clz(const Int128 *op); +uint32_t i128_ctz(const Int128 *op); +int i128_lsb(const Int128 *op); +int i128_msb(const Int128 *op); +Int128 i128_from_float_signed(Real d); +Int128 i128_from_float_unsigned(Real d); +Int128 i128_from_signed(int64_t i); +Int128 i128_from_unsigned(uint64_t i); +bool i128_get_bit(const Int128 *op, int bit); static inline bool type_may_negate(Type *type) { RETRY: switch (type->type_kind) { - case ALL_FLOATS: - case ALL_SIGNED_INTS: case TYPE_VECTOR: - case TYPE_IXX: + type = type->vector.base; + goto RETRY; + case ALL_FLOATS: + case ALL_INTS: return true; case TYPE_DISTINCT: type = type->decl->distinct_decl.base_type; @@ -1689,12 +1741,16 @@ static inline bool type_may_negate(Type *type) case TYPE_TYPEDEF: type = type->canonical; goto RETRY; + case TYPE_FAILABLE: + type = type->failable; + goto RETRY; default: return false; } } +bool cast_implicit_ignore_failable(Expr *expr, Type *to_type); bool cast_implicit(Expr *expr, Type *to_type); bool cast(Expr *expr, Type *to_type); @@ -1704,8 +1760,6 @@ bool cast_implicit_bit_width(Expr *expr, Type *to_type); CastKind cast_to_bool_kind(Type *type); -bool cast_implicitly_to_runtime(Expr *expr); - const char *llvm_codegen(void *context); void *llvm_gen(Module *module); void llvm_codegen_setup(); @@ -1792,14 +1846,12 @@ static inline void expr_replace(Expr *expr, Expr *replacement) *expr = *replacement; expr->span = loc; } -void expr_copy_types(Expr *to, Expr *from); -void expr_copy_properties(Expr *to, Expr *from); + void expr_const_set_int(ExprConst *expr, uint64_t v, TypeKind kind); void expr_const_set_float(ExprConst *expr, Real d, TypeKind kind); void expr_const_set_bool(ExprConst *expr, bool b); void expr_const_set_null(ExprConst *expr); -bool expr_const_int_overflowed(const ExprConst *expr); bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op); bool expr_const_will_overflow(const ExprConst *expr, TypeKind kind); ByteSize expr_const_list_size(const ConstInitializer *list); @@ -1820,12 +1872,6 @@ static inline bool expr_is_init_list(Expr *expr) ExprKind kind = expr->expr_kind; return kind == EXPR_DESIGNATED_INITIALIZER_LIST || kind == EXPR_INITIALIZER_LIST; } -static inline void expr_set_type(Expr *expr, Type *type) -{ - assert(type); - expr->type = type; - expr->original_type = type; -} #pragma mark --- Lexer functions @@ -1883,15 +1929,20 @@ bool sema_unwrap_var(Context *context, Decl *decl); bool sema_rewrap_var(Context *context, Decl *decl); bool sema_erase_var(Context *context, Decl *decl); bool sema_erase_unwrapped(Context *context, Decl *decl); -bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, bool may_be_failable); +bool sema_analyse_cond_expr(Context *context, Expr *expr); +bool sema_analyse_assigned_expr(Context *context, Type *to, Expr *expr, bool may_be_failable); + +bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr); ArrayIndex sema_get_initializer_const_array_size(Context *context, Expr *initializer, bool *may_be_array, bool *is_const_size); -bool sema_analyse_expr(Context *context, Type *to, Expr *expr); +bool sema_analyse_expr(Context *context, Expr *expr); +bool sema_analyse_inferred_expr(Context *context, Type *to, Expr *expr); bool sema_analyse_decl(Context *context, Decl *decl); bool sema_analyse_var_decl(Context *context, Decl *decl); bool sema_analyse_ct_assert_stmt(Context *context, Ast *statement); bool sema_analyse_statement(Context *context, Ast *statement); -bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *left_type, Expr *right, ExprFailableStatus lhs_is_failable); -bool sema_expr_analyse_general_call(Context *context, Type *to, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro); +bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *left_type, Expr *right, bool is_unwrapped_var); + +bool sema_expr_analyse_general_call(Context *context, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro, bool failable); Decl *sema_resolve_symbol_in_current_dynamic_scope(Context *context, const char *symbol); Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path *path); Decl *sema_resolve_method(Context *context, Decl *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref); @@ -1980,9 +2031,10 @@ bool type_is_valid_for_vector(Type *type); Type *type_get_array(Type *arr_type, ByteSize len); Type *type_get_indexed_type(Type *type); Type *type_get_ptr(Type *ptr_type); +Type *type_get_ptr_recurse(Type *ptr_type); Type *type_get_subarray(Type *arr_type); Type *type_get_inferred_array(Type *arr_type); - +Type *type_get_failable(Type *failable_type); Type *type_get_vector(Type *vector_type, unsigned len); Type *type_get_vector_bool(Type *original_type); Type *type_cint(void); @@ -1990,9 +2042,7 @@ Type *type_cuint(void); Type *type_int_signed_by_bitsize(unsigned bitsize); Type *type_int_unsigned_by_bitsize(unsigned bytesize); bool type_is_abi_aggregate(Type *type); -static inline bool type_is_any_integer(Type *type); static inline bool type_is_builtin(TypeKind kind); -static inline bool type_is_ct(Type *type); bool type_is_empty_record(Type *type, bool allow_array); bool type_is_empty_field(Type *type, bool allow_array); static inline bool type_is_float(Type *type); @@ -2002,7 +2052,7 @@ Type *type_find_function_type(FunctionSignature *signature); static inline bool type_is_integer(Type *type); static inline bool type_is_integer_unsigned(Type *type); static inline bool type_is_integer_signed(Type *type); -static inline bool type_is_integer_kind(Type *type); +static inline bool type_is_integer_or_bool_kind(Type *type); static inline bool type_is_numeric(Type *type); static inline bool type_underlying_is_numeric(Type *type); static inline bool type_is_pointer(Type *type); @@ -2024,7 +2074,6 @@ static inline Type *type_reduced_from_expr(Expr *expr); ByteSize type_size(Type *type); const char *type_to_error_string(Type *type); const char *type_quoted_error_string(Type *type); -bool type_may_convert_to_boolean(Type *type); static inline TypeInfo *type_info_new(TypeInfoKind kind, SourceSpan span); static inline TypeInfo *type_info_new_base(Type *type, SourceSpan span); @@ -2043,6 +2092,10 @@ static inline Type *type_reduced_from_expr(Expr *expr) return type_lowering(expr->type); } +static inline Type *type_no_fail(Type *type) +{ + return type && type->type_kind == TYPE_FAILABLE ? type->failable : type; +} static inline bool type_is_pointer_sized_or_more(Type *type) { @@ -2054,35 +2107,48 @@ static inline bool type_is_pointer_sized(Type *type) return type_is_integer(type) && type_size(type) == type_size(type_iptr); } +#define DECL_TYPE_KIND_REAL(k_, t_) \ + TypeKind k_ = (t_)->type_kind; \ + if (k_ == TYPE_TYPEDEF) k_ = (t_)->canonical->type_kind; + +#define IS_FAILABLE(element_) ((element_)->type->type_kind == TYPE_FAILABLE) +#define TYPE_IS_FAILABLE(type_) (type_->type_kind == TYPE_FAILABLE) + +static inline Type *type_with_added_failability(Expr *expr, bool add_failable) +{ + Type *type = expr->type; + if (!add_failable || type->type_kind == TYPE_FAILABLE) return type; + return type_get_failable(type); +} + +static inline Type *type_get_opt_fail(Type *type, bool add_failable) +{ + if (!add_failable || type->type_kind == TYPE_FAILABLE) return type; + return type_get_failable(type); +} static inline bool type_is_integer(Type *type) { - assert(type == type->canonical); - return type->type_kind >= TYPE_I8 && type->type_kind < TYPE_IXX; -} - -static inline bool type_is_any_integer(Type *type) -{ - assert(type == type->canonical); - return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX; + DECL_TYPE_KIND_REAL(kind, type); + return kind >= TYPE_INTEGER_FIRST && type->type_kind <= TYPE_INTEGER_LAST; } static inline bool type_is_integer_signed(Type *type) { - assert(type == type->canonical); - return type->type_kind >= TYPE_I8 && type->type_kind < TYPE_U8; + DECL_TYPE_KIND_REAL(kind, type); + return kind >= TYPE_INT_FIRST && type->type_kind <= TYPE_INT_LAST; } -static inline bool type_is_integer_kind(Type *type) +static inline bool type_is_integer_or_bool_kind(Type *type) { - assert(type == type->canonical); - return type->type_kind >= TYPE_BOOL && type->type_kind < TYPE_IXX; + DECL_TYPE_KIND_REAL(kind, type); + return kind >= TYPE_BOOL && kind <= TYPE_U128; } static inline bool type_is_integer_unsigned(Type *type) { - assert(type == type->canonical); - return type->type_kind >= TYPE_U8 && type->type_kind < TYPE_IXX; + DECL_TYPE_KIND_REAL(kind, type); + return kind >= TYPE_UINT_FIRST && type->type_kind <= TYPE_UINT_LAST; } static inline bool type_info_poison(TypeInfo *type) @@ -2092,26 +2158,11 @@ static inline bool type_info_poison(TypeInfo *type) return false; } -static inline bool type_is_ct(Type *type) -{ - RETRY: - switch (type->type_kind) - { - case TYPE_FXX: - case TYPE_IXX: - return true; - case TYPE_TYPEDEF: - type = type->canonical; - goto RETRY; - default: - return false; - } -} static inline bool type_is_pointer(Type *type) { - type = type->canonical; - return type->type_kind == TYPE_POINTER; + DECL_TYPE_KIND_REAL(kind, type); + return kind == TYPE_POINTER; } static inline uint64_t aligned_offset(uint64_t offset, uint64_t alignment) @@ -2121,14 +2172,14 @@ static inline uint64_t aligned_offset(uint64_t offset, uint64_t alignment) static inline bool type_is_substruct(Type *type) { - assert(type == type->canonical); - return type->type_kind == TYPE_STRUCT && type->decl->is_substruct; + DECL_TYPE_KIND_REAL(kind, type); + return kind == TYPE_STRUCT && type->decl->is_substruct; } static inline bool type_is_float(Type *type) { - assert(type == type->canonical); - return type->type_kind >= TYPE_F16 && type->type_kind <= TYPE_FXX; + DECL_TYPE_KIND_REAL(kind, type); + return kind >= TYPE_FLOAT_FIRST && kind <= TYPE_FLOAT_LAST; } static inline TypeInfo *type_info_new(TypeInfoKind kind, SourceSpan span) @@ -2171,7 +2222,6 @@ static inline bool type_convert_will_trunc(Type *destination, Type *source) UnaryOp unaryop_from_token(TokenType type); -PostUnaryOp post_unaryop_from_token(TokenType type); BinaryOp binaryop_from_token(TokenType type); BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op); TokenType binaryop_to_token(BinaryOp type); @@ -2233,6 +2283,12 @@ static inline Type *type_flatten(Type *type) type = type->decl->enums.type_info->type; continue; } + if (type->type_kind == TYPE_FAILABLE) + { + type = type->failable; + if (!type) type = type_void; + continue; + } return type; } } @@ -2245,19 +2301,19 @@ static Type *type_vector_type(Type *type) static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_TYPEID; } static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind < TYPE_U8; } -static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind < TYPE_IXX; } -static inline bool type_kind_is_any_integer(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_IXX; } +static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind <= TYPE_U128; } +static inline bool type_kind_is_any_integer(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_U128; } static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind < TYPE_U8; } -static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind < TYPE_IXX; } +static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U128; } 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; } - +int type_kind_bitsize(TypeKind kind); bool type_is_scalar(Type *type); static inline bool type_is_numeric(Type *type) { TypeKind kind = type->type_kind; - return (kind >= TYPE_I8 && kind <= TYPE_FXX) || kind == TYPE_VECTOR; + return (kind >= TYPE_I8 && kind <= TYPE_FLOAT_LAST) || kind == TYPE_VECTOR; } static inline bool type_underlying_is_numeric(Type *type) @@ -2305,6 +2361,10 @@ static inline Type *type_lowering(Type *type) static inline Decl *decl_raw(Decl *decl) { + while (decl->decl_kind == DECL_DEFINE && decl->define_decl.define_kind == DEFINE_IDENT_ALIAS) + { + decl = decl->define_decl.alias; + } if (decl->decl_kind != DECL_VAR || decl->var.kind != VARDECL_UNWRAPPED) return decl; decl = decl->var.alias; assert(decl->decl_kind != DECL_VAR || decl->var.kind != VARDECL_UNWRAPPED); @@ -2346,7 +2406,7 @@ static inline DeclKind decl_from_token(TokenType type) static inline bool type_is_promotable_integer(Type *type) { // If we support other architectures, update this. - return type_is_integer_kind(type) && type->builtin.bitsize < platform_target.width_c_int; + return type_is_integer_or_bool_kind(type) && type->builtin.bitsize < platform_target.width_c_int; } static inline bool type_is_promotable_float(Type *type) @@ -2394,3 +2454,15 @@ void platform_linker(const char *output_file, const char **files, unsigned file_ #define ASSIGN_TYPE_ELSE(_assign, _type_stmt, _res) TypeInfo* TEMP(_type) = (_type_stmt); if (!type_info_ok(TEMP(_type))) return _res; _assign = TEMP(_type) #define ASSIGN_DECL_ELSE(_assign, _decl_stmt, _res) Decl* TEMP(_decl) = (_decl_stmt); if (!decl_ok(TEMP(_decl))) return _res; _assign = TEMP(_decl) +static inline Type *abi_rtype(FunctionSignature *signature) +{ + Type *type = signature->rtype->type; + if (type->type_kind == TYPE_FAILABLE) return type->failable; + return type; +} + +static inline Type *abi_returntype(FunctionSignature *signature) +{ + Type *type = signature->rtype->type; + return type->type_kind == TYPE_FAILABLE ? type_anyerr : type; +} diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 716bedece..44bb95750 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -162,10 +162,6 @@ Expr *copy_expr(Expr *source_expr) MACRO_COPY_EXPR(expr->guard_expr.inner); return expr; case EXPR_CONST: - if (expr->const_expr.const_kind == CONST_INTEGER && expr->const_expr.i.digit_count > 1) - { - bigint_init_bigint(&expr->const_expr.i, &source_expr->const_expr.i); - } return expr; case EXPR_BINARY: MACRO_COPY_EXPR(expr->binary_expr.left); @@ -177,10 +173,8 @@ Expr *copy_expr(Expr *source_expr) MACRO_COPY_EXPR(expr->ternary_expr.else_expr); return expr; case EXPR_UNARY: - MACRO_COPY_EXPR(expr->unary_expr.expr); - return expr; case EXPR_POST_UNARY: - MACRO_COPY_EXPR(expr->post_expr.expr); + MACRO_COPY_EXPR(expr->unary_expr.expr); return expr; case EXPR_TYPEID: MACRO_COPY_TYPE(expr->typeid_expr); @@ -301,7 +295,7 @@ Ast *copy_ast(Ast *source) case AST_DEFAULT_STMT: MACRO_COPY_AST(ast->case_stmt.body); return ast; - case AST_DEFINE_STMT: + case AST_VAR_STMT: ast->var_stmt = copy_decl(ast->var_stmt); return ast; case AST_DEFER_STMT: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 871fe002b..c44ef9f66 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -50,7 +50,7 @@ typedef enum AST_CASE_STMT, AST_COMPOUND_STMT, AST_CONTINUE_STMT, - AST_DEFINE_STMT, + AST_VAR_STMT, AST_CT_ASSERT, AST_CT_COMPOUND_STMT, AST_CT_IF_STMT, @@ -245,12 +245,6 @@ typedef enum DESIGNATOR_RANGE } DesignatorType; -typedef enum -{ - FAILABLE_NO, - FAILABLE_YES, - FAILABLE_UNWRAPPED -} ExprFailableStatus; typedef enum @@ -523,21 +517,27 @@ typedef enum TYPE_VOID, TYPE_BOOL, TYPE_I8, + TYPE_INTEGER_FIRST = TYPE_I8, + TYPE_INT_FIRST = TYPE_I8, TYPE_I16, TYPE_I32, TYPE_I64, TYPE_I128, + TYPE_INT_LAST = TYPE_I128, TYPE_U8, + TYPE_UINT_FIRST = TYPE_U8, TYPE_U16, TYPE_U32, TYPE_U64, TYPE_U128, - TYPE_IXX, + TYPE_UINT_LAST = TYPE_U128, + TYPE_INTEGER_LAST = TYPE_U128, TYPE_F16, + TYPE_FLOAT_FIRST = TYPE_F16, TYPE_F32, TYPE_F64, TYPE_F128, - TYPE_FXX, + TYPE_FLOAT_LAST = TYPE_F128, TYPE_TYPEID, TYPE_POINTER, TYPE_ENUM, @@ -554,6 +554,7 @@ typedef enum TYPE_SUBARRAY, TYPE_INFERRED_ARRAY, TYPE_UNTYPED_LIST, + TYPE_FAILABLE, TYPE_TYPEINFO, TYPE_VECTOR, TYPE_VIRTUAL, @@ -563,11 +564,10 @@ typedef enum #define CT_TYPES TYPE_TYPEINFO: case TYPE_INFERRED_ARRAY: case TYPE_UNTYPED_LIST: case TYPE_POISONED #define ALL_INTS TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: case TYPE_I128: \ -case TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64: case TYPE_U128: case TYPE_IXX +case TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64: case TYPE_U128 #define ALL_SIGNED_INTS TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: case TYPE_I128 #define ALL_UNSIGNED_INTS TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64: case TYPE_U128 -#define ALL_REAL_FLOATS TYPE_F16: case TYPE_F32: case TYPE_F64: case TYPE_F128 -#define ALL_FLOATS ALL_REAL_FLOATS: case TYPE_FXX +#define ALL_FLOATS TYPE_F16: case TYPE_F32: case TYPE_F64: case TYPE_F128 #define TYPE_KINDS (TYPE_LAST + 1) @@ -585,12 +585,6 @@ typedef enum UNARYOP_LAST = UNARYOP_DEC } UnaryOp; -typedef enum -{ - POSTUNARYOP_INC, - POSTUNARYOP_DEC, -} PostUnaryOp; - typedef enum { VARDECL_CONST = 0, diff --git a/src/compiler/float.c b/src/compiler/float.c new file mode 100644 index 000000000..ce8f3518c --- /dev/null +++ b/src/compiler/float.c @@ -0,0 +1,266 @@ +#include "compiler_internal.h" + +#include +#include + +static int precision_bits(TypeKind kind) +{ + switch (kind) + { + case TYPE_F16: + return 11; + /*case TYPE_BF16: + return 8;*/ + case TYPE_F32: + return 24; + case TYPE_F64: + return 53; + case TYPE_F128: + return 113; + default: + UNREACHABLE + } +} + +static int max_exponent(TypeKind kind) +{ + switch (kind) + { + case TYPE_F16: + return 15; + /*case TYPE_BF16: + return 127;*/ + case TYPE_F32: + return 127; + case TYPE_F64: + return 1023; + case TYPE_F128: + return 16383; + default: + UNREACHABLE + } +} + +static int min_exponent(TypeKind kind) +{ + switch (kind) + { + case TYPE_F16: + return -14; + /*case TYPE_BF16: + return -126;*/ + case TYPE_F32: + return -126; + case TYPE_F64: + return -1022; + case TYPE_F128: + return -16382; + default: + UNREACHABLE + } +} + + +Float float_add(Float op1, Float op2) +{ + assert(op1.type == op2.type); + return (Float){ op1.f + op2.f, op1.type }; +} + +Float float_sub(Float op1, Float op2) +{ + assert(op1.type == op2.type); + return (Float){ op1.f - op2.f, op1.type }; +} + +Float float_mul(Float op1, Float op2) +{ + assert(op1.type == op2.type); + return (Float){ op1.f * op2.f, op1.type }; +} + +Float float_div(Float op1, Float op2) +{ + assert(op1.type == op2.type); + return (Float){ op1.f / op2.f, op1.type }; +} + +Float float_neg(Float op) +{ + op.f = -op.f; + return op; +} + +static char *err_invalid_float_width = "The float width is not valid, it must be one of 16, 32, 64 and 128."; +static char *err_float_out_of_range = "The float value is out of range."; +static char *err_float_format_invalid = "The float format is invalid."; + +Float float_from_string(const char *string, char **error) +{ + const char *index = string; + char c; + scratch_buffer_clear(); + while ((c = *(index++)) && (c == '_' || (c >= '0' && c <= '9'))) + { + if (c == '_') continue; + scratch_buffer_append_char(c); + } + if (c == '.') + { + scratch_buffer_append_char(c); + while ((c = *(index++)) && (c == '_' || (c >= '0' && c <= '9'))) + { + if (c == '_') continue; + scratch_buffer_append_char(c); + } + } + if (c == 'e' || c == 'E') + { + scratch_buffer_append_char(c); + if (*index == '-') + { + scratch_buffer_append_char('-'); + index++; + } + else if (*index == '+') index++; + while ((c = *(index++)) && (c >= '0' && c <= '9')) + { + scratch_buffer_append_char(c); + } + } + TypeKind kind = TYPE_F64; + if (c == 'f') + { + int i = 0; + while ((c = *(index++)) && (c >= '0' && c <= '9')) + { + if (i > 100) + { + if (error) *error = err_invalid_float_width; + return (Float){ .type = TYPE_POISONED }; + } + i = i * 10 + c - '0'; + } + switch (i) + { + case 32: + kind = TYPE_F32; + break; + case 16: + kind = TYPE_F16; + break; + case 64: + case 0: + kind = TYPE_F64; + break; + case 128: + kind = TYPE_F128; + break; + default: + if (error) *error = err_invalid_float_width; + return (Float){ .type = TYPE_POISONED }; + } + } + + const char *str = scratch_buffer_to_string(); + char *end = NULL; + errno = 0; + double d = strtod(str, &end); + if (d == HUGE_VAL && errno == ERANGE) + { + if (error) *error = err_float_out_of_range; + return (Float){ .type = TYPE_POISONED }; + } + char *expected_end = global_context.scratch_buffer + global_context.scratch_buffer_len; + if (d == 0 && end != expected_end) + { + if (error) *error = err_float_format_invalid; + return (Float){ .type = TYPE_POISONED }; + } + return (Float){ d, kind }; +} + +Float float_from_hex(const char *string, char **error) +{ + const char *index = string + 2; + char c; + scratch_buffer_clear(); + scratch_buffer_append("0x"); + while ((c = *(index++)) && (c == '_' || is_hex(c))) + { + if (c == '_') continue; + scratch_buffer_append_char(c); + } + if (c == '.') + { + scratch_buffer_append_char(c); + while ((c = *(index++)) && (c == '_' || is_hex(c))) + { + if (c == '_') continue; + scratch_buffer_append_char(c); + } + } + if (c == 'p' || c == 'P') + { + scratch_buffer_append_char(c); + if (*index == '-') + { + scratch_buffer_append_char('-'); + index++; + } + else if (*index == '+') index++; + while ((c = *(index++)) && (c >= '0' && c <= '9')) + { + scratch_buffer_append_char(c); + } + } + TypeKind kind = TYPE_F64; + if (c == 'f') + { + int i = 0; + while ((c = *(index++)) && (c >= '0' && c <= '9')) + { + if (i > 100) + { + if (error) *error = err_invalid_float_width; + return (Float){ .type = TYPE_POISONED }; + } + i = i * 10 + c - '0'; + } + switch (i) + { + case 32: + kind = TYPE_F32; + break; + case 16: + kind = TYPE_F16; + break; + case 0: + case 64: + kind = TYPE_F64; + break; + case 128: + kind = TYPE_F128; + break; + default: + if (error) *error = err_invalid_float_width; + return (Float){ .type = TYPE_POISONED }; + } + } + + const char *str = scratch_buffer_to_string(); + char *end = NULL; + errno = 0; + double d = strtod(str, &end); + if (d == HUGE_VAL && errno == ERANGE) + { + if (error) *error = err_float_out_of_range; + return (Float){ .type = TYPE_POISONED }; + } + if (d == 0 && end != global_context.scratch_buffer + global_context.scratch_buffer_len) + { + if (error) *error = err_float_format_invalid; + return (Float){ .type = TYPE_POISONED }; + } + return (Float){ d, kind }; +} \ No newline at end of file diff --git a/src/compiler/headers.c b/src/compiler/headers.c index 3fac9df12..d75d63945 100644 --- a/src/compiler/headers.c +++ b/src/compiler/headers.c @@ -34,6 +34,9 @@ static void header_print_type(FILE *file, Type *type) UNREACHABLE case TYPE_BITSTRUCT: TODO + case TYPE_FAILABLE: + // If this is reachable then we are not doing the proper lowering. + UNREACHABLE case TYPE_VOID: OUTPUT("void"); return; @@ -50,7 +53,6 @@ static void header_print_type(FILE *file, Type *type) OUTPUT("int32_t"); return; case TYPE_I64: - case TYPE_IXX: OUTPUT("int64_t"); return; case TYPE_I128: @@ -78,7 +80,6 @@ static void header_print_type(FILE *file, Type *type) OUTPUT("float"); return; case TYPE_F64: - case TYPE_FXX: OUTPUT("double"); return; case TYPE_F128: diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index a67e93b37..abda788e0 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -352,6 +352,36 @@ static inline bool scan_ident(Lexer *lexer, TokenType normal, TokenType const_to #pragma mark --- Number scanning +static bool scan_number_suffix(Lexer *lexer, bool *is_float) +{ + if (!is_alphanum_(peek(lexer))) return true; + switch (peek(lexer)) + { + case 'u': + case 'U': + case 'I': + case 'i': + if (*is_float) + { + return add_error_token(lexer, "Integer suffix '%x' is not valid for a floating point literal.", peek(lexer)); + } + next(lexer); + while (is_number(peek(lexer))) next(lexer); + break; + case 'f': + *is_float = true; + next(lexer); + while (is_number(peek(lexer))) next(lexer); + break; + default: + break; + } + if (is_alphanum_(peek(lexer))) + { + return add_error_token(lexer, "This doesn't seem to be a valid literal."); + } + return true; +} /** * Parsing octals. Here we depart from the (error prone) C style octals with initial zero e.g. 0231 * Instead we only support 0o prefix like 0o231. Note that lexing here doesn't actually parse the @@ -362,6 +392,12 @@ static bool scan_oct(Lexer *lexer) char o = next(lexer); // Skip the o if (!is_oct(next(lexer))) return add_error_token(lexer, "An expression starting with '0%c' would expect to be followed by octal numbers (0-7).", o); while (is_oct_or_(peek(lexer))) next(lexer); + bool is_float = false; + if (!scan_number_suffix(lexer, &is_float)) return false; + if (is_float) + { + return add_error_token(lexer, "Octal literals cannot have a floating point suffix."); + } return add_token(lexer, TOKEN_INTEGER, lexer->lexing_start); } @@ -377,6 +413,12 @@ static bool scan_binary(Lexer *lexer) "did you try to write a hex value but forgot the '0x'?"); } while (is_binary_or_(peek(lexer))) next(lexer); + bool is_float = false; + if (!scan_number_suffix(lexer, &is_float)) return false; + if (is_float) + { + return add_error_token(lexer, "Binary literals cannot have a floating point suffix."); + } return add_token(lexer, TOKEN_INTEGER, lexer->lexing_start); } @@ -434,6 +476,8 @@ static inline bool scan_hex(Lexer *lexer) if (!scan_exponent(lexer)) return false; } if (prev(lexer) == '_') return add_error_token(lexer, "The number ended with '_', but that character needs to be between, not after, digits."); + // TODO this does not currently work. + if (!scan_number_suffix(lexer, &is_float)) return false; if (is_float) { // IMPROVE @@ -441,26 +485,17 @@ static inline bool scan_hex(Lexer *lexer) // this is not ideal, we should try to use f128 if possible for example. // Possibly we should move to a BigDecimal implementation or at least a soft float 256 // implementation for the constants. - char *end = NULL; - errno = 0; -#if LONG_DOUBLE - Real fval = strtold(lexer->lexing_start, &end); -#else - Real fval = strtod(lexer->lexing_start, &end); -#endif - if (errno == ERANGE) + char *err = NULL; + Float f = float_from_hex(lexer->lexing_start, &err); + if (f.type == TYPE_POISONED) { - return add_error_token(lexer, "The float value is out of range."); - } - if (end != lexer->current) - { - return add_error_token(lexer, "Invalid float value."); + return add_error_token(lexer, err); } add_generic_token(lexer, TOKEN_REAL); - lexer->latest_token_data->value = fval; + lexer->latest_token_data->value = f; return true; } - return add_token(lexer, is_float ? TOKEN_REAL : TOKEN_INTEGER, lexer->lexing_start); + return add_token(lexer, TOKEN_INTEGER, lexer->lexing_start); } /** @@ -502,7 +537,8 @@ static inline bool scan_dec(Lexer *lexer) } if (prev(lexer) == '_') return add_error_token(lexer, "The number ended with '_', but that character needs to be between, not after, digits."); - + // TODO this does not currently work. + if (!scan_number_suffix(lexer, &is_float)) return false; if (is_float) { // IMPROVE @@ -510,18 +546,14 @@ static inline bool scan_dec(Lexer *lexer) // this is not ideal, we should try to use f128 if possible for example. // Possibly we should move to a BigDecimal implementation or at least a soft float 256 // implementation for the constants. - char *end = NULL; -#if LONG_DOUBLE - Real fval = strtold(lexer->lexing_start, &end); -#else - Real fval = strtod(lexer->lexing_start, &end); -#endif - if (end != lexer->current) + char *err = NULL; + Float f = float_from_string(lexer->lexing_start, &err); + if (f.type == TYPE_POISONED) { - return add_error_token(lexer, "Invalid float value."); + return add_error_token(lexer, err); } add_generic_token(lexer, TOKEN_REAL); - lexer->latest_token_data->value = fval; + lexer->latest_token_data->value = f; return true; } return add_token(lexer, TOKEN_INTEGER, lexer->lexing_start); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 6acf1443c..198d87bd4 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -262,7 +262,7 @@ static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl { decl->backend_ref = LLVMAddGlobal(c->module, llvm_get_type(c, decl->type), "tempglobal"); } - if (decl->var.failable) + if (IS_FAILABLE(decl)) { scratch_buffer_clear(); scratch_buffer_append(decl->external_name); @@ -312,7 +312,8 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) LLVMValueRef init_value; - ByteSize alignment = type_alloca_alignment(decl->type); + Type *var_type = type_lowering(decl->type); + ByteSize alignment = type_alloca_alignment(var_type); Expr *init_expr = decl->var.init_expr; if (init_expr) @@ -341,7 +342,7 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) } else { - init_value = LLVMConstNull(llvm_get_type(c, decl->type)); + init_value = LLVMConstNull(llvm_get_type(c, var_type)); } @@ -359,7 +360,7 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) { llvm_set_alignment(failable_ref, type_alloca_alignment(type_anyerr)); } - if (decl->var.init_expr && decl->var.init_expr->failable) + if (init_expr && IS_FAILABLE(init_expr) && init_expr->expr_kind == EXPR_FAILABLE) { UNREACHABLE } @@ -395,9 +396,9 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) break; } - if (init_value && LLVMTypeOf(init_value) != llvm_get_type(c, decl->type)) + if (init_value && LLVMTypeOf(init_value) != llvm_get_type(c, var_type)) { - decl->backend_ref = LLVMConstBitCast(decl->backend_ref, llvm_get_ptr_type(c, decl->type)); + decl->backend_ref = LLVMConstBitCast(decl->backend_ref, llvm_get_ptr_type(c, var_type)); } LLVMReplaceAllUsesWith(old, decl->backend_ref); LLVMDeleteGlobal(old); @@ -467,12 +468,9 @@ LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *nam void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl) { - if (decl->type == type_void) - { - return; - } - LLVMTypeRef type = llvm_get_type(c, decl->type); - decl->backend_ref = llvm_emit_alloca(c, type, decl->alignment, decl->name ? decl->name : "anon"); + Type *type = type_lowering(decl->type); + if (type == type_void) return; + decl->backend_ref = llvm_emit_alloca(c, llvm_get_type(c, type), decl->alignment, decl->name ? decl->name : "anon"); } void llvm_emit_local_var_alloca(GenContext *c, Decl *decl) @@ -789,7 +787,7 @@ void llvm_value_set_decl_address(BEValue *value, Decl *decl) llvm_value_set_address(value, decl_ref(decl), decl->type); value->alignment = decl->alignment; - if (decl->decl_kind == DECL_VAR && decl->var.failable) + if (decl->decl_kind == DECL_VAR && IS_FAILABLE(decl)) { value->kind = BE_ADDRESS_FAILABLE; value->failable = decl->var.failable_ref; diff --git a/src/compiler/llvm_codegen_c_abi.c b/src/compiler/llvm_codegen_c_abi.c index b3f918766..f0959b39b 100644 --- a/src/compiler/llvm_codegen_c_abi.c +++ b/src/compiler/llvm_codegen_c_abi.c @@ -237,3 +237,4 @@ ABIArgInfo *c_abi_classify_argument_type_default(Type *type) // No, then do a direct pass. return abi_arg_new_direct(); } + diff --git a/src/compiler/llvm_codegen_c_abi_aarch64.c b/src/compiler/llvm_codegen_c_abi_aarch64.c index 883a85a1f..627793b00 100644 --- a/src/compiler/llvm_codegen_c_abi_aarch64.c +++ b/src/compiler/llvm_codegen_c_abi_aarch64.c @@ -140,17 +140,18 @@ ABIArgInfo *aarch64_classify_return_type(Type *type, bool variadic) void c_abi_func_create_aarch64(FunctionSignature *signature) { - if (signature->failable) + Type *rtype = abi_rtype(signature); + if (IS_FAILABLE(signature->rtype)) { - signature->failable_abi_info = aarch64_classify_return_type(signature->rtype->type, signature->variadic == VARIADIC_RAW); - if (signature->rtype->type->type_kind != TYPE_VOID) + signature->failable_abi_info = aarch64_classify_return_type(rtype, signature->variadic == VARIADIC_RAW); + if (rtype->type_kind != TYPE_VOID) { - signature->ret_abi_info = aarch64_classify_argument_type(type_get_ptr(type_lowering(signature->rtype->type))); + signature->ret_abi_info = aarch64_classify_argument_type(type_get_ptr(type_lowering(rtype))); } } else { - signature->ret_abi_info = aarch64_classify_return_type(signature->rtype->type, signature->variadic == VARIADIC_RAW); + signature->ret_abi_info = aarch64_classify_return_type(rtype, signature->variadic == VARIADIC_RAW); } Decl **params = signature->params; VECEACH(params, i) diff --git a/src/compiler/llvm_codegen_c_abi_riscv.c b/src/compiler/llvm_codegen_c_abi_riscv.c index 4e80128f8..acff67454 100644 --- a/src/compiler/llvm_codegen_c_abi_riscv.c +++ b/src/compiler/llvm_codegen_c_abi_riscv.c @@ -259,10 +259,12 @@ void c_abi_func_create_riscv(FunctionSignature *signature) unsigned gpr = 8; unsigned fpr = 8; - Type *return_type = signature->failable ? type_anyerr : signature->rtype->type; + bool failable = IS_FAILABLE(signature->rtype); + Type *rtype = abi_rtype(signature); + Type *return_type = abi_returntype(signature); return_type = type_lowering(return_type); ABIArgInfo *return_abi = riscv_classify_return(return_type); - if (signature->failable) + if (failable) { signature->failable_abi_info = return_abi; } @@ -291,9 +293,9 @@ void c_abi_func_create_riscv(FunctionSignature *signature) unsigned arg_fprs_left = platform_target.riscv.flen ? fpr : 0; // If we have a failable, then the return type is a parameter. - if (signature->failable && signature->rtype->type->type_kind != TYPE_VOID) + if (IS_FAILABLE(signature->rtype) && rtype != type_void) { - signature->ret_abi_info = riscv_classify_argument_type(type_get_ptr(type_lowering(signature->rtype->type)), + signature->ret_abi_info = riscv_classify_argument_type(type_get_ptr(type_lowering(rtype)), true, &arg_gprs_left, &arg_fprs_left); } diff --git a/src/compiler/llvm_codegen_c_abi_wasm.c b/src/compiler/llvm_codegen_c_abi_wasm.c index b826e6b16..f1314c380 100644 --- a/src/compiler/llvm_codegen_c_abi_wasm.c +++ b/src/compiler/llvm_codegen_c_abi_wasm.c @@ -58,19 +58,21 @@ static ABIArgInfo *wasm_classify_return(Type *type) void c_abi_func_create_wasm(FunctionSignature *signature) { - if (signature->failable) + Type *rtype = abi_rtype(signature); + Type *return_type = abi_returntype(signature); + if (IS_FAILABLE(signature->rtype)) { signature->failable_abi_info = wasm_classify_return(type_lowering(type_anyerr)); } else { - signature->ret_abi_info = wasm_classify_return(type_lowering(signature->rtype->type)); + signature->ret_abi_info = wasm_classify_return(type_lowering(rtype)); } // If we have a failable, then the return type is a parameter. - if (signature->failable && signature->rtype->type->type_kind != TYPE_VOID) + if (IS_FAILABLE(signature->rtype) && rtype->type_kind != TYPE_VOID) { - signature->ret_abi_info = wasm_classify_argument_type(type_get_ptr(type_lowering(signature->rtype->type))); + signature->ret_abi_info = wasm_classify_argument_type(type_get_ptr(type_lowering(rtype))); } Decl **params = signature->params; diff --git a/src/compiler/llvm_codegen_c_abi_win64.c b/src/compiler/llvm_codegen_c_abi_win64.c index 71c438f3a..26a5b8db4 100644 --- a/src/compiler/llvm_codegen_c_abi_win64.c +++ b/src/compiler/llvm_codegen_c_abi_win64.c @@ -160,17 +160,18 @@ void c_abi_func_create_win64(FunctionSignature *signature) break; } - if (signature->failable) + Type *rtype = abi_rtype(signature); + if (IS_FAILABLE(signature->rtype)) { signature->failable_abi_info = win64_classify(®s, type_anyerr, true, is_vector_call, is_reg_call); - if (signature->rtype->type->type_kind != TYPE_VOID) + if (rtype->type_kind != TYPE_VOID) { - signature->ret_abi_info = win64_classify(®s, type_get_ptr(type_lowering(signature->rtype->type)), false, is_vector_call, is_reg_call); + signature->ret_abi_info = win64_classify(®s, type_get_ptr(type_lowering(rtype)), false, is_vector_call, is_reg_call); } } else { - signature->ret_abi_info = win64_classify(®s, signature->rtype->type, true, is_vector_call, is_reg_call); + signature->ret_abi_info = win64_classify(®s, rtype, true, is_vector_call, is_reg_call); } // Set up parameter registers. diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index 458089626..c44ed4f17 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -395,8 +395,6 @@ static void x64_classify(Type *type, ByteSize offset_base, X64Class *lo_class, X { case TYPE_ENUM: case TYPE_TYPEDEF: - case TYPE_FXX: - case TYPE_IXX: case TYPE_TYPEID: case TYPE_FUNC: case TYPE_DISTINCT: @@ -404,6 +402,7 @@ static void x64_classify(Type *type, ByteSize offset_base, X64Class *lo_class, X case TYPE_ANYERR: case TYPE_ERRTYPE: case TYPE_BITSTRUCT: + case TYPE_FAILABLE: case CT_TYPES: UNREACHABLE case TYPE_VOID: @@ -589,8 +588,6 @@ AbiType *x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_ty return x64_get_int_type_at_offset(element, offset - element_offset, source_type, source_offset); } case TYPE_VOID: - case TYPE_FXX: - case TYPE_IXX: case TYPE_TYPEID: case TYPE_ENUM: case TYPE_FUNC: @@ -600,6 +597,7 @@ AbiType *x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_ty case TYPE_ANYERR: case TYPE_ERRTYPE: case TYPE_BITSTRUCT: + case TYPE_FAILABLE: case CT_TYPES: UNREACHABLE case TYPE_I128: @@ -914,21 +912,22 @@ void c_abi_func_create_x64(FunctionSignature *signature) .sse_registers = is_regcall ? 16 : 8 }; - if (signature->failable) + Type *rtype = abi_rtype(signature); + if (IS_FAILABLE(signature->rtype)) { signature->failable_abi_info = x64_classify_return_type(type_anyerr, &available_registers, is_regcall); if (abi_arg_is_indirect(signature->failable_abi_info)) { available_registers.int_registers--; } - if (signature->rtype->type->type_kind != TYPE_VOID) + if (rtype->type_kind != TYPE_VOID) { - signature->ret_abi_info = x64_classify_parameter(type_get_ptr(type_lowering(signature->rtype->type)), &available_registers, is_regcall, NAMED); + signature->ret_abi_info = x64_classify_parameter(type_get_ptr(type_lowering(rtype)), &available_registers, is_regcall, NAMED); } } else { - signature->ret_abi_info = x64_classify_return_type(signature->rtype->type, &available_registers, is_regcall); + signature->ret_abi_info = x64_classify_return_type(rtype, &available_registers, is_regcall); if (abi_arg_is_indirect(signature->ret_abi_info)) { available_registers.int_registers--; diff --git a/src/compiler/llvm_codegen_c_abi_x86.c b/src/compiler/llvm_codegen_c_abi_x86.c index c79f3b843..72e091461 100644 --- a/src/compiler/llvm_codegen_c_abi_x86.c +++ b/src/compiler/llvm_codegen_c_abi_x86.c @@ -92,7 +92,7 @@ static ABIArgInfo *create_indirect_return_x86(Type *type, Regs *regs) static bool x86_should_return_type_in_reg(Type *type) { - type = type->canonical; + assert(type->canonical == type); unsigned size = type_size(type); if (size > 8) return false; @@ -119,6 +119,7 @@ static bool x86_should_return_type_in_reg(Type *type) case TYPE_ANYERR: case TYPE_BITSTRUCT: case CT_TYPES: + case TYPE_FAILABLE: UNREACHABLE case ALL_INTS: case ALL_FLOATS: @@ -384,7 +385,7 @@ static bool x86_try_put_primitive_in_reg(CallABI call, Regs *regs, Type *type) case CALL_X86_VECTOR: case CALL_X86_REG: if (type_size(type) > 4) return false; - return type_is_integer_kind(type) || type_is_pointer(type); + return type_is_integer_or_bool_kind(type) || type_is_pointer(type); default: return true; } @@ -594,6 +595,7 @@ static ABIArgInfo *x86_classify_argument(CallABI call, Regs *regs, Type *type) case TYPE_TYPEID: case TYPE_BITSTRUCT: case TYPE_STRLIT: + case TYPE_FAILABLE: case CT_TYPES: UNREACHABLE case ALL_FLOATS: @@ -655,17 +657,18 @@ void c_abi_func_create_x86(FunctionSignature *signature) // 4. Classify the return type. In the case of failable, we need to classify the failable itself as the // return type. - if (signature->failable) + Type *rtype = abi_rtype(signature); + if (IS_FAILABLE(signature->rtype)) { signature->failable_abi_info = x86_classify_return(signature->call_abi, ®s, type_anyerr); - if (signature->rtype->type->type_kind != TYPE_VOID) + if (rtype->type_kind != TYPE_VOID) { - signature->ret_abi_info = x86_classify_argument(signature->call_abi, ®s, type_get_ptr(type_lowering(signature->rtype->type))); + signature->ret_abi_info = x86_classify_argument(signature->call_abi, ®s, type_get_ptr(type_lowering(rtype))); } } else { - signature->ret_abi_info = x86_classify_return(signature->call_abi, ®s, signature->rtype->type); + signature->ret_abi_info = x86_classify_return(signature->call_abi, ®s, rtype); } /* diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index 5ee6facf5..46068aad4 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -289,9 +289,7 @@ static LLVMMetadataRef llvm_debug_enum_type(GenContext *c, Type *type, LLVMMetad VECEACH(enums, i) { Decl *enum_constant = enums[i]; - uint64_t val = is_unsigned - ? bigint_as_unsigned(&enum_constant->enum_constant.expr->const_expr.i) - : (uint64_t)bigint_as_signed(&enum_constant->enum_constant.expr->const_expr.i); + uint64_t val = int_to_u64(enum_constant->enum_constant.expr->const_expr.ixx); LLVMMetadataRef debug_info = LLVMDIBuilderCreateEnumerator( c->debug.builder, enum_constant->name, TOKLEN(enum_constant->name_token), @@ -481,12 +479,13 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type * // Consider special handling of UTF8 arrays. switch (type->type_kind) { - case TYPE_IXX: - case TYPE_FXX: case TYPE_TYPEID: case TYPE_STRLIT: case CT_TYPES: UNREACHABLE + case TYPE_FAILABLE: + // If this is reachable then we're not doing the proper lowering. + UNREACHABLE case TYPE_BOOL: return llvm_debug_simple_type(c, type, DW_ATE_boolean); case TYPE_I8: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index a082e3765..5e71e430c 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1342,9 +1342,9 @@ static void gencontext_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator); case UNARYOP_NOT: llvm_emit_expr(c, value, inner); - llvm_value_rvalue(c, value); if (type_is_vector(type)) { + llvm_value_rvalue(c, value); Type *vec_type = type_vector_type(type); if (type_is_float(vec_type)) { @@ -1359,17 +1359,37 @@ static void gencontext_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr llvm_value_set(value, llvm_value, res_type); return; } - if (type_is_float(type)) + switch (type->type_kind) { - llvm_value = LLVMBuildFCmp(c->builder, LLVMRealUNE, value->value, llvm_get_zero(c, type), "not"); - } - else if (type->type_kind == TYPE_BOOL) - { - llvm_value = LLVMBuildNot(c->builder, value->value, "not"); - } - else - { - llvm_value = LLVMBuildICmp(c->builder, LLVMIntEQ, value->value, llvm_get_zero(c, type), "not"); + case ALL_FLOATS: + llvm_value_rvalue(c, value); + llvm_value = LLVMBuildFCmp(c->builder, LLVMRealUNE, value->value, llvm_get_zero(c, type), "not"); + break; + case TYPE_BOOL: + llvm_value_rvalue(c, value); + llvm_value = LLVMBuildNot(c->builder, value->value, "not"); + break; + case TYPE_SUBARRAY: + if (value->kind != BE_VALUE) + { + llvm_emit_len_for_expr(c, value, value); + llvm_value_rvalue(c, value); + llvm_value = value->value; + } + else + { + llvm_value = LLVMBuildExtractValue(c->builder, value->value, 1, "len"); + } + llvm_value = LLVMBuildIsNull(c->builder, llvm_value, "not"); + break; + case ALL_INTS: + case TYPE_POINTER: + llvm_value_rvalue(c, value); + llvm_value = LLVMBuildIsNull(c->builder, value->value, "not"); + break; + default: + DEBUG_LOG("Unexpectedly tried to not %s", type_quoted_error_string(inner->type)); + UNREACHABLE } llvm_value_set_bool(value, llvm_value); return; @@ -1474,7 +1494,7 @@ static void llvm_emit_trap_zero(GenContext *c, Type *type, LLVMValueRef value, c if (!active_target.feature.safe_mode) return; LLVMValueRef zero = llvm_get_zero(c, type); - LLVMValueRef ok = type_is_any_integer(type) ? LLVMBuildICmp(c->builder, LLVMIntEQ, value, zero, "zero") : LLVMBuildFCmp(c->builder, LLVMRealUEQ, value, zero, "zero"); + LLVMValueRef ok = type_is_integer(type) ? LLVMBuildICmp(c->builder, LLVMIntEQ, value, zero, "zero") : LLVMBuildFCmp(c->builder, LLVMRealUEQ, value, zero, "zero"); llvm_emit_panic_on_true(c, ok, error, loc); } @@ -1809,6 +1829,8 @@ void llvm_emit_int_comp_zero(GenContext *c, BEValue *result, BEValue *lhs, Binar void llvm_emit_int_comparison(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs, BinaryOp binary_op) { + llvm_value_rvalue(c, lhs); + llvm_value_rvalue(c, rhs); llvm_emit_int_comp(c, result, lhs->type, rhs->type, lhs->value, rhs->value, binary_op); } void llvm_emit_int_comp(GenContext *c, BEValue *result, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op) @@ -1858,6 +1880,30 @@ void llvm_emit_int_comp(GenContext *c, BEValue *result, Type *lhs_type, Type *rh rhs_signed = false; } } + if (lhs_signed && !rhs_signed && !vector_type && LLVMIsConstant(lhs_value) && type_size(lhs_type) <= 64) + { + long long val = LLVMConstIntGetSExtValue(lhs_value); + if (val < 0) + { + switch (binary_op) + { + case BINARYOP_EQ: + case BINARYOP_GE: + case BINARYOP_GT: + llvm_value_set(result, llvm_const_int(c, type_bool, 0), type_bool); + return; + case BINARYOP_NE: + case BINARYOP_LE: + case BINARYOP_LT: + llvm_value_set(result, llvm_const_int(c, type_bool, 1), type_bool); + return; + default: + UNREACHABLE + } + } + lhs_signed = false; + } + if (!lhs_signed) { assert(lhs_signed == rhs_signed); @@ -1895,9 +1941,11 @@ void llvm_emit_int_comp(GenContext *c, BEValue *result, Type *lhs_type, Type *rh return; } + // Left side is signed. LLVMValueRef comp_value; LLVMValueRef check_value; + switch (binary_op) { case BINARYOP_EQ: @@ -2378,8 +2426,8 @@ static void llvm_emit_post_unary_expr(GenContext *context, BEValue *be_value, Ex llvm_emit_post_inc_dec(context, be_value, - expr->post_expr.expr, - expr->post_expr.operator == POSTUNARYOP_INC ? 1 : -1, + expr->unary_expr.expr, + expr->unary_expr.operator == UNARYOP_INC ? 1 : -1, false); } @@ -2474,9 +2522,39 @@ void llvm_emit_try_assign_expr(GenContext *c, BEValue *be_value, Expr *expr) } } +static inline void llvm_emit_catch_expr(GenContext *c, BEValue *value, Expr *expr) +{ + LLVMBasicBlockRef end_block = llvm_basic_block_new(c, "noerr_block"); + + // Store catch/error var + PUSH_ERROR(); + + LLVMValueRef error_var = llvm_emit_alloca_aligned(c, type_anyerr, "error_var"); + llvm_value_set_address(value, error_var, type_anyerr); + llvm_store_bevalue_raw(c, value, llvm_get_zero(c, type_anyerr)); + c->error_var = error_var; + c->catch_block = end_block; + + BEValue expr_value; + llvm_emit_expr(c, &expr_value, expr->try_expr.expr); + llvm_value_fold_failable(c, &expr_value); + + // Restore. + POP_ERROR(); + + // Emit success and jump to end block. + llvm_emit_br(c, end_block); + + llvm_emit_block(c, end_block); + +} void llvm_emit_try_expr(GenContext *c, BEValue *value, Expr *expr) { - + if (!expr->try_expr.is_try) + { + llvm_emit_catch_expr(c, value, expr); + return; + } LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error_block"); LLVMBasicBlockRef no_err_block = llvm_basic_block_new(c, "noerr_block"); LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "phi_trycatch_block"); @@ -2505,9 +2583,9 @@ void llvm_emit_try_expr(GenContext *c, BEValue *value, Expr *expr) llvm_emit_block(c, phi_block); - LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val"); - LLVMValueRef lhs = llvm_const_int(c, type_bool, expr->try_expr.is_try ? 1 : 0); - LLVMValueRef rhs = llvm_const_int(c, type_bool, expr->try_expr.is_try ? 0 : 1); + LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val"); + LLVMValueRef lhs = llvm_const_int(c, type_bool, 1); + LLVMValueRef rhs = llvm_const_int(c, type_bool, 0); LLVMValueRef logic_values[2] = { lhs, rhs }; LLVMBasicBlockRef blocks[2] = { no_err_block, error_block }; @@ -2832,21 +2910,21 @@ void gencontext_emit_ternary_expr(GenContext *c, BEValue *value, Expr *expr) llvm_value_set(value, phi, expr->type); } -static LLVMValueRef llvm_emit_real(LLVMTypeRef type, Real f) +static LLVMValueRef llvm_emit_real(LLVMTypeRef type, Float f) { - if (isnan(f)) + if (isnan(f.f)) { return LLVMConstRealOfString(type, "nan"); } - if (isinf(f)) + if (isinf(f.f)) { - return LLVMConstRealOfString(type, f < 0 ? "-inf" : "inf"); + return LLVMConstRealOfString(type, f.f < 0 ? "-inf" : "inf"); } scratch_buffer_clear(); #if LONG_DOUBLE - global_context.scratch_buffer_len = sprintf(global_context.scratch_buffer, "%La", f); + global_context.scratch_buffer_len = sprintf(global_context.scratch_buffer, "%La", f.f); #else - global_context.scratch_buffer_len = sprintf(global_context.scratch_buffer, "%a", f); + global_context.scratch_buffer_len = sprintf(global_context.scratch_buffer, "%a", f.f); #endif return LLVMConstRealOfStringAndSize(type, global_context.scratch_buffer, global_context.scratch_buffer_len); } @@ -2884,20 +2962,30 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) return; } case CONST_INTEGER: - if (type_is_unsigned(type)) + { + LLVMValueRef value; + Int128 i = expr->const_expr.ixx.i; + switch (expr->const_expr.ixx.type) { - llvm_value_set(be_value, llvm_const_int(c, type, bigint_as_unsigned(&expr->const_expr.i)), type); - } - else - { - llvm_value_set(be_value, llvm_const_int(c, type, bigint_as_signed(&expr->const_expr.i)), type); + case TYPE_I128: + case TYPE_U128: + { + uint64_t words[2] = { i.low, i.high }; + value = LLVMConstIntOfArbitraryPrecision(llvm_get_type(c, type), 2, words); + break; + } + default: + value = llvm_const_int(c, type, i.low); + break; } + llvm_value_set(be_value, value, type); return; + } case CONST_LIST: llvm_emit_const_initializer_list_expr(c, be_value, expr); return; case CONST_FLOAT: - llvm_value_set(be_value, llvm_emit_real(llvm_get_type(c, type), expr->const_expr.f), type); + llvm_value_set(be_value, llvm_emit_real(llvm_get_type(c, type), expr->const_expr.fxx), type); return; case CONST_POINTER: llvm_value_set(be_value, LLVMConstNull(llvm_get_type(c, type)), type); @@ -2987,8 +3075,6 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM switch (type_lowering(param_type)->type_kind) { case TYPE_VOID: - case TYPE_IXX: - case TYPE_FXX: case TYPE_TYPEID: case TYPE_FUNC: case TYPE_DISTINCT: @@ -2997,13 +3083,13 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM case TYPE_ERRTYPE: case TYPE_ANYERR: case TYPE_BITSTRUCT: + case TYPE_FAILABLE: case CT_TYPES: UNREACHABLE break; case TYPE_BOOL: - case ALL_SIGNED_INTS: - case ALL_UNSIGNED_INTS: - case ALL_REAL_FLOATS: + case ALL_INTS: + case ALL_FLOATS: case TYPE_POINTER: vec_add(*values, LLVMBuildLoad2(context->builder, llvm_get_type(context, param_type), expand_ptr, "loadexpanded")); return; @@ -3312,14 +3398,14 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 4. Prepare the return abi data. ABIArgInfo *ret_info = signature->ret_abi_info; - Type *return_type = signature->rtype->type->canonical; + Type *return_type = abi_returntype(signature); + bool is_failable = IS_FAILABLE(signature->rtype); // 5. In the case of a failable, the error is replacing the regular return abi. LLVMValueRef error_var = NULL; - if (signature->failable) + if (is_failable) { ret_info = signature->failable_abi_info; - return_type = type_anyerr; } *result_value = (BEValue){ .kind = BE_VALUE, .value = NULL }; @@ -3328,7 +3414,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) { case ABI_ARG_INDIRECT: // 6a. We can use the stored error var if there is no redirect. - if (signature->failable && c->error_var && !ret_info->attributes.realign) + if (is_failable && c->error_var && !ret_info->attributes.realign) { error_var = c->error_var; vec_add(values, error_var); @@ -3355,7 +3441,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 7. We might have a failable indirect return and a normal return. // In this case we need to add it by hand. BEValue synthetic_return_param = { 0 }; - if (signature->failable && signature->ret_abi_info) + if (is_failable && signature->ret_abi_info) { // 7b. Create the address to hold the return. Type *actual_return_type = type_lowering(signature->rtype->type); @@ -3494,7 +3580,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) case ABI_ARG_IGNORE: // 12. Basically void returns or empty structs. // Here we know we don't have a failable or any return value that can be used. - assert(!signature->failable && "Failable should have produced a return value."); + assert(!is_failable && "Failable should have produced a return value."); *result_value = (BEValue) { .type = type_void, .kind = BE_VALUE }; return; case ABI_ARG_INDIRECT: @@ -3602,7 +3688,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) } // 17. Handle failables. - if (signature->failable) + if (is_failable) { BEValue no_err; @@ -3668,7 +3754,7 @@ static inline void gencontext_emit_expression_list_expr(GenContext *context, BEV static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value, Type *type, Ast **stmts) { - + Type *type_lowered = type_lowering(type); LLVMValueRef old_ret_out = context->return_out; LLVMBasicBlockRef saved_block_return_exit = context->block_return_exit; LLVMBasicBlockRef saved_block_failable_exit = context->block_failable_exit; @@ -3682,9 +3768,9 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value LLVMValueRef error_out = context->error_var; LLVMBasicBlockRef error_block = context->catch_block; - if (type != type_void) + if (type_lowered != type_void) { - return_out = llvm_emit_alloca_aligned(context, type, "blockret"); + return_out = llvm_emit_alloca_aligned(context, type_lowered, "blockret"); } context->block_error_var = error_out; context->block_failable_exit = error_block; @@ -3711,7 +3797,7 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value if (return_out) { - llvm_value_set_address(be_value, return_out, type); + llvm_value_set_address(be_value, return_out, type_lowered); } else { @@ -3783,7 +3869,7 @@ BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValue if (failable) { - if (expr->failable) + if (IS_FAILABLE(expr)) { assign_block = llvm_basic_block_new(c, "after_assign"); c->error_var = failable; @@ -3823,7 +3909,7 @@ BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValue } POP_ERROR(); - if (failable && expr->failable) + if (failable && IS_FAILABLE(expr)) { llvm_emit_br(c, assign_block); llvm_emit_block(c, assign_block); @@ -3848,12 +3934,13 @@ static inline void gencontext_emit_failable(GenContext *context, BEValue *be_val llvm_emit_br(context, context->catch_block); LLVMBasicBlockRef ignored_block = llvm_basic_block_new(context, "postfailed"); llvm_emit_block(context, ignored_block); - if (expr->type->canonical == type_void) + Type *type = type_no_fail(expr->type); + if (type->canonical == type_void) { llvm_value_set(be_value, NULL, type_void); return; } - llvm_value_set(be_value, LLVMGetUndef(llvm_get_type(context, expr->type)), expr->type); + llvm_value_set(be_value, LLVMGetUndef(llvm_get_type(context, type)), type); } static inline LLVMValueRef llvm_update_vector(GenContext *c, LLVMValueRef vector, LLVMValueRef value, ArrayIndex index, bool *is_const) @@ -3931,12 +4018,14 @@ static inline void llvm_emit_vector_initializer_list(GenContext *c, BEValue *val static inline void llvm_emit_initializer_list_expr(GenContext *c, BEValue *value, Expr *expr) { - if (type_is_vector(expr->type)) + Type *type = type_lowering(expr->type); + if (type_is_vector(type)) { llvm_emit_vector_initializer_list(c, value, expr); return; } - llvm_value_set_address(value, llvm_emit_alloca_aligned(c, expr->type, "literal"), expr->type); + assert(!IS_FAILABLE(expr) || c->catch_block); + llvm_value_set_address(value, llvm_emit_alloca_aligned(c, type, "literal"), type); llvm_emit_initialize_reference(c, value, expr); } @@ -4127,6 +4216,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) llvm_emit_try_assign_expr(c, value, expr); return; case EXPR_NOP: + llvm_value_set(value, NULL, type_void); return; case EXPR_ELSE: gencontext_emit_else_expr(c, value, expr); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 43b72cff8..0217b891b 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -250,12 +250,14 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl // If we have a failable it's always the return argument, so we need to copy // the return value into the return value holder. LLVMValueRef return_out = c->return_out; + bool is_failable = IS_FAILABLE(signature->rtype); Type *return_type = signature->rtype->type; + if (is_failable) return_type = return_type->failable; BEValue no_fail; // In this case we use the failable as the actual return. - if (signature->failable) + if (is_failable) { if (return_value && return_value->value) { @@ -346,18 +348,19 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl void llvm_emit_return_implicit(GenContext *c) { - if (c->cur_func_decl->func_decl.function_signature.rtype->type != type_void) + Type *rtype_real = c->cur_func_decl->func_decl.function_signature.rtype->type; + if (type_lowering(type_no_fail(rtype_real)) != type_void) { LLVMBuildUnreachable(c->builder); return; } - if (!c->cur_func_decl->func_decl.function_signature.failable) + if (rtype_real->type_kind == TYPE_FAILABLE) { llvm_emit_return_abi(c, NULL, NULL); return; } BEValue value; - llvm_value_set(&value, LLVMConstNull(llvm_get_type(c, type_anyerr)), type_anyerr); + llvm_value_set(&value, llvm_get_zero(c, type_anyerr), type_anyerr); llvm_emit_return_abi(c, NULL, &value); } @@ -400,7 +403,8 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) llvm_debug_scope_push(context, context->debug.function); } - if (signature->failable && signature->failable_abi_info->kind == ABI_ARG_INDIRECT) + bool is_failable = IS_FAILABLE(signature->rtype); + if (is_failable && signature->failable_abi_info->kind == ABI_ARG_INDIRECT) { context->failable_out = LLVMGetParam(context->function, arg++); } @@ -415,7 +419,7 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) else { context->return_out = NULL; - if (signature->ret_abi_info && signature->failable) + if (signature->ret_abi_info && is_failable) { context->return_out = LLVMGetParam(context->function, arg++); } diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index c8a25f017..3a5e6b72a 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -307,7 +307,7 @@ static inline LLVMValueRef decl_failable_ref(Decl *decl) { assert(decl->decl_kind == DECL_VAR); if (decl->var.kind == VARDECL_UNWRAPPED) return decl_failable_ref(decl->var.alias); - if (!decl->var.failable) return NULL; + if (decl->type->type_kind != TYPE_FAILABLE) return NULL; return decl->var.failable_ref; } @@ -411,7 +411,7 @@ static inline LLVMValueRef llvm_get_zero(GenContext *c, Type *type) static inline LLVMValueRef llvm_const_int(GenContext *c, Type *type, uint64_t val) { type = type_lowering(type); - assert(type_is_any_integer(type) || type->type_kind == TYPE_BOOL); + assert(type_is_integer(type) || type->type_kind == TYPE_BOOL); return LLVMConstInt(llvm_get_type(c, type), val, type_is_integer_signed(type)); } @@ -438,3 +438,4 @@ static inline bool abi_info_should_flatten(ABIArgInfo *info) { return info->kind == ABI_ARG_DIRECT_COERCE && info->direct_coerce.elements > 1U && !info->direct_coerce.prevent_flatten; } + diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 07e54fb38..08601d903 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -48,7 +48,8 @@ void gencontext_emit_ct_compound_stmt(GenContext *context, Ast *ast) LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl) { // 1. Get the declaration and the LLVM type. - LLVMTypeRef alloc_type = llvm_get_type(c, type_lowering(decl->type)); + Type *var_type = type_lowering(type_no_fail(decl->type)); + LLVMTypeRef alloc_type = llvm_get_type(c, var_type); // 2. In the case we have a static variable, // then we essentially treat this as a global. @@ -56,8 +57,8 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl) { void *builder = c->builder; c->builder = NULL; - decl->backend_ref = LLVMAddGlobal(c->module, llvm_get_type(c, decl->type), "tempglobal"); - if (decl->var.failable) + decl->backend_ref = LLVMAddGlobal(c->module, alloc_type, "tempglobal"); + if (IS_FAILABLE(decl)) { scratch_buffer_clear(); scratch_buffer_append(decl->external_name); @@ -69,14 +70,14 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl) return decl->backend_ref; } llvm_emit_local_var_alloca(c, decl); - if (decl->var.failable) + if (IS_FAILABLE(decl)) { scratch_buffer_clear(); scratch_buffer_append(decl->name); scratch_buffer_append(".f"); decl->var.failable_ref = llvm_emit_alloca_aligned(c, type_anyerr, scratch_buffer_to_string()); // Only clear out the result if the assignment isn't a failable. - if (!decl->var.init_expr || !decl->var.init_expr->failable) + if (!decl->var.init_expr || !IS_FAILABLE(decl->var.init_expr)) { LLVMBuildStore(c->builder, LLVMConstNull(llvm_get_type(c, type_anyerr)), decl->var.failable_ref); } @@ -178,7 +179,7 @@ static inline void gencontext_emit_return(GenContext *c, Ast *ast) c->error_var = c->block_error_var; c->catch_block = c->block_failable_exit; } - else if (c->cur_func_decl->func_decl.function_signature.failable) + else if (IS_FAILABLE(c->cur_func_decl->func_decl.function_signature.rtype)) { error_return_block = llvm_basic_block_new(c, "err_retblock"); error_out = llvm_emit_alloca_aligned(c, type_anyerr, "reterr"); @@ -962,6 +963,96 @@ void gencontext_emit_scoped_stmt(GenContext *context, Ast *ast) llvm_emit_defer(context, ast->scoped_stmt.defers.start, ast->scoped_stmt.defers.end); } +static bool expr_is_pure(Expr *expr) +{ + if (!expr) return true; + switch (expr->expr_kind) + { + case EXPR_CONST: + case EXPR_CONST_IDENTIFIER: + case EXPR_IDENTIFIER: + case EXPR_NOP: + return true; + case EXPR_BINARY: + if (expr->binary_expr.operator >= BINARYOP_ASSIGN) return false; + return expr_is_pure(expr->binary_expr.right) && expr_is_pure(expr->binary_expr.left); + case EXPR_UNARY: + switch (expr->unary_expr.operator) + { + case UNARYOP_INC: + case UNARYOP_DEC: + case UNARYOP_TADDR: + return false; + default: + return expr_is_pure(expr->unary_expr.expr); + } + break; + case EXPR_ACCESS: + return expr_is_pure(expr->access_expr.parent); + case EXPR_POISONED: + case EXPR_CT_IDENT: + case EXPR_TYPEID: + case EXPR_CT_CALL: + case EXPR_TYPEOF: + UNREACHABLE + case EXPR_MACRO_BODY_EXPANSION: + case EXPR_CALL: + case EXPR_CATCH_UNWRAP: + case EXPR_COMPOUND_LITERAL: + case EXPR_COND: + case EXPR_DESIGNATOR: + case EXPR_DECL: + case EXPR_ELSE: + case EXPR_EXPR_BLOCK: + case EXPR_FAILABLE: + case EXPR_GUARD: + case EXPR_HASH_IDENT: + case EXPR_MACRO_BLOCK: + case EXPR_MACRO_EXPANSION: + case EXPR_FLATPATH: + case EXPR_INITIALIZER_LIST: + case EXPR_DESIGNATED_INITIALIZER_LIST: + case EXPR_PLACEHOLDER: + case EXPR_POST_UNARY: + case EXPR_SCOPED_EXPR: + case EXPR_SLICE_ASSIGN: + case EXPR_TRY_UNWRAP: + case EXPR_TRY_UNWRAP_CHAIN: + case EXPR_TRY_ASSIGN: + case EXPR_UNDEF: + case EXPR_TYPEINFO: + return false; + case EXPR_CAST: + return expr_is_pure(expr->cast_expr.expr); + case EXPR_EXPRESSION_LIST: + { + VECEACH(expr->expression_list, i) + { + if (!expr_is_pure(expr->expression_list[i])) return false; + } + return true; + } + break; + case EXPR_GROUP: + return expr_is_pure(expr->group_expr); + case EXPR_LEN: + return expr_is_pure(expr->len_expr.inner); + case EXPR_SLICE: + return expr_is_pure(expr->slice_expr.expr) + && expr_is_pure(expr->slice_expr.start) + && expr_is_pure(expr->slice_expr.end); + case EXPR_SUBSCRIPT: + return expr_is_pure(expr->subscript_expr.expr) + && expr_is_pure(expr->subscript_expr.index); + case EXPR_TERNARY: + return expr_is_pure(expr->ternary_expr.cond) + && expr_is_pure(expr->ternary_expr.else_expr) + && expr_is_pure(expr->ternary_expr.then_expr); + case EXPR_TRY: + return expr_is_pure(expr->try_expr.expr); + } + UNREACHABLE +} static inline void llvm_emit_assume(GenContext *c, Expr *expr) { @@ -994,7 +1085,7 @@ static inline void llvm_emit_assume(GenContext *c, Expr *expr) } // 3. Check if pure, if so we emit the assume. - if (expr->pure) + if (expr_is_pure(expr)) { BEValue value; llvm_emit_expr(c, &value, expr); @@ -1092,7 +1183,7 @@ static inline void gencontext_emit_unreachable_stmt(GenContext *context, Ast *as void gencontext_emit_expr_stmt(GenContext *c, Ast *ast) { BEValue value; - if (ast->expr_stmt->failable) + if (IS_FAILABLE(ast->expr_stmt)) { PUSH_ERROR(); LLVMBasicBlockRef discard_fail = llvm_basic_block_new(c, "voiderr"); @@ -1279,7 +1370,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) case AST_DOCS: case AST_DOC_DIRECTIVE: case AST_POISONED: - case AST_DEFINE_STMT: + case AST_VAR_STMT: case AST_IF_CATCH_SWITCH_STMT: UNREACHABLE case AST_SCOPED_STMT: diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 43cd11831..d8c1ed9a0 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -232,8 +232,11 @@ LLVMTypeRef llvm_func_type(GenContext *context, Type *type) LLVMTypeRef return_type = NULL; - Type *real_return_type = signature->failable ? type_anyerr : signature->rtype->type->canonical; - ABIArgInfo *ret_arg_info = signature->failable ? signature->failable_abi_info : signature->ret_abi_info; + Type *rtype = signature->rtype->type; + bool is_failable = rtype->type_kind == TYPE_FAILABLE; + if (is_failable) rtype = rtype->failable; + Type *real_return_type = is_failable ? type_anyerr : rtype; + ABIArgInfo *ret_arg_info = is_failable ? signature->failable_abi_info : signature->ret_abi_info; ret_arg_info->param_index_end = 0; ret_arg_info->param_index_start = 0; @@ -276,9 +279,9 @@ LLVMTypeRef llvm_func_type(GenContext *context, Type *type) } // If it's failable and it's not void (meaning ret_abi_info will be NULL) - if (signature->failable && signature->ret_abi_info) + if (is_failable && signature->ret_abi_info) { - add_func_type_param(context, type_get_ptr(signature->rtype->type), signature->ret_abi_info, ¶ms); + add_func_type_param(context, type_get_ptr(rtype), signature->ret_abi_info, ¶ms); } // Add in all of the required arguments. @@ -310,6 +313,9 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) { case CT_TYPES: UNREACHABLE + case TYPE_FAILABLE: + // If this is reachable, then we're not doing the proper lowering. + UNREACHABLE case TYPE_TYPEID: case TYPE_ANYERR: case TYPE_ERRTYPE: @@ -329,7 +335,6 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) case TYPE_VOID: return any_type->backend_type = LLVMVoidTypeInContext(c->context); case TYPE_F64: - case TYPE_FXX: return any_type->backend_type = LLVMDoubleTypeInContext(c->context); case TYPE_F16: return any_type->backend_type = LLVMHalfTypeInContext(c->context); @@ -340,8 +345,6 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) case ALL_SIGNED_INTS: case ALL_UNSIGNED_INTS: return any_type->backend_type = LLVMIntTypeInContext(c->context, any_type->builtin.bitsize); - case TYPE_IXX: - return any_type->backend_type = LLVMIntTypeInContext(c->context, 32U); case TYPE_BOOL: return any_type->backend_type = LLVMIntTypeInContext(c->context, 8U); case TYPE_POINTER: diff --git a/src/compiler/number.c b/src/compiler/number.c index ce1466d25..b17428e38 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -7,16 +7,14 @@ void expr_const_set_int(ExprConst *expr, uint64_t v, TypeKind kind) { + expr->ixx.i.high = 0; if (type_kind_is_signed(kind)) { - bigint_init_signed(&expr->i, (int64_t)v); - } - else - { - bigint_init_unsigned(&expr->i, v); + if (v > (uint64_t)INT64_MAX) expr->ixx.i.high = UINT64_MAX; } + expr->ixx.i.low = v; + expr->ixx.type = kind; expr->const_kind = CONST_INTEGER; - expr->int_type = kind; } void expr_const_set_bool(ExprConst *expr, bool b) @@ -27,8 +25,7 @@ void expr_const_set_bool(ExprConst *expr, bool b) void expr_const_set_null(ExprConst *expr) { - expr->i.digit_count = 0; - expr->i.digit = 0; + expr->ixx = (Int) { .i = (Int128) { 0, 0 }, .type = type_iptr->canonical->type_kind }; expr->const_kind = CONST_POINTER; } @@ -52,29 +49,6 @@ static inline bool compare_bool(bool left, bool right, BinaryOp op) 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(Real left, Real right, BinaryOp op) { switch (op) @@ -104,9 +78,9 @@ bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp case CONST_BOOL: return compare_bool(left->b, right->b, op); case CONST_INTEGER: - return compare_ints(&left->i, &right->i, op); + return int_comp(left->ixx, right->ixx, op); case CONST_FLOAT: - return compare_fps(left->f, right->f, op); + return compare_fps(left->fxx.f, right->fxx.f, op); case CONST_POINTER: return true; case CONST_STRING: @@ -190,29 +164,14 @@ bool expr_const_will_overflow(const ExprConst *expr, TypeKind kind) { switch (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 expr->i.is_negative || !bigint_fits_in_bits(&expr->i, 8, false); - case TYPE_U16: - return expr->i.is_negative || !bigint_fits_in_bits(&expr->i, 16, false); - case TYPE_U32: - return expr->i.is_negative || !bigint_fits_in_bits(&expr->i, 32, false); - case TYPE_U64: - return expr->i.is_negative || !bigint_fits_in_bits(&expr->i, 64, false); + case ALL_INTS: + return !int_fits(expr->ixx, kind); case TYPE_F16: - return !bigint_fits_in_bits(&expr->i, 17, false); - case TYPE_IXX: + REMINDER("Check f16 narrowing"); + FALLTHROUGH; case TYPE_F32: case TYPE_F64: case TYPE_F128: - case TYPE_FXX: case TYPE_BOOL: return false; default: @@ -220,10 +179,6 @@ bool expr_const_will_overflow(const ExprConst *expr, TypeKind kind) } } -bool expr_const_int_overflowed(const ExprConst *expr) -{ - return expr_const_will_overflow(expr, expr->int_type); -} const char *expr_const_to_error_string(const ExprConst *expr) { char *buff = NULL; @@ -234,12 +189,12 @@ const char *expr_const_to_error_string(const ExprConst *expr) case CONST_BOOL: return expr->b ? "true" : "false"; case CONST_INTEGER: - return bigint_to_error_string(&expr->i, 10); + return int_to_str(expr->ixx, 10); case CONST_FLOAT: #if LONG_DOUBLE - asprintf(&buff, "%Lg", expr->f); + asprintf(&buff, "%Lg", expr->fxx.f); #else - asprintf(&buff, "%g", expr->f); + asprintf(&buff, "%g", expr->fxx.f); #endif return buff; case CONST_STRING: @@ -265,16 +220,16 @@ void expr_const_set_float(ExprConst *expr, Real d, TypeKind kind) switch (kind) { case TYPE_F32: - expr->f = (float)d; + expr->fxx = (Float) { (float)d, TYPE_F32 }; break; case TYPE_F64: - expr->f = (double)d; + expr->fxx = (Float) { (double)d, TYPE_F64 }; break; default: - expr->f = d; + expr->fxx = (Float) { d, kind }; + break; } expr->const_kind = CONST_FLOAT; - expr->float_type = kind; } ByteSize expr_const_list_size(const ConstInitializer *list) diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index ceac89a0b..ad00fcfc9 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -410,8 +410,8 @@ static Expr *parse_post_unary(Context *context, Expr *left) { assert(expr_ok(left)); Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, context->tok); - unary->post_expr.expr = left; - unary->post_expr.operator = post_unaryop_from_token(context->tok.type); + unary->unary_expr.expr = left; + unary->unary_expr.operator = unaryop_from_token(context->tok.type); unary->span.loc = left->span.loc; advance(context); return unary; @@ -666,7 +666,7 @@ static Expr *parse_subscript_expr(Context *context, Expr *left) else { index = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - expr_set_type(index, type_uint); + index->type = type_uint; index->resolve_status = RESOLVE_DONE; expr_const_set_int(&index->const_expr, 0, type_uint->type_kind); } @@ -759,22 +759,20 @@ static Expr *parse_ct_call(Context *context, Expr *left) SEMA_TOKEN_ERROR(context->tok, "Expected an integer index."); return poisoned_expr; } - BigInt *value = &int_expr->const_expr.i; - BigInt limit; - bigint_init_unsigned(&limit, MAX_ARRAYINDEX); - if (bigint_cmp(value, &limit) == CMP_GT) + Int value = int_expr->const_expr.ixx; + if (!int_fits(value, TYPE_I64)) { SEMA_ERROR(int_expr, "Array index out of range."); return poisoned_expr; } - if (bigint_cmp_zero(value) == CMP_LT) + if (int_is_neg(value)) { SEMA_ERROR(int_expr, "Array index must be zero or greater."); return poisoned_expr; } TRY_CONSUME_OR(TOKEN_RBRACKET, "Expected a ']' after the number.", poisoned_expr); flat_element.array = true; - flat_element.index = (ArrayIndex)bigint_as_unsigned(value); + flat_element.index = value.i.low; } else if (try_consume(context, TOKEN_DOT)) { @@ -922,6 +920,18 @@ static Expr *parse_placeholder(Context *context, Expr *left) return expr; } +static int read_num_type(const char *string, const char *end) +{ + REMINDER("Limit num type reader"); + int i = 0; + while (string < end) + { + i *= 10; + i += *(string++) - '0'; + } + return i; +} + static Expr *parse_integer(Context *context, Expr *left) { assert(!left && "Had left hand side"); @@ -929,73 +939,178 @@ static Expr *parse_integer(Context *context, Expr *left) const char *string = TOKSTR(context->tok); const char *end = string + TOKLEN(context->tok); - 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; + Int128 i = { 0, 0 }; + bool is_unsigned = false; + uint64_t type_bits = 0; + int hex_characters = 0; + int oct_characters = 0; + int binary_characters = 0; + bool wrapped = false; + uint64_t max; switch (TOKLEN(context->tok) > 2 ? string[1] : '0') { case 'x': string += 2; + is_unsigned = true; + max = UINT64_MAX >> 4; while (string < end) { char c = *(string++); + if (c == 'u' || c == 'U') + { + type_bits = read_num_type(string, end); + break; + } + if (c == 'i' || c == 'I') + { + is_unsigned = false; + type_bits = read_num_type(string, end); + break; + } if (c == '_') continue; - bigint_shl_int(&res, i, 4); - if (c < 'A') - { - bigint_init_unsigned(&diff, c - '0'); - } - else if (c < 'a') - { - bigint_init_unsigned(&diff, c - 'A' + 10); - } - else - { - bigint_init_unsigned(&diff, c - 'a' + 10); - } - bigint_add(i, &res, &diff); + if (i.high > max) wrapped = true; + i = i128_shl64(i, 4); + i = i128_add64(i, hex_nibble(c)); + hex_characters++; } break; case 'o': string += 2; + is_unsigned = true; + max = UINT64_MAX >> 3; while (string < end) { char c = *(string++); + if (c == 'i' || c == 'I') + { + is_unsigned = false; + type_bits = read_num_type(string, end); + break; + } + if (c == 'u' || c == 'U') + { + type_bits = read_num_type(string, end); + break; + } if (c == '_') continue; - bigint_shl_int(&res, i, 4); - bigint_init_unsigned(&diff, c - '0'); - bigint_add(i, &res, &diff); + if (i.high > max) wrapped = true; + i = i128_shl64(i, 3); + i = i128_add64(i, c - '0'); + oct_characters++; } break; case 'b': string += 2; + max = UINT64_MAX >> 1; while (string < end) { char c = *(string++); if (c == '_') continue; - bigint_shl_int(&res, i, 1); - bigint_init_unsigned(&diff, c - '0'); - bigint_add(i, &res, &diff); + binary_characters++; + if (i.high > max) wrapped = true; + i = i128_shl64(i, 1); + i = i128_add64(i, c - '0'); } break; default: while (string < end) { char c = *(string++); + if (c == 'i' || c == 'I') + { + is_unsigned = false; + type_bits = read_num_type(string, end); + break; + } + if (c == 'u' || c == 'U') + { + is_unsigned = true; + type_bits = read_num_type(string, end); + break; + } if (c == '_') continue; - bigint_mul(&res, i, &ten); - bigint_init_unsigned(&diff, c - '0'); - bigint_add(i, &res, &diff); + uint64_t old_top = i.high; + i = i128_mult64(i, 10); + i = i128_add64(i, c - '0'); + if (!wrapped && old_top > i.high) wrapped = true; } break; } + if (wrapped) + { + SEMA_TOKEN_ERROR(context->tok, "Integer size exceeded 128 bits, max 128 bits are supported."); + return poisoned_expr; + } expr_int->const_expr.const_kind = CONST_INTEGER; - expr_int->const_expr.int_type = TYPE_IXX; - expr_set_type(expr_int, type_compint); + Type *type = is_unsigned ? type_cuint() : type_cint(); + expr_int->const_expr.narrowable = !type_bits; + if (type_bits) + { + if (!is_power_of_two(type_bits) || type_bits > 128) + { + SEMA_TOKEN_ERROR(context->tok, "Integer width should be 8, 16, 32, 64 or 128."); + return poisoned_expr; + } + } + else + { + if (hex_characters) + { + type_bits = 4 * hex_characters; + if (type_bits > 128) + { + SEMA_TOKEN_ERROR(context->tok, "%d hex digits indicates a bit width over 128, which is not supported.", hex_characters); + return poisoned_expr; + } + } + if (oct_characters) + { + type_bits = 3 * oct_characters; + if (type_bits > 128) + { + SEMA_TOKEN_ERROR(context->tok, "%d octal digits indicates a bit width over 128, which is not supported.", oct_characters); + return poisoned_expr; + } + } + if (binary_characters) + { + type_bits = binary_characters; + if (type_bits > 128) + { + SEMA_TOKEN_ERROR(context->tok, "%d binary digits indicates a bit width over 128, which is not supported.", binary_characters); + return poisoned_expr; + } + } + if (type_bits && !is_power_of_two(type_bits)) type_bits = next_highest_power_of_2(type_bits); + } + if (!type_bits) + { + type_bits = type_size(type) * 8; + } + if (type_bits) + { + type = is_unsigned ? type_int_unsigned_by_bitsize(type_bits) : type_int_signed_by_bitsize(type_bits); + } + expr_int->const_expr.ixx = (Int) { i, type->type_kind }; + if (!int_fits(expr_int->const_expr.ixx, type->type_kind)) + { + int radix = 10; + if (hex_characters) radix = 16; + if (oct_characters) radix = 8; + if (binary_characters) radix = 2; + if (type_bits) + { + SEMA_TOKEN_ERROR(context->tok, "'%s' does not fit in a '%c%d' literal.", + i128_to_string(i, radix, true), is_unsigned ? 'u' : 'i', type_bits); + } + else + { + SEMA_TOKEN_ERROR(context->tok, "'%s' does not fit in an %s literal.", + i128_to_string(i, radix, true), is_unsigned ? "unsigned int" : "int"); + } + return poisoned_expr; + } + expr_int->type = type; advance(context); return expr_int; } @@ -1100,7 +1215,8 @@ static Expr *parse_bytes_expr(Context *context, Expr *left) expr_bytes->const_expr.bytes.ptr = data; expr_bytes->const_expr.bytes.len = len; expr_bytes->const_expr.const_kind = CONST_BYTES; - expr_set_type(expr_bytes, type_get_array(type_char, len)); + Type *type = type_get_array(type_char, len); + expr_bytes->type = type; assert(data + len == data_current); return expr_bytes; } @@ -1109,24 +1225,25 @@ static Expr *parse_char_lit(Context *context, Expr *left) { assert(!left && "Had left hand side"); Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + expr_int->const_expr.narrowable = true; TokenData *data = tokendata_from_id(context->tok.id); switch (data->width) { case 1: - expr_const_set_int(&expr_int->const_expr, data->char_lit.u8, TYPE_IXX); - expr_set_type(expr_int, type_compint); + expr_const_set_int(&expr_int->const_expr, data->char_lit.u8, TYPE_U8); + expr_int->type = type_char; break; case 2: - expr_const_set_int(&expr_int->const_expr, data->char_lit.u16, TYPE_IXX); - expr_set_type(expr_int, type_compint); + expr_const_set_int(&expr_int->const_expr, data->char_lit.u16, TYPE_U16); + expr_int->type = type_ushort; break; case 4: - expr_const_set_int(&expr_int->const_expr, data->char_lit.u32, TYPE_IXX); - expr_set_type(expr_int, type_compint); + expr_const_set_int(&expr_int->const_expr, data->char_lit.u32, TYPE_U32); + expr_int->type = type_uint; break; case 8: expr_const_set_int(&expr_int->const_expr, data->char_lit.u64, TYPE_U64); - expr_set_type(expr_int, type_compint); + expr_int->type = type_ulong; break; default: UNREACHABLE @@ -1141,10 +1258,26 @@ static Expr *parse_double(Context *context, Expr *left) { assert(!left && "Had left hand side"); Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - number->const_expr.f = TOKREAL(context->tok.id); - expr_set_type(number, type_compfloat); - number->const_expr.float_type = TYPE_FXX; + number->const_expr.fxx = TOKREAL(context->tok.id); + switch (number->const_expr.fxx.type) + { + case TYPE_F128: + number->type = type_quad; + break; + case TYPE_F64: + number->type = type_double; + break; + case TYPE_F32: + number->type = type_float; + break; + case TYPE_F16: + number->type = type_half; + break; + default: + UNREACHABLE + } number->const_expr.const_kind = CONST_FLOAT; + number->const_expr.narrowable = true; advance(context); return number; } @@ -1226,7 +1359,7 @@ static Expr *parse_string_literal(Context *context, Expr *left) { assert(!left && "Had left hand side"); Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - expr_set_type(expr_string, type_compstr); + expr_string->type = type_compstr; TokenData *data = TOKDATA(context->tok); const char *str = data->string; @@ -1255,7 +1388,7 @@ static Expr *parse_string_literal(Context *context, Expr *left) assert(str); expr_string->const_expr.string.chars = str; expr_string->const_expr.string.len = (uint32_t)len; - expr_set_type(expr_string, type_compstr); + expr_string->type = type_compstr; expr_string->const_expr.const_kind = CONST_STRING; return expr_string; } @@ -1265,7 +1398,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 = TOKEN_IS(TOKEN_TRUE), .const_kind = CONST_BOOL }; - expr_set_type(number, type_bool); + number->type = type_bool; advance(context); return number; } @@ -1275,7 +1408,7 @@ static Expr *parse_null(Context *context, Expr *left) assert(!left && "Had left hand side"); Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); number->const_expr.const_kind = CONST_POINTER; - expr_set_type(number, type_voidptr); + number->type = type_voidptr; advance(context); return number; } @@ -1309,10 +1442,11 @@ Expr *parse_type_expression_with_path(Context *context, Path *path) advance_and_verify(context, TOKEN_TYPE_IDENT); RANGE_EXTEND_PREV(type); ASSIGN_TYPE_ELSE(type, parse_type_with_base(context, type), poisoned_expr); + type->failable = try_consume(context, TOKEN_BANG); } else { - ASSIGN_TYPE_ELSE(type, parse_type(context), poisoned_expr); + ASSIGN_TYPE_ELSE(type, parse_failable_type(context), poisoned_expr); } if (!type->virtual_type && TOKEN_IS(TOKEN_LBRACE)) { diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 5512a51dd..09415ca8a 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -737,6 +737,23 @@ TypeInfo *parse_type(Context *context) return parse_type_with_base(context, base); } +TypeInfo *parse_failable_type(Context *context) +{ + ASSIGN_TYPE_ELSE(TypeInfo *info, parse_base_type(context), poisoned_type_info); + ASSIGN_TYPE_ELSE(info, parse_type_with_base(context, info), poisoned_type_info); + if (try_consume(context, TOKEN_BANG)) + { + assert(!info->failable); + info->failable = true; + if (info->resolve_status == RESOLVE_DONE) + { + info->type = type_get_failable(info->type); + } + RANGE_EXTEND_PREV(info); + } + return info; +} + #pragma mark --- Decl parsing @@ -754,13 +771,13 @@ Decl *parse_decl_after_type(Context *context, TypeInfo *type) return poisoned_decl; } - EXPECT_IDENT_FOR_OR("variable name", poisoned_decl); TokenId name = context->tok.id; advance(context); Decl *decl = decl_new_var(name, type, VARDECL_LOCAL, VISIBLE_LOCAL); + if (!parse_attributes(context, &decl->attributes)) return poisoned_decl; if (TOKEN_IS(TOKEN_EQ)) { if (!decl) @@ -789,17 +806,14 @@ Decl *parse_decl(Context *context) bool is_static = try_consume(context, TOKEN_STATIC); - ASSIGN_TYPE_ELSE(TypeInfo *type, parse_type(context), poisoned_decl); - - bool failable = try_consume(context, TOKEN_BANG); + ASSIGN_TYPE_ELSE(TypeInfo *type, parse_failable_type(context), poisoned_decl); ASSIGN_DECL_ELSE(Decl *decl, parse_decl_after_type(context, type), poisoned_decl); - if (failable && decl->var.unwrap) + if (type->failable && decl->var.unwrap) { SEMA_ERROR(decl, "You cannot use unwrap with a failable variable."); return poisoned_decl; } - decl->var.failable = failable; decl->var.is_static = is_static; return decl; } @@ -1004,14 +1018,10 @@ bool parse_attributes(Context *context, Attr ***attributes_ref) */ static inline Decl *parse_global_declaration(Context *context, Visibility visibility) { - ASSIGN_TYPE_ELSE(TypeInfo *type, parse_type(context), poisoned_decl); - - bool failable = try_consume(context, TOKEN_BANG); + ASSIGN_TYPE_ELSE(TypeInfo *type, parse_failable_type(context), poisoned_decl); Decl *decl = decl_new_var(context->tok.id, type, VARDECL_GLOBAL, visibility); - decl->var.failable = failable; - if (TOKEN_IS(TOKEN_CONST_IDENT)) { SEMA_TOKEN_ERROR(context->tok, "This looks like a constant variable, did you forget 'const'?"); @@ -1533,12 +1543,8 @@ static inline Decl *parse_define_type(Context *context, Visibility visibility) decl->span.loc = start; decl->typedef_decl.is_func = true; decl->typedef_decl.is_distinct = distinct; - ASSIGN_TYPE_ELSE(TypeInfo *type_info, parse_type(context), poisoned_decl); + ASSIGN_TYPE_ELSE(TypeInfo *type_info, parse_failable_type(context), poisoned_decl); decl->typedef_decl.function_signature.rtype = type_info; - if (try_consume(context, TOKEN_BANG)) - { - decl->typedef_decl.function_signature.failable = true; - } if (!parse_parameter_list(context, decl->visibility, &(decl->typedef_decl.function_signature), true)) { return poisoned_decl; @@ -1684,11 +1690,12 @@ static inline Decl *parse_define(Context *context, Visibility visibility) * func_header ::= type '!'? (type '.')? IDENT * macro_header ::= (type '!'?)? (type '.')? IDENT */ -static inline bool parse_func_macro_header(Context *context, bool rtype_is_optional, TypeInfo **rtype_ref, bool *failable_ref, TypeInfo **method_type_ref, TokenId *name_ref) +static inline bool +parse_func_macro_header(Context *context, bool rtype_is_optional, TypeInfo **rtype_ref, TypeInfo **method_type_ref, + TokenId *name_ref) { TypeInfo *rtype = NULL; TypeInfo *method_type = NULL; - bool failable = false; // 1. We have a macro with just a name, if so, then we set the name and we're done. if (rtype_is_optional && !context_next_is_type_and_not_ident(context)) @@ -1697,10 +1704,7 @@ static inline bool parse_func_macro_header(Context *context, bool rtype_is_optio } // 2. Now we must have a type - either that is the return type or the method type. - ASSIGN_TYPE_ELSE(rtype, parse_type(context), false); - - // 3. We possibly have a failable return at this point. - failable = try_consume(context, TOKEN_BANG); + ASSIGN_TYPE_ELSE(rtype, parse_failable_type(context), false); // 4. We might have a type here, if so then we read it. if (!TOKEN_IS(TOKEN_DOT) && context_next_is_type_and_not_ident(context)) @@ -1715,7 +1719,7 @@ static inline bool parse_func_macro_header(Context *context, bool rtype_is_optio if (!method_type) { // 5b. If the rtype is not optional or the return type was a failable, then this is an error. - if (!rtype_is_optional || failable) + if (!rtype_is_optional || rtype->failable) { SEMA_TOKID_ERROR(context->prev_tok, "This looks like you are declaring a method without a return type?"); @@ -1733,7 +1737,6 @@ static inline bool parse_func_macro_header(Context *context, bool rtype_is_optio } RESULT: TRY_CONSUME_OR(TOKEN_IDENT, "Expected a name here.", false); - *failable_ref = failable; *name_ref = context->prev_tok; *rtype_ref = rtype; *method_type_ref = method_type; @@ -1752,11 +1755,9 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil Decl *decl = decl_new(kind, context->tok.id, visibility); TypeInfo **rtype_ref = &decl->macro_decl.rtype; TypeInfo **method_type_ref = &decl->macro_decl.type_parent; - bool failable; TokenId name; - if (!parse_func_macro_header(context, true, rtype_ref, &failable, method_type_ref, &name)) return poisoned_decl; + if (!parse_func_macro_header(context, true, rtype_ref, method_type_ref, &name)) return poisoned_decl; - decl->macro_decl.failable = failable; decl->name = TOKSTR(name); decl->name_token = name; @@ -1965,10 +1966,8 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit advance_and_verify(context, TOKEN_FUNC); TypeInfo **rtype_ref = &func->func_decl.function_signature.rtype; TypeInfo **method_type_ref = &func->func_decl.type_parent; - bool failable; TokenId name; - if (!parse_func_macro_header(context, false, rtype_ref, &failable, method_type_ref, &name)) return poisoned_decl; - func->func_decl.function_signature.failable = failable; + if (!parse_func_macro_header(context, false, rtype_ref, method_type_ref, &name)) return poisoned_decl; func->name = TOKSTR(name); func->name_token = name; RANGE_EXTEND_PREV(func); @@ -2396,4 +2395,5 @@ Decl *parse_top_level_statement(Context *context) assert(decl); decl->docs = docs; return decl; -} \ No newline at end of file +} + diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 32f4d4240..4cf0ecc3b 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -439,9 +439,8 @@ static inline bool parse_foreach_var(Context *context, Ast *foreach) // If we don't get foreach (foo ... or foreach (*foo ... then a type is expected. if (!TOKEN_IS(TOKEN_IDENT) && !TOKEN_IS(TOKEN_AMP)) { - ASSIGN_TYPE_ELSE(type, parse_type(context), false); + ASSIGN_TYPE_ELSE(type, parse_failable_type(context), false); - failable = try_consume(context, TOKEN_BANG); // Add the failable to the type for nicer error reporting. RANGE_EXTEND_PREV(type); } @@ -460,7 +459,6 @@ static inline bool parse_foreach_var(Context *context, Ast *foreach) return false; } Decl *var = decl_new_var(context->prev_tok, type, VARDECL_LOCAL, VISIBLE_LOCAL); - var->var.failable = failable; foreach->foreach_stmt.variable = var; return true; } @@ -586,14 +584,13 @@ static inline Ast *parse_decl_or_expr_stmt(Context *context) // If so we need to unwrap this. if (expr->expr_kind == EXPR_FAILABLE && expr->failable_expr->expr_kind == EXPR_TYPEINFO) { - failable = true; + UNREACHABLE expr_replace(expr, expr->failable_expr); } if (expr->expr_kind == EXPR_TYPEINFO) { ast->ast_kind = AST_DECLARE_STMT; ASSIGN_DECL_ELSE(ast->declare_stmt, parse_decl_after_type(context, expr->type_expr), poisoned_ast); - ast->declare_stmt->var.failable = failable; } else { @@ -612,7 +609,7 @@ static inline Ast *parse_decl_or_expr_stmt(Context *context) */ static inline Ast *parse_var_stmt(Context *context) { - Ast *ast = AST_NEW_TOKEN(AST_DEFINE_STMT, context->tok); + Ast *ast = AST_NEW_TOKEN(AST_VAR_STMT, context->tok); TokenId start = context->tok.id; advance_and_verify(context, TOKEN_VAR); Decl *decl; @@ -631,7 +628,7 @@ static inline Ast *parse_var_stmt(Context *context) advance(context); if (try_consume(context, TOKEN_EQ)) { - ASSIGN_TYPE_ELSE(decl->var.type_info, parse_type(context), poisoned_ast); + ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_ast); } break; default: diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 31a44ff31..1d4a2fc0d 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -37,6 +37,7 @@ Expr *parse_expr(Context *context); bool consume_ident(Context *context, const char* name); Expr *parse_try_expr_after_try(Context *context, bool is_try); TypeInfo *parse_type(Context *context); +TypeInfo *parse_failable_type(Context *context); TypeInfo *parse_type_with_base(Context *context, TypeInfo *type_info); Expr* parse_constant_expr(Context *context); Expr *parse_initializer(Context *context); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index fc914fbe6..86a552aaa 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -17,7 +17,7 @@ static inline bool insert_cast(Expr *expr, CastKind kind, Type *type) expr->cast_expr.kind = kind; expr->cast_expr.expr = inner; expr->cast_expr.type_info = NULL; - expr_set_type(expr, type); + expr->type = type; return true; } @@ -34,7 +34,8 @@ bool pointer_to_integer(Expr *expr, Type *type) // Must have been a null expr_const_set_int(&expr->const_expr, 0, TYPE_POINTER); - expr_set_type(expr, type); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -44,7 +45,8 @@ bool pointer_to_bool(Expr *expr, Type *type) // Must have been a null expr->const_expr.b = false; - expr_set_type(expr, type); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -54,7 +56,8 @@ bool pointer_to_pointer(Expr* expr, Type *type) if (insert_runtime_cast_unless_const(expr, CAST_PTRPTR, type)) return true; // Must have been a null - expr_set_type(expr, type); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -69,22 +72,22 @@ bool string_literal_to_subarray(Expr* left, Type *type) static void const_int_to_fp_cast(Expr *expr, Type *canonical, Type *type) { - Real f = bigint_as_float(&expr->const_expr.i); + Real f = int_to_real(expr->const_expr.ixx); switch (canonical->type_kind) { case TYPE_F32: - expr->const_expr.f = (float)f; + expr->const_expr.fxx = (Float) { (float)f, TYPE_F32 }; break; case TYPE_F64: - expr->const_expr.f = (double)f; + expr->const_expr.fxx = (Float) { (double)f, TYPE_F64 }; break; default: - expr->const_expr.f = f; + expr->const_expr.fxx = (Float) { f, canonical->type_kind }; break; } - expr_set_type(expr, type); - expr->const_expr.float_type = canonical->type_kind; + expr->type = type; expr->const_expr.const_kind = CONST_FLOAT; + expr->const_expr.narrowable = false; } @@ -95,7 +98,8 @@ bool bool_to_int(Expr *expr, Type *canonical, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_BOOLINT, type)) return true; expr_const_set_int(&expr->const_expr, expr->const_expr.b ? 1 : 0, canonical->type_kind); - expr_set_type(expr, type); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -109,7 +113,8 @@ bool bool_to_float(Expr *expr, Type *canonical, Type *type) assert(expr->const_expr.const_kind == CONST_BOOL); expr_const_set_float(&expr->const_expr, expr->const_expr.b ? 1.0 : 0.0, canonical->type_kind); - expr_set_type(expr, type); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -121,8 +126,9 @@ bool integer_to_bool(Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_INTBOOL, type)) return true; - expr_const_set_bool(&expr->const_expr, bigint_cmp_zero(&expr->const_expr.i) != CMP_EQ); - expr_set_type(expr, type); + expr_const_set_bool(&expr->const_expr, !int_is_zero(expr->const_expr.ixx)); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -133,8 +139,9 @@ bool float_to_bool(Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_FPBOOL, type)) return true; - expr_const_set_bool(&expr->const_expr, expr->const_expr.f != 0.0); - expr_set_type(expr, type); + expr_const_set_bool(&expr->const_expr, expr->const_expr.fxx.f != 0.0); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -146,8 +153,9 @@ static bool float_to_float(Expr* expr, Type *canonical, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_FPFP, type)) return true; - expr_const_set_float(&expr->const_expr, expr->const_expr.f, canonical->type_kind); - expr_set_type(expr, type); + expr_const_set_float(&expr->const_expr, expr->const_expr.fxx.f, canonical->type_kind); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -159,22 +167,12 @@ bool float_to_integer(Expr *expr, Type *canonical, Type *type) bool is_signed = type_is_unsigned(canonical); if (insert_runtime_cast_unless_const(expr, is_signed ? CAST_FPSI : CAST_FPUI, type)) return true; - assert(canonical->type_kind >= TYPE_I8 && canonical->type_kind < TYPE_IXX); - Real d = expr->const_expr.f; - BigInt temp; - if (is_signed) - { - bigint_init_signed(&temp, (int64_t)d); - bigint_truncate(&expr->const_expr.i, &temp, canonical->builtin.bitsize, true); - } - else - { - bigint_init_unsigned(&temp, (uint64_t)d); - bigint_truncate(&expr->const_expr.i, &temp, canonical->builtin.bitsize, false); - } - expr->const_expr.int_type = canonical->type_kind; + assert(type_is_integer(canonical)); + Real d = expr->const_expr.fxx.f; + expr->const_expr.ixx = int_from_real(d, canonical->type_kind); expr->const_expr.const_kind = CONST_INTEGER; - expr_set_type(expr, type); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -190,13 +188,10 @@ static bool int_literal_to_int(Expr *expr, Type *canonical, Type *type) SEMA_ERROR(expr, "This expression could not be resolved to a concrete type. Please add more type annotations."); UNREACHABLE } - bool is_signed = canonical->type_kind < TYPE_U8; - BigInt temp; - bigint_truncate(&temp, &expr->const_expr.i, canonical->builtin.bitsize, is_signed); - expr->const_expr.i = temp; - expr->const_expr.int_type = canonical->type_kind; + expr->const_expr.ixx = int_conv(expr->const_expr.ixx, canonical->type_kind); assert(expr->const_expr.const_kind == CONST_INTEGER); - expr_set_type(expr, type); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -215,12 +210,10 @@ static bool int_conversion(Expr *expr, CastKind kind, Type *canonical, Type *typ { if (insert_runtime_cast_unless_const(expr, kind, type)) return true; - BigInt temp; - bigint_truncate(&temp, &expr->const_expr.i, canonical->builtin.bitsize, kind == CAST_UISI || kind == CAST_SISI); - expr->const_expr.i = temp; - expr->const_expr.int_type = canonical->type_kind; + expr->const_expr.ixx = int_conv(expr->const_expr.ixx, canonical->type_kind); expr->const_expr.const_kind = CONST_INTEGER; - expr_set_type(expr, type); + expr->type = type; + expr->const_expr.narrowable = false; return true; } @@ -250,8 +243,9 @@ static bool int_literal_to_float(Expr *expr, Type *canonical, Type *type) static bool int_literal_to_bool(Expr *expr, Type *type) { assert(expr->expr_kind == EXPR_CONST); - expr_const_set_bool(&expr->const_expr, bigint_cmp_zero(&expr->const_expr.i) != CMP_EQ); - expr_set_type(expr, type); + expr_const_set_bool(&expr->const_expr, !int_is_zero(expr->const_expr.ixx)); + expr->const_expr.narrowable = false; + expr->type = type; return true; } @@ -263,10 +257,10 @@ static bool int_to_pointer(Expr *expr, Type *type) { if (expr->expr_kind == EXPR_CONST) { - if (bigint_cmp_zero(&expr->const_expr.i) == CMP_EQ) + if (int_is_zero(expr->const_expr.ixx)) { expr_const_set_null(&expr->const_expr); - expr_set_type(expr, type); + expr->type = type; return true; } } @@ -280,8 +274,6 @@ static bool int_to_int(Expr *left, Type *from_canonical, Type *canonical, Type * assert(from_canonical->canonical == from_canonical); switch (from_canonical->type_kind) { - case TYPE_IXX: - return int_literal_to_int(left, canonical, type); case ALL_SIGNED_INTS: return int_conversion(left, type_is_unsigned(canonical) ? CAST_SIUI : CAST_SISI, canonical, type); case ALL_UNSIGNED_INTS: @@ -298,7 +290,7 @@ static bool enum_to_integer(Expr* expr, Type *from, Type *canonical, Type *type) // 1. If the underlying type is the same, this is just setting the type. if (canonical == enum_type_canonical) { - expr_set_type(expr, type); + expr->type = type; return true; } // 2. Dispatch to the right cast: @@ -334,45 +326,6 @@ bool enum_to_pointer(Expr* expr, Type *from, Type *type) return int_to_pointer(expr, type); } -Type *type_by_expr_range(ExprConst *expr) -{ - if (expr->const_kind == CONST_FLOAT && expr->float_type == TYPE_FXX) - { - return type_double; - } - assert(expr->const_kind == CONST_INTEGER && expr->int_type == TYPE_IXX); - // 1. Does it fit in a C int? If so, that's the type. - Type *type = type_cint(); - if (!expr_const_will_overflow(expr, type->type_kind)) return type; - - int width_max = platform_target.int128 ? 128 : 64; - int current_width = platform_target.width_c_int * 2; - while (current_width <= width_max) - { - type = type_int_signed_by_bitsize(current_width); - if (!expr_const_will_overflow(expr, type->type_kind)) return type; - type = type_int_unsigned_by_bitsize(current_width); - if (!expr_const_will_overflow(expr, type->type_kind)) return type; - current_width *= width_max; - } - return NULL; -} - -bool cast_implicitly_to_runtime(Expr *expr) -{ - Type *canonical = expr->type->canonical; - Type *type; - switch (canonical->type_kind) - { - case TYPE_IXX: - case TYPE_FXX: - type = type_by_expr_range(&expr->const_expr); - return type && cast(expr, type); - default: - return true; - } -} - CastKind cast_to_bool_kind(Type *type) { @@ -412,6 +365,7 @@ CastKind cast_to_bool_kind(Type *type) case TYPE_VECTOR: case TYPE_BITSTRUCT: case TYPE_UNTYPED_LIST: + case TYPE_FAILABLE: return CAST_ERROR; } UNREACHABLE @@ -419,9 +373,21 @@ CastKind cast_to_bool_kind(Type *type) bool cast_may_explicit(Type *from_type, Type *to_type) { + Type *from = from_type; + Type *to = to_type; + + if (from_type->type_kind == TYPE_FAILABLE && to_type->type_kind != TYPE_FAILABLE) + { + return false; + } + if (to_type->type_kind == TYPE_FAILABLE) + { + return cast_may_explicit(type_no_fail(from_type), to_type->failable); + } + // 1. We flatten the distinct types, since they should be freely convertible - Type *from = type_flatten_distinct(from_type); - Type *to = type_flatten_distinct(to_type); + from = type_flatten_distinct(from); + to = type_flatten_distinct(to); // 2. Same underlying type, always ok if (from == to) return true; @@ -429,13 +395,15 @@ bool cast_may_explicit(Type *from_type, Type *to_type) TypeKind to_kind = to->type_kind; switch (from->type_kind) { + case TYPE_DISTINCT: + case TYPE_TYPEDEF: + case TYPE_FAILABLE: + UNREACHABLE case TYPE_POISONED: case TYPE_INFERRED_ARRAY: case TYPE_VOID: case TYPE_TYPEINFO: - case TYPE_DISTINCT: case TYPE_FUNC: - case TYPE_TYPEDEF: return false; case TYPE_TYPEID: // May convert to anything pointer sized or larger, no enums @@ -448,18 +416,17 @@ bool cast_may_explicit(Type *from_type, Type *to_type) case TYPE_ANYERR: // May convert to a bool, an error type or an integer return to == type_bool || to_kind == TYPE_ERRTYPE || type_is_integer(to); - case TYPE_IXX: case ALL_SIGNED_INTS: case ALL_UNSIGNED_INTS: case TYPE_ENUM: // Allow conversion int/enum -> float/bool/enum int/enum -> pointer is only allowed if the int/enum is pointer sized. if (type_is_integer(to) || type_is_float(to) || to == type_bool || to_kind == TYPE_ENUM) return true; // TODO think about this, maybe we should require a bitcast? - if (to_kind == TYPE_POINTER && (from == type_compint || type_is_pointer_sized(from))) return true; + if (to_kind == TYPE_POINTER && type_is_pointer_sized(from)) return true; return false; case ALL_FLOATS: // Allow conversion float -> float/int/bool/enum - return type_is_any_integer(to) || type_is_float(to) || to == type_bool || to_kind == TYPE_ENUM; + return type_is_integer(to) || type_is_float(to) || to == type_bool || to_kind == TYPE_ENUM; case TYPE_POINTER: // Allow conversion ptr -> int (min pointer size)/bool/pointer/vararray if ((type_is_integer(to) && type_size(to) >= type_size(type_iptr)) || to == type_bool || to_kind == TYPE_POINTER) return true; @@ -471,7 +438,7 @@ bool cast_may_explicit(Type *from_type, Type *to_type) return to_kind == TYPE_POINTER; case TYPE_ERRTYPE: // Allow MyError.A -> error, to an integer or to bool - return to->type_kind == TYPE_ANYERR || type_is_any_integer(to) || to == type_bool; + return to->type_kind == TYPE_ANYERR || type_is_integer(to) || to == type_bool; case TYPE_ARRAY: if (to_kind == TYPE_VECTOR) { @@ -543,17 +510,28 @@ static bool may_cast_to_virtual(Type *virtual, Type *from) */ bool cast_may_implicit(Type *from_type, Type *to_type) { + if (from_type == type_anyfail) + { + return to_type->type_kind == TYPE_FAILABLE; + } + Type *from = from_type->canonical; Type *to = to_type->canonical; // 1. Same canonical type - we're fine. if (from == to) return true; + if (from->type_kind == TYPE_FAILABLE && to->type_kind != TYPE_FAILABLE) return false; + if (to->type_kind == TYPE_FAILABLE) + { + return cast_may_implicit(type_no_fail(from), type_no_fail(to)); + } + // 2. Handle floats if (type_is_float(to)) { // 2a. Any integer may convert to a float. - if (type_is_any_integer(from)) return true; + if (type_is_integer(from)) return true; // 2b. Any narrower float or FXX may convert to a float. if (type_is_float(from)) @@ -569,9 +547,6 @@ bool cast_may_implicit(Type *from_type, Type *to_type) // 3. Handle ints if (type_is_integer(to)) { - // TODO, consider size here, and maybe the type should b removed. - if (from->type_kind == TYPE_IXX) return true; - // For an enum, lower to the underlying enum type. if (from->type_kind == TYPE_ENUM) { @@ -643,7 +618,7 @@ bool cast_may_implicit(Type *from_type, Type *to_type) // 7. In the case of distinct types, we allow implicit conversion from literal types. if (to->type_kind == TYPE_DISTINCT) { - if (from->type_kind == TYPE_STRLIT || from->type_kind == TYPE_FXX || from->type_kind == TYPE_IXX) + if (from->type_kind == TYPE_STRLIT) { return cast_may_implicit(from, type_flatten(to)); } @@ -701,73 +676,472 @@ bool may_convert_float_const_implicit(Expr *expr, Type *to_type) default: UNREACHABLE } - if (expr->const_expr.f < -lo_limit || expr->const_expr.f > hi_limit) + if (expr->const_expr.fxx.f < -lo_limit || expr->const_expr.fxx.f > hi_limit) { #if LONG_DOUBLE - SEMA_ERROR(expr, "The value '%Lg' is out of range for %s, so you need an explicit cast to truncate the value.", expr->const_expr.f, type_quoted_error_string(to_type)); + SEMA_ERROR(expr, "The value '%Lg' is out of range for %s, so you need an explicit cast to truncate the value.", expr->const_expr.fxx.f, type_quoted_error_string(to_type)); #else - SEMA_ERROR(expr, "The value '%g' is out of range for %s, so you need an explicit cast to truncate the value.", expr->const_expr.f, type_quoted_error_string(to_type)); + SEMA_ERROR(expr, "The value '%g' is out of range for %s, so you need an explicit cast to truncate the value.", expr->const_expr.fxx.f, type_quoted_error_string(to_type)); #endif return false; } return true; } +bool float_const_fits_type(Expr *expr, Type *to_type) +{ + Type *to_type_flat = type_flatten(to_type); + Real hi_limit; + Real lo_limit; + switch (to_type_flat->type_kind) + { + case TYPE_F16: + lo_limit = hi_limit = FLOAT16_LIMIT; + break; + case TYPE_F32: + lo_limit = hi_limit = FLOAT32_LIMIT; + break; + case TYPE_F64: + lo_limit = hi_limit = FLOAT64_LIMIT; + break; + case TYPE_F128: + // Assume this to be true + return true; + case TYPE_BOOL: + return true; + default: + UNREACHABLE + } + return expr->const_expr.fxx.f >= -lo_limit && expr->const_expr.fxx.f <= hi_limit; +} + bool may_convert_int_const_implicit(Expr *expr, Type *to_type) { Type *to_type_flat = type_flatten(to_type); if (expr_const_will_overflow(&expr->const_expr, to_type_flat->type_kind)) { - SEMA_ERROR(expr, "The value '%s' is out of range for %s, it can be truncated with an explicit cast.", bigint_to_error_string(&expr->const_expr.i, 10), type_quoted_error_string(to_type)); + SEMA_ERROR(expr, "The value '%s' is out of range for %s, it can be truncated with an explicit cast.", + int_to_str(expr->const_expr.ixx, 10), type_quoted_error_string(to_type)); return false; } return true; } -bool may_convert_const_implicit(Expr *expr, Type *to_type) +Expr *recursive_may_narrow_float(Expr *expr, Type *type) { - switch (expr->type->canonical->type_kind) + switch (expr->expr_kind) { - case TYPE_FXX: - return may_convert_float_const_implicit(expr, to_type); - case TYPE_IXX: - return may_convert_int_const_implicit(expr, to_type); - default: + case EXPR_BINARY: + switch (expr->binary_expr.operator) + { + case BINARYOP_ERROR: + UNREACHABLE + case BINARYOP_MULT: + case BINARYOP_SUB: + case BINARYOP_ADD: + case BINARYOP_DIV: + case BINARYOP_MOD: + { + Expr *res = recursive_may_narrow_float(expr->binary_expr.left, type); + if (res) return res; + return recursive_may_narrow_float(expr->binary_expr.right, type); + } + case BINARYOP_BIT_OR: + case BINARYOP_BIT_XOR: + case BINARYOP_BIT_AND: + case BINARYOP_AND: + case BINARYOP_OR: + case BINARYOP_GT: + case BINARYOP_GE: + case BINARYOP_LT: + case BINARYOP_LE: + case BINARYOP_NE: + case BINARYOP_EQ: + case BINARYOP_SHR: + case BINARYOP_SHL: + case BINARYOP_BIT_AND_ASSIGN: + case BINARYOP_BIT_OR_ASSIGN: + case BINARYOP_BIT_XOR_ASSIGN: + case BINARYOP_SHR_ASSIGN: + case BINARYOP_SHL_ASSIGN: + UNREACHABLE + case BINARYOP_ASSIGN: + case BINARYOP_ADD_ASSIGN: + case BINARYOP_DIV_ASSIGN: + case BINARYOP_MOD_ASSIGN: + case BINARYOP_MULT_ASSIGN: + case BINARYOP_SUB_ASSIGN: + return recursive_may_narrow_float(expr->binary_expr.left, type); + } UNREACHABLE - + case EXPR_MACRO_BODY_EXPANSION: + case EXPR_CALL: + case EXPR_POISONED: + case EXPR_ACCESS: + case EXPR_CATCH_UNWRAP: + case EXPR_COMPOUND_LITERAL: + case EXPR_COND: + case EXPR_DECL: + case EXPR_CT_IDENT: + case EXPR_DESIGNATOR: + case EXPR_EXPR_BLOCK: + case EXPR_MACRO_BLOCK: + case EXPR_MACRO_EXPANSION: + case EXPR_IDENTIFIER: + case EXPR_SLICE_ASSIGN: + case EXPR_SLICE: + case EXPR_SUBSCRIPT: + if (type_size(expr->type) > type_size(type)) return expr; + return NULL; + case EXPR_ELSE: + { + Expr *res = recursive_may_narrow_float(expr->else_expr.expr, type); + if (res) return res; + if (expr->else_expr.is_jump) return NULL; + return recursive_may_narrow_float(expr->else_expr.else_expr, type); + } + case EXPR_EXPRESSION_LIST: + return recursive_may_narrow_float(VECLAST(expr->expression_list), type); + case EXPR_GROUP: + return recursive_may_narrow_float(expr->group_expr, type); + case EXPR_GUARD: + return recursive_may_narrow_float(expr->guard_expr.inner, type); + case EXPR_TERNARY: + { + Expr *res = recursive_may_narrow_float(expr->ternary_expr.then_expr ? expr->ternary_expr.then_expr + : expr->ternary_expr.cond, type); + if (res) return res; + return recursive_may_narrow_float(expr->ternary_expr.else_expr, type); + } + case EXPR_CAST: + if (expr->cast_expr.implicit) + { + return recursive_may_narrow_float(expr->cast_expr.expr, type); + } + return type_size(expr->type) > type_size(type) ? expr : NULL; + case EXPR_CONST: + if (!expr->const_expr.narrowable) + { + return type_size(expr->type) > type_size(type) ? expr : NULL; + } + assert(expr->const_expr.const_kind == CONST_FLOAT); + if (!float_const_fits_type(expr, type_flatten(type))) + { + return expr; + } + return NULL; + case EXPR_CONST_IDENTIFIER: + return type_size(expr->type) > type_size(type) ? expr : NULL; + case EXPR_FAILABLE: + case EXPR_HASH_IDENT: + case EXPR_FLATPATH: + case EXPR_INITIALIZER_LIST: + case EXPR_DESIGNATED_INITIALIZER_LIST: + case EXPR_PLACEHOLDER: + case EXPR_TYPEID: + case EXPR_TYPEINFO: + case EXPR_TYPEOF: + case EXPR_UNDEF: + case EXPR_CT_CALL: + case EXPR_NOP: + case EXPR_LEN: + UNREACHABLE + case EXPR_POST_UNARY: + return recursive_may_narrow_float(expr->unary_expr.expr, type); + case EXPR_SCOPED_EXPR: + return recursive_may_narrow_float(expr->expr_scope.expr, type); + case EXPR_TRY: + assert(expr->try_expr.is_try); + return recursive_may_narrow_float(expr->try_expr.expr, type); + case EXPR_TRY_UNWRAP: + TODO + case EXPR_TRY_UNWRAP_CHAIN: + TODO + case EXPR_TRY_ASSIGN: + return recursive_may_narrow_float(expr->try_assign_expr.expr, type); + case EXPR_UNARY: + { + switch (expr->unary_expr.operator) + { + case UNARYOP_ERROR: + case UNARYOP_DEREF: + case UNARYOP_ADDR: + case UNARYOP_NOT: + case UNARYOP_TADDR: + UNREACHABLE + case UNARYOP_NEG: + case UNARYOP_BITNEG: + case UNARYOP_INC: + case UNARYOP_DEC: + return recursive_may_narrow_float(expr->unary_expr.expr, type); + } + } } + UNREACHABLE } + +Expr *recursive_may_narrow_int(Expr *expr, Type *type) +{ + switch (expr->expr_kind) + { + case EXPR_BINARY: + switch (expr->binary_expr.operator) + { + case BINARYOP_ERROR: + UNREACHABLE + case BINARYOP_MULT: + case BINARYOP_SUB: + case BINARYOP_ADD: + case BINARYOP_DIV: + case BINARYOP_MOD: + case BINARYOP_BIT_OR: + case BINARYOP_BIT_XOR: + case BINARYOP_BIT_AND: + { + Expr *res = recursive_may_narrow_int(expr->binary_expr.left, type); + if (res) return res; + return recursive_may_narrow_int(expr->binary_expr.right, type); + } + case BINARYOP_AND: + case BINARYOP_OR: + case BINARYOP_GT: + case BINARYOP_GE: + case BINARYOP_LT: + case BINARYOP_LE: + case BINARYOP_NE: + case BINARYOP_EQ: + return NULL; + case BINARYOP_SHR: + case BINARYOP_SHL: + case BINARYOP_ASSIGN: + case BINARYOP_ADD_ASSIGN: + case BINARYOP_BIT_AND_ASSIGN: + case BINARYOP_BIT_OR_ASSIGN: + case BINARYOP_BIT_XOR_ASSIGN: + case BINARYOP_DIV_ASSIGN: + case BINARYOP_MOD_ASSIGN: + case BINARYOP_MULT_ASSIGN: + case BINARYOP_SHR_ASSIGN: + case BINARYOP_SHL_ASSIGN: + case BINARYOP_SUB_ASSIGN: + return recursive_may_narrow_int(expr->binary_expr.left, type); + } + UNREACHABLE + case EXPR_MACRO_BODY_EXPANSION: + case EXPR_CALL: + case EXPR_POISONED: + case EXPR_ACCESS: + case EXPR_CATCH_UNWRAP: + case EXPR_COMPOUND_LITERAL: + case EXPR_COND: + case EXPR_DECL: + case EXPR_CT_IDENT: + case EXPR_DESIGNATOR: + case EXPR_EXPR_BLOCK: + case EXPR_MACRO_BLOCK: + case EXPR_MACRO_EXPANSION: + case EXPR_IDENTIFIER: + case EXPR_SLICE_ASSIGN: + case EXPR_SLICE: + case EXPR_SUBSCRIPT: + if (type_size(expr->type) > type_size(type)) return expr; + return NULL; + case EXPR_LEN: + if (type_size(type) < type_size(type_cint())) return expr; + return NULL; + case EXPR_ELSE: + { + Expr *res = recursive_may_narrow_int(expr->else_expr.expr, type); + if (res) return res; + if (expr->else_expr.is_jump) return NULL; + return recursive_may_narrow_int(expr->else_expr.else_expr, type); + } + case EXPR_EXPRESSION_LIST: + return recursive_may_narrow_int(VECLAST(expr->expression_list), type); + case EXPR_GROUP: + return recursive_may_narrow_int(expr->group_expr, type); + case EXPR_GUARD: + return recursive_may_narrow_int(expr->guard_expr.inner, type); + case EXPR_TERNARY: + { + Expr *res = recursive_may_narrow_int(expr->ternary_expr.then_expr ? expr->ternary_expr.then_expr + : expr->ternary_expr.cond, type); + if (res) return res; + return recursive_may_narrow_int(expr->ternary_expr.else_expr, type); + } + case EXPR_CAST: + if (expr->cast_expr.implicit) + { + return recursive_may_narrow_int(expr->cast_expr.expr, type); + } + return type_size(expr->type) > type_size(type) ? expr : NULL; + case EXPR_CONST: + if (!expr->const_expr.narrowable) + { + return type_size(expr->type) > type_size(type) ? expr : NULL; + } + assert(expr->const_expr.const_kind == CONST_INTEGER); + if (expr_const_will_overflow(&expr->const_expr, type_flatten(type)->type_kind)) + { + return expr; + } + return NULL; + case EXPR_CONST_IDENTIFIER: + return type_size(expr->type) > type_size(type) ? expr : NULL; + case EXPR_FAILABLE: + case EXPR_HASH_IDENT: + case EXPR_FLATPATH: + case EXPR_INITIALIZER_LIST: + case EXPR_DESIGNATED_INITIALIZER_LIST: + case EXPR_PLACEHOLDER: + case EXPR_TYPEID: + case EXPR_TYPEINFO: + case EXPR_TYPEOF: + case EXPR_UNDEF: + case EXPR_CT_CALL: + case EXPR_NOP: + UNREACHABLE + case EXPR_POST_UNARY: + return recursive_may_narrow_int(expr->unary_expr.expr, type); + case EXPR_SCOPED_EXPR: + return recursive_may_narrow_int(expr->expr_scope.expr, type); + case EXPR_TRY: + assert(expr->try_expr.is_try); + return recursive_may_narrow_int(expr->try_expr.expr, type); + case EXPR_TRY_UNWRAP: + TODO + case EXPR_TRY_UNWRAP_CHAIN: + TODO + case EXPR_TRY_ASSIGN: + return recursive_may_narrow_int(expr->try_assign_expr.expr, type); + case EXPR_UNARY: + { + switch (expr->unary_expr.operator) + { + case UNARYOP_ERROR: + case UNARYOP_DEREF: + case UNARYOP_ADDR: + case UNARYOP_NOT: + case UNARYOP_TADDR: + UNREACHABLE + case UNARYOP_NEG: + case UNARYOP_BITNEG: + case UNARYOP_INC: + case UNARYOP_DEC: + return recursive_may_narrow_int(expr->unary_expr.expr, type); + } + } + } + UNREACHABLE +} +bool cast_implicit_ignore_failable(Expr *expr, Type *to_type) +{ + if (expr->type->type_kind == TYPE_FAILABLE && to_type->type_kind != TYPE_FAILABLE) + { + to_type = type_get_failable(to_type); + } + return cast_implicit(expr, to_type); +} + bool cast_implicit(Expr *expr, Type *to_type) { - assert(expr->original_type); - if (expr->type == to_type) return true; - if (!cast_may_implicit(expr->original_type, to_type) && !cast_may_implicit(expr->type->canonical, to_type)) + Type *expr_type = expr->type; + if (expr_type == type_anyfail) { - if (cast_may_explicit(expr->original_type, to_type) || cast_may_implicit(expr->type->canonical, to_type)) + if (to_type->type_kind != TYPE_FAILABLE) { - SEMA_ERROR(expr, "Implicitly casting %s to %s is not permitted, but you can do an explicit cast using '()(value)'.", type_quoted_error_string(expr->original_type), type_quoted_error_string(to_type)); + SEMA_ERROR(expr, "Cannot cast %s to a non-failable type %s.", type_quoted_error_string(expr_type), + type_quoted_error_string(to_type)); + return false; } - else + expr->type = to_type; + return true; + } + Type *expr_canonical = expr_type->canonical; + Type *to_canonical = to_type->canonical; + if (expr_canonical == to_canonical) return true; + if (!cast_may_implicit(expr_canonical, to_canonical)) + { + if (!cast_may_explicit(expr_canonical, to_canonical)) { - SEMA_ERROR(expr, "You cannot cast anything %s into %s even with an explicit cast, so this looks like an error.", type_quoted_error_string(expr->original_type), type_quoted_error_string(to_type)); + if (expr_canonical->type_kind == TYPE_FAILABLE && to_canonical->type_kind != TYPE_FAILABLE) + { + SEMA_ERROR(expr, "A failable %s cannot be converted to %s.", type_quoted_error_string(expr->type), type_quoted_error_string(to_type)); + return false; + } + SEMA_ERROR(expr, "You cannot cast %s into %s even with an explicit cast, so this looks like an error.", type_quoted_error_string(expr->type), type_quoted_error_string(to_type)); + return false; } + if (expr->expr_kind == EXPR_CONST && expr->const_expr.narrowable) + { + Type *expr_flatten = type_flatten_distinct(expr_canonical); + Type *to_flatten = type_flatten_distinct(to_canonical); + if (type_is_integer(expr_flatten) && type_is_integer(to_flatten)) + { + Expr *problem = recursive_may_narrow_int(expr, to_canonical); + if (problem) + { + SEMA_ERROR(problem, "The value '%s' is out of range for %s, so you need an explicit cast to truncate the value.", expr_const_to_error_string(&expr->const_expr), + type_quoted_error_string(to_type)); + return false; + } + goto OK; + } + if (type_is_float(expr_flatten) && type_is_float(to_flatten)) + { + Expr *problem = recursive_may_narrow_float(expr, to_canonical); + if (problem) + { + SEMA_ERROR(problem, "The value '%s' is out of range for %s, so you need an explicit cast to truncate the value.", expr_const_to_error_string(&expr->const_expr), + type_quoted_error_string(to_type)); + return false; + } + goto OK; + } + } + if (type_is_integer(expr_canonical) && type_is_integer(to_canonical)) + { + assert(type_size(expr_canonical) > type_size(to_canonical)); + Expr *problem = recursive_may_narrow_int(expr, to_canonical); + if (problem) + { + SEMA_ERROR(problem, "Cannot narrow %s to %s.", type_quoted_error_string(problem->type), + type_quoted_error_string(to_type)); + return false; + } + goto OK; + } + if (type_is_float(expr_canonical) && type_is_float(to_canonical)) + { + Expr *problem = recursive_may_narrow_float(expr, to_canonical); + if (problem) + { + SEMA_ERROR(problem, "The value '%s' is out of range for %s.", expr_const_to_error_string(&expr->const_expr), + type_quoted_error_string(to_type)); + return false; + } + goto OK; + } + SEMA_ERROR(expr, "Implicitly casting %s to %s is not permitted, but you can do an explicit cast using '()(value)'.", type_quoted_error_string( + type_no_fail(expr->type)), type_quoted_error_string(type_no_fail(to_type))); return false; } + + OK: // Additional checks for compile time values. - if (expr->expr_kind == EXPR_CONST) + if (expr->expr_kind == EXPR_CONST && expr->const_expr.narrowable) { - if (expr->type->type_kind == TYPE_FXX) + if (type_is_float(expr->type)) { if (!may_convert_float_const_implicit(expr, to_type)) return false; } - else if (expr->type->type_kind == TYPE_IXX) + else if (type_is_integer(expr->type)) { if (!may_convert_int_const_implicit(expr, to_type)) return false; } } - Type *original_type = expr->original_type ? expr->original_type : expr->type; cast(expr, to_type); - expr->original_type = original_type; + if (expr->expr_kind == EXPR_CAST) expr->cast_expr.implicit = true; return true; } @@ -845,10 +1219,15 @@ static inline bool subarray_to_bool(Expr *expr) bool cast(Expr *expr, Type *to_type) { Type *from_type = type_flatten(expr->type->canonical); - Type *canonical = type_flatten(to_type); - if (from_type == canonical) + Type *to = type_flatten(to_type); + if (from_type == to) { - expr_set_type(expr, to_type); + if (!IS_FAILABLE(expr) && to_type->type_kind == TYPE_FAILABLE) + { + to_type = type_no_fail(to_type); + } + expr->type = to_type; + if (expr->expr_kind == EXPR_CONST) expr->const_expr.narrowable = false; return true; } switch (from_type->type_kind) @@ -859,83 +1238,77 @@ bool cast(Expr *expr, Type *to_type) case TYPE_FUNC: case TYPE_TYPEDEF: case CT_TYPES: + case TYPE_FAILABLE: UNREACHABLE case TYPE_BITSTRUCT: UNREACHABLE case TYPE_BOOL: // Bool may convert into integers and floats but only explicitly. - if (type_is_integer(canonical)) return bool_to_int(expr, canonical, to_type); - if (type_is_float(canonical)) return bool_to_float(expr, canonical, to_type); + if (type_is_integer(to)) return bool_to_int(expr, to, to_type); + if (type_is_float(to)) return bool_to_float(expr, to, to_type); break; case TYPE_ANYERR: - if (canonical->type_kind == TYPE_BOOL) return insert_cast(expr, CAST_EUBOOL, to_type); - if (canonical->type_kind == TYPE_ERRTYPE) return insert_cast(expr, CAST_EUER, to_type); - if (type_is_integer(canonical)) return insert_cast(expr, CAST_EUINT, to_type); - break; - case TYPE_IXX: - if (type_is_integer(canonical)) return int_literal_to_int(expr, canonical, to_type); - if (type_is_float(canonical)) return int_literal_to_float(expr, canonical, to_type); - if (canonical == type_bool) return int_literal_to_bool(expr, to_type); - if (canonical->type_kind == TYPE_POINTER) return int_to_pointer(expr, to_type); - if (canonical->type_kind == TYPE_ENUM) return lit_integer_to_enum(expr, canonical, to_type); + if (to->type_kind == TYPE_BOOL) return insert_cast(expr, CAST_EUBOOL, to_type); + if (to->type_kind == TYPE_ERRTYPE) return insert_cast(expr, CAST_EUER, to_type); + if (type_is_integer(to)) return insert_cast(expr, CAST_EUINT, to_type); break; case ALL_SIGNED_INTS: - if (type_is_integer_unsigned(canonical)) return int_conversion(expr, CAST_SIUI, canonical, to_type); - if (type_is_integer_signed(canonical)) return int_conversion(expr, CAST_SISI, canonical, to_type); - if (type_is_float(canonical)) return int_to_float(expr, CAST_SIFP, canonical, to_type); - if (canonical == type_bool) return integer_to_bool(expr, to_type); - if (canonical->type_kind == TYPE_POINTER) return int_to_pointer(expr, to_type); - if (canonical->type_kind == TYPE_ENUM) return lit_integer_to_enum(expr, canonical, to_type); + if (type_is_integer_unsigned(to)) return int_conversion(expr, CAST_SIUI, to, to_type); + if (type_is_integer_signed(to)) return int_conversion(expr, CAST_SISI, to, to_type); + if (type_is_float(to)) return int_to_float(expr, CAST_SIFP, to, to_type); + if (to == type_bool) return integer_to_bool(expr, to_type); + if (to->type_kind == TYPE_POINTER) return int_to_pointer(expr, to_type); + if (to->type_kind == TYPE_ENUM) return lit_integer_to_enum(expr, to, to_type); break; case ALL_UNSIGNED_INTS: - if (type_is_integer_unsigned(canonical)) return int_conversion(expr, CAST_UIUI, canonical, to_type); - if (type_is_integer_signed(canonical)) return int_conversion(expr, CAST_UISI, canonical, to_type); - if (type_is_float(canonical)) return int_to_float(expr, CAST_UIFP, canonical, to_type); - if (canonical == type_bool) return integer_to_bool(expr, to_type); - if (canonical->type_kind == TYPE_POINTER) return int_to_pointer(expr, to_type); + if (type_is_integer_unsigned(to)) return int_conversion(expr, CAST_UIUI, to, to_type); + if (type_is_integer_signed(to)) return int_conversion(expr, CAST_UISI, to, to_type); + if (type_is_float(to)) return int_to_float(expr, CAST_UIFP, to, to_type); + if (to == type_bool) return integer_to_bool(expr, to_type); + if (to->type_kind == TYPE_POINTER) return int_to_pointer(expr, to_type); break; case ALL_FLOATS: - if (type_is_integer(canonical)) return float_to_integer(expr, canonical, to_type); - if (canonical == type_bool) return float_to_bool(expr, to_type); - if (type_is_float(canonical)) return float_to_float(expr, canonical, to_type); + if (type_is_integer(to)) return float_to_integer(expr, to, to_type); + if (to == type_bool) return float_to_bool(expr, to_type); + if (type_is_float(to)) return float_to_float(expr, to, to_type); break; case TYPE_POINTER: - if (type_is_integer(canonical)) return pointer_to_integer(expr, to_type); - if (canonical->type_kind == TYPE_BOOL) return pointer_to_bool(expr, to_type); - if (canonical->type_kind == TYPE_POINTER) return pointer_to_pointer(expr, to_type); - if (canonical->type_kind == TYPE_SUBARRAY) return insert_cast(expr, CAST_APTSA, to_type); + if (type_is_integer(to)) return pointer_to_integer(expr, to_type); + if (to->type_kind == TYPE_BOOL) return pointer_to_bool(expr, to_type); + if (to->type_kind == TYPE_POINTER) return pointer_to_pointer(expr, to_type); + if (to->type_kind == TYPE_SUBARRAY) return insert_cast(expr, CAST_APTSA, to_type); break; case TYPE_VIRTUAL: case TYPE_VIRTUAL_ANY: - if (canonical->type_kind == TYPE_POINTER) return insert_cast(expr, CAST_VRPTR, to_type); + if (to->type_kind == TYPE_POINTER) return insert_cast(expr, CAST_VRPTR, to_type); break; case TYPE_ENUM: - if (type_is_integer(canonical)) return enum_to_integer(expr, from_type, canonical, to_type); - if (type_is_float(canonical)) return enum_to_float(expr, from_type, canonical, to_type); - if (canonical == type_bool) return enum_to_bool(expr, from_type, to_type); - if (canonical->type_kind == TYPE_POINTER) return enum_to_pointer(expr, from_type, to_type); + if (type_is_integer(to)) return enum_to_integer(expr, from_type, to, to_type); + if (type_is_float(to)) return enum_to_float(expr, from_type, to, to_type); + if (to == type_bool) return enum_to_bool(expr, from_type, to_type); + if (to->type_kind == TYPE_POINTER) return enum_to_pointer(expr, from_type, to_type); break; case TYPE_ERRTYPE: - if (canonical->type_kind == TYPE_ANYERR) return err_to_anyerr(expr, to_type); - if (canonical == type_bool) return err_to_bool(expr, to_type); + if (to->type_kind == TYPE_ANYERR) return err_to_anyerr(expr, to_type); + if (to == type_bool) return err_to_bool(expr, to_type); if (type_is_integer(to_type)) return insert_cast(expr, CAST_ERINT, to_type); break; case TYPE_STRUCT: case TYPE_UNION: case TYPE_ARRAY: - if (canonical->type_kind == TYPE_ARRAY || canonical->type_kind == TYPE_STRUCT || canonical->type_kind == TYPE_UNION) + if (to->type_kind == TYPE_ARRAY || to->type_kind == TYPE_STRUCT || to->type_kind == TYPE_UNION) { return insert_cast(expr, CAST_STST, to_type); } // Starting in a little while... break; case TYPE_STRLIT: - canonical = type_flatten(canonical); - if (canonical->type_kind == TYPE_POINTER) return insert_cast(expr, CAST_STRPTR, to_type); - if (canonical->type_kind == TYPE_SUBARRAY) return string_literal_to_subarray(expr, to_type); + to = type_flatten(to); + if (to->type_kind == TYPE_POINTER) return insert_cast(expr, CAST_STRPTR, to_type); + if (to->type_kind == TYPE_SUBARRAY) return string_literal_to_subarray(expr, to_type); break; case TYPE_SUBARRAY: - if (canonical->type_kind == TYPE_POINTER) return insert_cast(expr, CAST_SAPTR, canonical); - if (canonical->type_kind == TYPE_BOOL) return subarray_to_bool(expr); + if (to->type_kind == TYPE_POINTER) return insert_cast(expr, CAST_SAPTR, to); + if (to->type_kind == TYPE_BOOL) return subarray_to_bool(expr); break; case TYPE_VECTOR: TODO diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 4361b7e9f..d06ab83ac 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -497,7 +497,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_of_required_type(context, param->type, expr, false)) return false; + if (!sema_analyse_assigned_expr(context, param->type, expr, false)) return false; Expr *inner = expr; while (inner->expr_kind == EXPR_CAST) inner = expr->cast_expr.expr; if (inner->expr_kind != EXPR_CONST) @@ -587,14 +587,15 @@ static inline bool sema_analyse_distinct(Context *context, Decl *decl) switch (base->type_kind) { case TYPE_STRLIT: - case TYPE_IXX: - case TYPE_FXX: case TYPE_FUNC: case TYPE_TYPEDEF: case TYPE_DISTINCT: case CT_TYPES: UNREACHABLE return false; + case TYPE_FAILABLE: + SEMA_ERROR(decl, "You cannot create a distinct type from a failable."); + return false; case TYPE_VIRTUAL_ANY: case TYPE_VIRTUAL: SEMA_ERROR(decl, "You cannot create a distinct type from a virtual type."); @@ -609,9 +610,8 @@ static inline bool sema_analyse_distinct(Context *context, Decl *decl) case TYPE_TYPEID: SEMA_ERROR(decl, "Cannot create a distinct type from %s.", type_quoted_error_string(base)); case TYPE_BOOL: - case ALL_SIGNED_INTS: - case ALL_UNSIGNED_INTS: - case ALL_REAL_FLOATS: + case ALL_INTS: + case ALL_FLOATS: case TYPE_POINTER: case TYPE_ENUM: case TYPE_BITSTRUCT: @@ -644,10 +644,7 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl) DEBUG_LOG("* Enum type resolved to %s.", type->name); bool success = true; unsigned enums = vec_size(decl->enums.values); - BigInt value; - BigInt add; - bigint_init_unsigned(&add, 1); - bigint_init_unsigned(&value, 0); + Int128 value = { 0, 0 }; for (unsigned i = 0; i < enums; i++) { @@ -668,23 +665,24 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl) if (!expr) { expr = expr_new(EXPR_CONST, source_span_from_token_id(enum_value->name_token)); - expr_set_type(expr, type); + expr->type = type; expr->resolve_status = RESOLVE_NOT_DONE; - bigint_init_bigint(&expr->const_expr.i, &value); - expr->const_expr.int_type = TYPE_IXX; + REMINDER("Do range check"); + expr->const_expr.ixx = (Int) { value, canonical->type_kind }; expr->const_expr.const_kind = CONST_INTEGER; - expr_set_type(expr, type_compint); + expr->const_expr.narrowable = true; + expr->type = canonical; enum_value->enum_constant.expr = expr; } // We try to convert to the desired type. - if (!sema_analyse_expr_of_required_type(context, type, expr, false)) + if (!sema_analyse_expr_of_required_type(context, type, expr)) { success = false; enum_value->resolve_status = RESOLVE_DONE; decl_poison(enum_value); // Reset! - bigint_init_unsigned(&value, 0); + value = (Int128) { 0, 0 }; continue; } @@ -701,7 +699,7 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl) } // Update the value - bigint_add(&value, &expr->const_expr.i, &add); + value = i128_add64(value, 1); DEBUG_LOG("* Value: %s", expr_const_to_error_string(&expr->const_expr)); enum_value->resolve_status = RESOLVE_DONE; } @@ -897,26 +895,24 @@ AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDoma SEMA_TOKID_ERROR(attr->name, "'align' requires an power-of-2 argument, e.g. align(8)."); return ATTRIBUTE_NONE; } - if (!sema_analyse_expr(context, type_usize, attr->expr)) return false; - if (attr->expr->expr_kind != EXPR_CONST || !type_is_any_integer(attr->expr->type->canonical)) + if (!sema_analyse_expr(context, attr->expr)) return false; + if (attr->expr->expr_kind != EXPR_CONST || !type_is_integer(attr->expr->type->canonical)) { SEMA_ERROR(attr->expr, "Expected a constant integer value as argument."); return ATTRIBUTE_NONE; } { - BigInt comp; - bigint_init_unsigned(&comp, MAX_ALIGNMENT); - if (bigint_cmp(&attr->expr->const_expr.i, &comp) == CMP_GT) + if (int_ucomp(attr->expr->const_expr.ixx, MAX_ALIGNMENT, BINARYOP_GT)) { SEMA_ERROR(attr->expr, "Alignment must be less or equal to %ull.", MAX_ALIGNMENT); return ATTRIBUTE_NONE; } - if (bigint_cmp_zero(&attr->expr->const_expr.i) != CMP_GT) + if (int_ucomp(attr->expr->const_expr.ixx, 0, BINARYOP_LE)) { SEMA_ERROR(attr->expr, "Alignment must be greater than zero."); return ATTRIBUTE_NONE; } - uint64_t align = bigint_as_unsigned(&attr->expr->const_expr.i); + uint64_t align = int_to_u64(attr->expr->const_expr.ixx); if (!is_power_of_two(align)) { SEMA_ERROR(attr->expr, "Alignment must be a power of two."); @@ -938,7 +934,7 @@ AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDoma SEMA_TOKID_ERROR(attr->name, "'%s' requires a string argument, e.g. %s(\"foo\").", TOKSTR(attr->name), TOKSTR(attr->name)); return ATTRIBUTE_NONE; } - if (!sema_analyse_expr(context, NULL, attr->expr)) return false; + if (!sema_analyse_expr(context, attr->expr)) return false; if (attr->expr->expr_kind != EXPR_CONST || attr->expr->type->canonical != type_compstr) { SEMA_ERROR(attr->expr, "Expected a constant string value as argument."); @@ -1333,7 +1329,7 @@ bool sema_analyse_var_decl(Context *context, Decl *decl) } if (!decl->var.type_info) { - if (!sema_analyse_expr(context, NULL, init_expr)) return false; + if (!sema_analyse_expr(context, init_expr)) return false; decl->type = init_expr->type; if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type); @@ -1358,7 +1354,6 @@ bool sema_analyse_var_decl(Context *context, Decl *decl) { bool type_is_inferred = decl->type->type_kind == TYPE_INFERRED_ARRAY; Expr *init = decl->var.init_expr; - // Handle explicit undef if (init->expr_kind == EXPR_UNDEF) { @@ -1376,7 +1371,8 @@ bool sema_analyse_var_decl(Context *context, Decl *decl) decl->resolve_status = RESOLVE_DONE; if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type); } - if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, decl->var.failable || decl->var.unwrap ? FAILABLE_YES : FAILABLE_NO)) return decl_poison(decl); + + if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, false)) return decl_poison(decl); if (type_is_inferred) { @@ -1386,13 +1382,13 @@ bool sema_analyse_var_decl(Context *context, Decl *decl) } else if (decl->type) { - expr_set_type(decl->var.init_expr, decl->type); + decl->var.init_expr->type = decl->type; } Expr *init_expr = decl->var.init_expr; // 1. Check type. - if (!sema_analyse_expr_of_required_type(context, decl->type, init_expr, false)) return false; + if (!sema_analyse_assigned_expr(context, decl->type, init_expr, false)) return false; // 2. Check const-ness if ((is_global || decl->var.is_static) && !expr_is_constant_eval(init_expr, CONSTANT_EVAL_ANY)) @@ -1401,12 +1397,13 @@ bool sema_analyse_var_decl(Context *context, Decl *decl) } else { - if (decl->var.unwrap && !init->failable) + if (decl->var.unwrap && init->type->type_kind != TYPE_FAILABLE) { SEMA_ERROR(decl->var.init_expr, "A failable expression was expected here."); return decl_poison(decl); } } + if (init_expr->expr_kind == EXPR_CONST) init_expr->const_expr.narrowable = false; } EXIT_OK: if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 04953b57b..0fc417634 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -9,14 +9,19 @@ * - Disallow jumping in and out of an expression block. */ - +static inline bool sema_expr_analyse_binary(Context *context, Expr *expr); +static inline bool sema_cast_rvalue(Context *context, Expr *expr); +static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl); +static inline void expr_set_as_const_list(Expr *expr, ConstInitializer *list); +static inline bool is_const(Expr *expr); +static bool sema_check_stmt_compile_time(Context *context, Ast *ast); +static bool binary_arithmetic_promotion(Context *context, Expr *left, Expr *right, Type *left_type, Type *right_type, Expr *parent, const char *error_message); static inline void expr_set_as_const_list(Expr *expr, ConstInitializer *list) { expr->expr_kind = EXPR_CONST; expr->const_expr.const_kind = CONST_LIST; expr->const_expr.list = list; } -static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr); int BINOP_PREC_REQ[BINARYOP_LAST] = { @@ -46,59 +51,32 @@ static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl) embedded_struct->resolve_status = RESOLVE_DONE; embedded_struct->access_expr.parent = parent; embedded_struct->access_expr.ref = parent_decl->strukt.members[0]; - expr_set_type(embedded_struct, embedded_struct->access_expr.ref->type); + embedded_struct->type = embedded_struct->access_expr.ref->type; return embedded_struct; } -static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *expr); -static inline bool expr_const_int_valid(Expr *expr, Type *type) -{ - if (expr_const_int_overflowed(&expr->const_expr)) - { - SEMA_ERROR(expr, "Cannot fit '%s' into type '%s'.", expr_const_to_error_string(&expr->const_expr), type_to_error_string(type)); - return false; - } - if (bigint_cmp_zero(&expr->const_expr.i) == CMP_LT && type_kind_is_unsigned(expr->const_expr.int_type)) - { - SEMA_ERROR(expr, "'%s' underflows type '%s'.", expr_const_to_error_string(&expr->const_expr), type_to_error_string(type)); - return false; - } - return true; -} +#define IS_CONST(_x) ((_x)->expr_kind == EXPR_CONST) static inline bool is_const(Expr *expr) { return expr->expr_kind == EXPR_CONST; } -static inline bool both_const(Expr *left, Expr *right) +static inline bool expr_both_const(Expr *left, Expr *right) { return left->expr_kind == EXPR_CONST && right->expr_kind == EXPR_CONST; } - static inline bool both_any_integer_or_integer_vector(Expr *left, Expr *right) { Type *flatten_left = type_flatten(left->type); Type *flatten_right = type_flatten(right->type); - if (type_is_any_integer(flatten_left) && type_is_any_integer(flatten_right)) return true; + if (type_is_integer(flatten_left) && type_is_integer(flatten_right)) return true; if (flatten_left->type_kind != TYPE_VECTOR || flatten_right->type_kind != TYPE_VECTOR) return false; - return type_is_any_integer(flatten_left->vector.base) && type_is_any_integer(flatten_right->vector.base); -} - -void expr_copy_properties(Expr *to, Expr *from) -{ - to->failable = from->failable; - to->pure = from->pure; -} - -void expr_copy_types(Expr *to, Expr *from) -{ - to->type = from->type; - to->original_type = from->original_type; + return type_is_integer(flatten_left->vector.base) && type_is_integer(flatten_right->vector.base); } void expr_insert_addr(Expr *original) @@ -112,7 +90,7 @@ void expr_insert_addr(Expr *original) Expr *inner = expr_alloc(); *inner = *original; original->expr_kind = EXPR_UNARY; - expr_set_type(original, type_get_ptr(inner->type)); + original->type = type_get_ptr(inner->type); original->unary_expr.operator = UNARYOP_ADDR; original->unary_expr.expr = inner; } @@ -121,9 +99,8 @@ Expr *expr_variable(Decl *decl) { Expr *expr = expr_new(EXPR_IDENTIFIER, decl->span); expr->identifier_expr.decl = decl; - expr->pure = true; expr->resolve_status = RESOLVE_DONE; - expr_set_type(expr, decl->type); + expr->type = decl->type; return expr; } @@ -370,36 +347,31 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) void expr_insert_deref(Expr *original) { assert(original->resolve_status == RESOLVE_DONE); - assert(original->type->canonical->type_kind == TYPE_POINTER); + Type *no_fail = type_no_fail(original->type); + assert(no_fail->canonical->type_kind == TYPE_POINTER); + + // 1. Assume *(&x) => x if (original->expr_kind == EXPR_UNARY && original->unary_expr.operator == UNARYOP_ADDR) { *original = *original->unary_expr.expr; return; } - Type *pointee = original->type->type_kind == TYPE_POINTER ? original->type->pointer : original->type->canonical->pointer; - Expr *inner = expr_alloc(); - *inner = *original; + // 2. Only fold to the canonical type if it wasn't a pointer. + Type *pointee = no_fail->type_kind == TYPE_POINTER ? no_fail->pointer : no_fail->canonical->pointer; + + // 3. Allocate our new and create our new inner, and overwrite the original. + Expr *inner = expr_copy(original); original->expr_kind = EXPR_UNARY; - expr_set_type(original, pointee); + original->type = type_get_opt_fail(pointee, IS_FAILABLE(inner)); original->unary_expr.operator = UNARYOP_DEREF; original->unary_expr.expr = inner; - original->pure = false; - original->failable = inner->failable; } -static void expr_unify_binary_properties(Expr *expr, Expr *left, Expr *right) -{ - expr->pure = left->pure & right->pure; - expr->failable = left->failable | right->failable; -} - static void expr_unify_binary(Expr *expr, Expr *left, Expr *right) { - expr->type = left->type; - expr->original_type = type_find_max_type(left->original_type->canonical, right->original_type->canonical); - expr_unify_binary_properties(expr, left, right); + expr->type = type_get_opt_fail(left->type, IS_FAILABLE(right)); } static inline void context_pop_returns(Context *context, Ast **restore) @@ -410,6 +382,7 @@ static inline void context_pop_returns(Context *context, Ast **restore) } context->returns = restore; } + static inline Ast **context_push_returns(Context *context) { Ast** old_returns = context->returns; @@ -428,7 +401,7 @@ static inline Ast **context_push_returns(Context *context) int sema_check_comp_time_bool(Context *context, Expr *expr) { - if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) return -1; + if (!sema_analyse_assigned_expr(context, type_bool, expr, false)) return -1; if (expr->expr_kind != EXPR_CONST) { SEMA_ERROR(expr, "Compile time evaluation requires a compile time constant value."); @@ -485,7 +458,7 @@ bool expr_is_ltype(Expr *expr) } } -static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr) +static inline bool sema_cast_ident_rvalue(Context *context, Expr *expr) { Decl *decl = expr->identifier_expr.decl; decl = decl_flatten(decl); @@ -497,10 +470,10 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr SEMA_ERROR(expr, "Expected function followed by (...) or prefixed by &."); return expr_poison(expr); case DECL_MACRO: - SEMA_ERROR(expr, "Expected macro followed by (...) or prefixed by '&'."); + SEMA_ERROR(expr, "Expected a macro followed by (...)."); return expr_poison(expr); case DECL_GENERIC: - SEMA_ERROR(expr, "Expected generic followed by (...) or prefixed by '&'."); + SEMA_ERROR(expr, "Expected generic function followed by (...)."); return expr_poison(expr); case DECL_ERRVALUE: SEMA_ERROR(expr, "Did you forget a '!' after '%s'?", decl->name); @@ -525,10 +498,10 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr SEMA_ERROR(expr, "Expected bitstruct followed by (...) or '.'."); return expr_poison(expr); case DECL_STRUCT: - SEMA_ERROR(expr, "Expected struct followed by (...) or '.'."); + SEMA_ERROR(expr, "Expected struct followed by {...} or '.'."); return expr_poison(expr); case DECL_UNION: - SEMA_ERROR(expr, "Expected union followed by (...) or '.'."); + SEMA_ERROR(expr, "Expected union followed by {...} or '.'."); return expr_poison(expr); case DECL_ENUM: SEMA_ERROR(expr, "Expected enum name followed by '.' and an enum value."); @@ -557,50 +530,44 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr UNREACHABLE } expr_replace(expr, copy_expr(decl->var.init_expr)); - return sema_analyse_expr(context, to, expr); + return sema_analyse_expr(context, expr); case VARDECL_PARAM_EXPR: expr_replace(expr, copy_expr(decl->var.init_expr)); assert(decl->var.init_expr->resolve_status == RESOLVE_DONE); return true; case VARDECL_PARAM_CT_TYPE: - TODO + case VARDECL_PARAM_CT: + case VARDECL_LOCAL_CT: + case VARDECL_LOCAL_CT_TYPE: + case VARDECL_ERASE: + case VARDECL_REWRAPPED: + // Impossible to reach this, they are already unfolded + UNREACHABLE case VARDECL_PARAM_REF: expr_replace(expr, copy_expr(decl->var.init_expr)); - return sema_cast_rvalue(context, to, expr); + return sema_cast_rvalue(context, expr); case VARDECL_PARAM: case VARDECL_GLOBAL: case VARDECL_LOCAL: case VARDECL_UNWRAPPED: - break; + return true; case VARDECL_MEMBER: SEMA_ERROR(expr, "Expected '%s' followed by a method call or property.", decl->name); return expr_poison(expr); - case VARDECL_PARAM_CT: - TODO - break; - case VARDECL_LOCAL_CT: - TODO - break; - case VARDECL_LOCAL_CT_TYPE: - TODO - break; - case VARDECL_ERASE: - case VARDECL_REWRAPPED: - UNREACHABLE } - return true; + UNREACHABLE } -static ExprFailableStatus expr_is_failable(Expr *expr) +static bool expr_is_unwrapped_ident(Expr *expr) { - if (expr->expr_kind != EXPR_IDENTIFIER) return FAILABLE_NO; + if (expr->expr_kind != EXPR_IDENTIFIER) return false; Decl *decl = expr->identifier_expr.decl; - if (decl->decl_kind != DECL_VAR) return FAILABLE_NO; - if (decl->var.kind == VARDECL_UNWRAPPED && decl->var.alias->var.failable) return FAILABLE_UNWRAPPED; - return decl->var.failable ? FAILABLE_YES : FAILABLE_NO; + if (decl->decl_kind != DECL_VAR) return false; + return decl->var.kind == VARDECL_UNWRAPPED && IS_FAILABLE(decl->var.alias); } + static inline bool sema_type_error_on_binop(Expr *expr) { const char *c = token_type_to_string(binaryop_to_token(expr->binary_expr.operator)); @@ -614,13 +581,6 @@ static bool expr_cast_to_index(Expr *index) { switch (index->type->canonical->type_kind) { - case TYPE_IXX: - if (!bigint_fits_in_bits(&index->const_expr.i, 64, true)) - { - SEMA_ERROR(index, "The index is out of range, it must fit in a signed 64 bit integer."); - return false; - } - return cast(index, type_iptrdiff); case TYPE_I8: case TYPE_I16: case TYPE_I32: @@ -643,7 +603,7 @@ static bool expr_cast_to_index(Expr *index) } } -static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_ternary(Context *context, Expr *expr) { Expr *left = expr->ternary_expr.then_expr; Expr *cond = expr->ternary_expr.cond; @@ -651,10 +611,9 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e // Normal if (left) { - if (!sema_analyse_expr(context, type_bool, cond)) return expr_poison(expr); + if (!sema_analyse_expr(context, cond)) return expr_poison(expr); if (!cast_implicit(cond, type_bool)) return expr_poison(expr); - if (!sema_analyse_expr(context, to, left)) return expr_poison(expr); - expr->failable = left->failable | cond->failable; + if (!sema_analyse_expr(context, left)) return expr_poison(expr); if (cond->expr_kind == EXPR_CONST) { path = cond->const_expr.b ? 1 : 0; @@ -663,8 +622,7 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e else { // Elvis - if (!sema_analyse_expr(context, to, cond)) return expr_poison(expr); - expr->failable = cond->failable; + if (!sema_analyse_expr(context, cond)) return expr_poison(expr); Type *type = cond->type->canonical; if (type->type_kind != TYPE_BOOL && cast_to_bool_kind(type) == CAST_ERROR) { @@ -682,26 +640,10 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e } Expr *right = expr->ternary_expr.else_expr; - if (!sema_analyse_expr(context, to, right)) return expr_poison(expr); + if (!sema_analyse_expr(context, right)) return expr_poison(expr); - expr->pure = cond->pure & left->pure & right->pure; - - expr->failable |= right->failable; Type *left_canonical = left->type->canonical; Type *right_canonical = right->type->canonical; - if (type_is_ct(left_canonical) && type_is_ct(right_canonical)) - { - if (to) - { - if (!cast_implicit(left, to) || !cast_implicit(right, to)) return false; - } - else - { - if (!cast_implicitly_to_runtime(left) || !cast_implicitly_to_runtime(right)) return false; - } - left_canonical = left->type->canonical; - right_canonical = right->type->canonical; - } if (left_canonical != right_canonical) { Type *max = type_find_max_type(left_canonical, right_canonical); @@ -745,9 +687,8 @@ static inline bool sema_expr_analyse_enum_constant(Expr *expr, TokenId name, Dec if (!enum_constant) return false; assert(enum_constant->resolve_status == RESOLVE_DONE); - expr_set_type(expr, decl->type); + expr->type = decl->type; expr->expr_kind = EXPR_CONST; - expr->pure = true; if (enum_constant->decl_kind == DECL_ENUM_CONSTANT) { expr->const_expr.const_kind = CONST_ENUM; @@ -786,7 +727,6 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr { Decl *ambiguous_decl = NULL; Decl *private_symbol = NULL; - expr->pure = true; DEBUG_LOG("Now resolving %s", TOKSTR(expr->identifier_expr.identifier)); Decl *decl = sema_resolve_normal_symbol(context, @@ -820,10 +760,6 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr { if (!sema_analyse_decl(context, decl)) return decl_poison(decl); } - if (decl->decl_kind == DECL_VAR && decl->var.failable) - { - expr->failable = true; - } if (decl->decl_kind == DECL_VAR) { switch (decl->var.kind) @@ -832,7 +768,7 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr if (!decl->type) { Expr *copy = copy_expr(decl->var.init_expr); - if (!sema_analyse_expr(context, to, copy)) return false; + if (!sema_analyse_expr(context, copy)) return false; if (!expr_is_constant_eval(copy, false)) { SEMA_ERROR(expr, "Constant value did not evaluate to a constant."); @@ -841,7 +777,7 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr expr_replace(expr, copy); return true; } - if (decl->var.failable) + if (IS_FAILABLE(decl)) { SEMA_ERROR(expr, "Constants may never be 'failable', please remove the '!'."); return false; @@ -853,8 +789,7 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr } if (!decl->type) decl->type = type_void; expr->identifier_expr.decl = decl; - expr_set_type(expr, decl->type); - expr->pure = true; + expr->type = decl->type; DEBUG_LOG("Resolution successful of %s.", decl->name); return true; } @@ -870,11 +805,11 @@ static inline bool sema_expr_analyse_macro_expansion(Context *context, Expr *exp expr->body_expansion_expr.ast = NULL; expr->body_expansion_expr.declarations = NULL; expr->resolve_status = RESOLVE_NOT_DONE; - expr->type = expr->original_type = type_void; + expr->type = type_void; return true; } } - if (!sema_analyse_expr_value(context, NULL, inner)) return false; + if (!sema_analyse_expr_lvalue(context, inner)) return false; Decl *decl; switch (inner->expr_kind) { @@ -894,14 +829,11 @@ static inline bool sema_expr_analyse_macro_expansion(Context *context, Expr *exp return false; } expr->macro_expansion_expr.decl = decl; - expr_copy_properties(expr, inner); return true; } static inline bool sema_expr_analyse_ct_identifier(Context *context, Expr *expr) { - expr->pure = true; - DEBUG_LOG("Now resolving %s", TOKSTR(expr->ct_ident_expr.identifier)); Decl *decl = sema_resolve_normal_symbol(context, expr->ct_ident_expr.identifier, @@ -918,15 +850,12 @@ static inline bool sema_expr_analyse_ct_identifier(Context *context, Expr *expr) assert(decl->resolve_status == RESOLVE_DONE); expr->ct_ident_expr.decl = decl; - expr_set_type(expr, decl->type); - expr->pure = true; + expr->type = decl->type; return true; } -static inline bool sema_expr_analyse_hash_identifier(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_hash_identifier(Context *context, Expr *expr) { - expr->pure = true; - DEBUG_LOG("Now resolving %s", TOKSTR(expr->hash_ident_expr.identifier)); Decl *decl = sema_resolve_normal_symbol(context, expr->hash_ident_expr.identifier, @@ -944,14 +873,30 @@ static inline bool sema_expr_analyse_hash_identifier(Context *context, Type *to, assert(decl->var.init_expr->resolve_status == RESOLVE_DONE); expr_replace(expr, copy_expr(decl->var.init_expr)); - return sema_analyse_expr(context, to, expr); + return sema_analyse_expr(context, expr); } -static inline bool sema_expr_analyse_binary_sub_expr(Context *context, Type *to, Expr *left, Expr *right) +static inline bool sema_expr_analyse_binary_subexpr(Context *context, Expr *left, Expr *right) { - return (int)sema_analyse_expr(context, to, left) & (int)sema_analyse_expr(context, to, right); + return (int)sema_analyse_expr(context, left) & (int)sema_analyse_expr(context, right); } +static inline bool sema_expr_analyse_binary_arithmetic_subexpr(Context *context, Expr *expr, const char *error) +{ + Expr *left = expr->binary_expr.left; + Expr *right = expr->binary_expr.right; + + // 1. Analyse both sides. + if (!sema_expr_analyse_binary_subexpr(context, left, right)) return false; + + Type *left_type = type_no_fail(left->type)->canonical; + Type *right_type = type_no_fail(right->type)->canonical; + + // 2. Perform promotion to a common type. + return binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, error); +} + + static inline int find_index_of_named_parameter(Decl **func_params, Expr *expr) { if (vec_size(expr->designator_expr.path) != 1) @@ -974,7 +919,7 @@ static inline int find_index_of_named_parameter(Decl **func_params, Expr *expr) return -1; } -static inline bool sema_expr_analyse_intrinsic_fp_invocation(Context *context, Expr *expr, Decl *decl, Type *to) +static inline bool sema_expr_analyse_intrinsic_fp_invocation(Context *context, Expr *expr, Decl *decl, bool *failable) { unsigned arguments = vec_size(expr->call_expr.arguments); if (arguments != 1) @@ -983,11 +928,12 @@ static inline bool sema_expr_analyse_intrinsic_fp_invocation(Context *context, E return false; } Expr *arg = expr->call_expr.arguments[0]; - if (!sema_analyse_expr(context, to, arg)) return false; + if (!sema_analyse_expr(context, arg)) return false; + // Convert ints to float comptime float. - if (type_is_any_integer(arg->type->canonical)) + if (type_is_integer(arg->type->canonical)) { - if (!cast_implicit(arg, type_compfloat)) return false; + if (!cast_implicit(arg, type_double)) return false; } // If this is not a float argument => error. if (!type_is_float(arg->type->canonical)) @@ -995,20 +941,18 @@ static inline bool sema_expr_analyse_intrinsic_fp_invocation(Context *context, E SEMA_ERROR(arg, "Expected a floating point argument.", decl->name); return false; } - // We lower to a real float in case we got a compfloat. - if (!cast_implicitly_to_runtime(arg)) return false; // The expression type is the argument type. - expr_set_type(expr, arg->type); + expr->type = type_with_added_failability(arg, *failable); return true; } -static inline bool sema_expr_analyse_intrinsic_invocation(Context *context, Expr *expr, Decl *decl, Type *to) +static inline bool sema_expr_analyse_intrinsic_invocation(Context *context, Expr *expr, Decl *decl, bool *failable) { if (decl->name == kw___ceil || decl->name == kw___trunc || decl->name == kw___round || decl->name == kw___sqrt) { - return sema_expr_analyse_intrinsic_fp_invocation(context, expr, decl, to); + return sema_expr_analyse_intrinsic_fp_invocation(context, expr, decl, failable); } UNREACHABLE } @@ -1033,7 +977,6 @@ static inline bool expr_may_unpack_as_vararg(Expr *expr, Type *variadic_base_typ typedef struct { bool macro; - bool failable; TokenId block_parameter; Decl **params; Expr *struct_var; @@ -1043,14 +986,6 @@ typedef struct static inline bool expr_promote_vararg(Context *context, Expr *arg) { Type *arg_type = arg->type->canonical; - // 1. Is it compile time? - if (type_is_ct(arg_type)) - { - // TODO Fix the int conversion. - // 1. Pick double / CInt - Type *target_type = type_is_any_integer(arg_type) ? type_cint() : type_double; - return cast_implicit(arg, target_type); - } // 2. Promote any integer or bool to at least CInt if (type_is_promotable_integer(arg_type) || arg_type == type_bool) @@ -1116,7 +1051,7 @@ static inline bool sema_check_invalid_body_arguments(Context *context, Expr *cal return true; } -static inline bool sema_expand_call_arguments(Context *context, CalledDecl *callee, Expr *call, Decl **params, Expr **args, unsigned func_param_count, bool variadic) +static inline bool sema_expand_call_arguments(Context *context, CalledDecl *callee, Expr *call, Decl **params, Expr **args, unsigned func_param_count, bool variadic, bool *failable) { unsigned num_args = vec_size(args); @@ -1160,7 +1095,7 @@ static inline bool sema_expand_call_arguments(Context *context, CalledDecl *call // 8g. Set the parameter and update failability. actual_args[index] = arg->designator_expr.value; - call->failable |= arg->designator_expr.value->failable; + *failable |= IS_FAILABLE(arg->designator_expr.value); continue; } @@ -1231,13 +1166,11 @@ static inline bool sema_expand_call_arguments(Context *context, CalledDecl *call call->call_expr.arguments = actual_args; return true; } -static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *call, CalledDecl callee) +static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *call, CalledDecl callee, bool *failable) { // 1. Check body arguments. if (!sema_check_invalid_body_arguments(context, call, &callee)) return false; - call->failable = callee.failable; - // 2. Pick out all the arguments and parameters. Expr **args = call->call_expr.arguments; Decl **params = callee.params; @@ -1290,7 +1223,7 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal func_param_count--; } - if (!sema_expand_call_arguments(context, &callee, call, params, args, func_param_count, callee.variadic != VARIADIC_NONE)) return false; + if (!sema_expand_call_arguments(context, &callee, call, params, args, func_param_count, callee.variadic != VARIADIC_NONE, failable)) return false; args = call->call_expr.arguments; num_args = vec_size(args); @@ -1312,7 +1245,7 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal { // 11c. Analyse the expression. We don't use any type inference here since // foo(...{ 1, 2, 3 }) is a fairly worthless thing. - if (!sema_analyse_expr(context, NULL, arg)) return false; + if (!sema_analyse_expr(context, arg)) return false; // 11d. If it is allowed. if (!expr_may_unpack_as_vararg(arg, variadic_type)) @@ -1326,17 +1259,17 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal else { // 11e. A simple variadic value: - if (!sema_analyse_expr_of_required_type(context, variadic_type, arg, true)) return false; + if (!sema_analyse_assigned_expr(context, variadic_type, arg, true)) return false; } // Set the argument at the location. - call->failable |= arg->failable; + *failable |= IS_FAILABLE(arg); continue; } // 12. We might have a naked variadic argument if (callee.variadic == VARIADIC_RAW) { // 12a. Analyse the expression. - if (!sema_analyse_expr(context, NULL, arg)) return false; + if (!sema_analyse_expr(context, arg)) return false; // 12b. In the case of a compile time variable non macros we cast to c_int / double. if (!callee.macro) @@ -1344,7 +1277,7 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal if (!expr_promote_vararg(context, arg)) return false; } // Set the argument at the location. - call->failable |= arg->failable; + *failable |= IS_FAILABLE(arg); continue; } UNREACHABLE @@ -1357,7 +1290,9 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal { case VARDECL_PARAM_REF: // &foo - if (!sema_analyse_expr_value(context, param->type, arg)) return false; + { + if (!sema_analyse_expr_lvalue(context, arg)) return false; + } if (param->type && param->type->canonical != arg->type->canonical) { SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(param->type)); @@ -1366,12 +1301,8 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal break; case VARDECL_PARAM: // foo - if (!sema_analyse_expr_of_required_type(context, param->type, arg, true)) return false; - if (!param->type && !cast_implicitly_to_runtime(arg)) - { - SEMA_ERROR(arg, "Constant cannot implicitly be cast to a real type."); - return false; - } + if (!sema_analyse_assigned_expr(context, param->type, arg, true)) return false; + if (IS_FAILABLE(arg)) *failable = true; if (callee.macro) { param->alignment = type_abi_alignment(param->type ? param->type : arg->type); @@ -1383,13 +1314,14 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal // compile time variables during evaluation: assert(callee.macro); SCOPE_START - if (!sema_analyse_expr_of_required_type(context, param->type, arg, true)) return SCOPE_POP_ERROR(); + if (!sema_analyse_assigned_expr(context, param->type, arg, true)) return SCOPE_POP_ERROR(); SCOPE_END; + if (IS_FAILABLE(arg)) *failable = true; break; case VARDECL_PARAM_CT: // $foo assert(callee.macro); - if (!sema_analyse_expr_of_required_type(context, param->type, arg, false)) return false; + if (!sema_analyse_assigned_expr(context, param->type, arg, false)) return false; if (!expr_is_constant_eval(arg, CONSTANT_EVAL_ANY)) { SEMA_ERROR(arg, "A compile time parameter must always be a constant, did you mistake it for a normal paramter?"); @@ -1398,8 +1330,9 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal break; case VARDECL_PARAM_CT_TYPE: // $Foo - if (!sema_analyse_expr_value(context, NULL, arg)) return false; - // TODO check typeof + { + if (!sema_analyse_expr_lvalue(context, arg)) return false; + } if (arg->expr_kind != EXPR_TYPEINFO) { SEMA_ERROR(arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name); @@ -1417,21 +1350,12 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal case VARDECL_ERASE: UNREACHABLE } - if (param->type) - { - if (!cast_implicit(arg, param->type)) return false; - } - else - { - param->type = arg->type; - } - call->failable |= arg->failable; + if (!param->type) param->type = type_no_fail(arg->type); } - - return true; } -static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionSignature *signature, Expr *expr, Decl *decl, Type *to, Expr *struct_var) +static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionSignature *signature, Expr *expr, Decl *decl, + Expr *struct_var, bool failable) { CalledDecl callee = { .macro = false, @@ -1439,112 +1363,106 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS .struct_var = struct_var, .params = signature->params, .variadic = signature->variadic, - .failable = signature->failable, }; - if (!sema_expr_analyse_call_invocation(context, expr, callee)) return false; + if (!sema_expr_analyse_call_invocation(context, expr, callee, &failable)) return false; // 2. Builtin? We handle that elsewhere. if (decl && decl->func_decl.is_builtin) { assert(!struct_var); - return sema_expr_analyse_intrinsic_invocation(context, expr, decl, to); + return sema_expr_analyse_intrinsic_invocation(context, expr, decl, &failable); } - expr_set_type(expr, signature->rtype->type); + Type *rtype = signature->rtype->type; + + expr->type = type_get_opt_fail(rtype, failable); return true; } -static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr *expr, Decl *var_decl) +static inline bool sema_expr_analyse_var_call(Context *context, Expr *expr, Decl *var_decl, bool failable) { Type *func_ptr_type = var_decl->type->canonical; - expr->failable |= var_decl->var.failable; if (func_ptr_type->type_kind != TYPE_POINTER || func_ptr_type->pointer->type_kind != TYPE_FUNC) { SEMA_ERROR(expr, "Only macros, functions and function pointers maybe invoked, this is of type '%s'.", type_to_error_string(var_decl->type)); return false; } expr->call_expr.is_pointer_call = true; + failable |= IS_FAILABLE(var_decl); return sema_expr_analyse_func_invocation(context, func_ptr_type->pointer->func.signature, expr, - NULL, - to, NULL); + NULL, NULL, failable); } - - - - -static inline Type *unify_returns(Context *context, Type *to) +// Unify returns in a macro or expression block. +static inline Type *unify_returns(Context *context) { bool all_returns_need_casts = false; - // Let's unify the return statements. + Type *common_type = NULL; + + // 1. Loop through the returns. VECEACH(context->returns, i) { Ast *return_stmt = context->returns[i]; Expr *ret_expr = return_stmt->return_stmt.expr; - bool last_expr_was_void = to == type_void; - Type *right_canonical = ret_expr ? ret_expr->type->canonical : type_void; - bool current_expr_was_void = right_canonical == type_void; - if (i > 0 && last_expr_was_void != current_expr_was_void) + Type *rtype = ret_expr ? ret_expr->type : type_void; + + // 2. If we have no common type, set to the return type. + if (!common_type) { - SEMA_ERROR(return_stmt, "You can't combine empty returns with value returns."); - SEMA_PREV(context->returns[i - 1], "Previous return was here."); - return NULL; - } - if (to != type_void) - { - if (current_expr_was_void) - { - SEMA_ERROR(return_stmt, "The return must be a value of type '%s'.", type_to_error_string(to)); - return NULL; - } - if (!cast_implicit(ret_expr, to)) - { - return NULL; - } + common_type = rtype; continue; } - // The simple case. - if (to == right_canonical) continue; + // 3. Same type -> we're done. + if (common_type == rtype) continue; - // Try to find a common type: - Type *max = type_find_max_type(to, right_canonical); + // 4. Find the max of the old and new. + Type *max = type_find_max_type(common_type, rtype); + + // 5. No match -> error. if (!max) { - SEMA_ERROR(return_stmt, "Cannot find a common parent type of '%s' and '%s'", - type_to_error_string(to), type_to_error_string(right_canonical)); + SEMA_ERROR(return_stmt, "Cannot find a common parent type of %s and %s", + rtype, common_type); SEMA_PREV(context->returns[i - 1], "The previous return was here."); return false; } - to = max; + + // 6. Set the new max, mark as needing a cast on all returns. + common_type = max; all_returns_need_casts = true; } + + // 7. Insert casts. if (all_returns_need_casts) { VECEACH(context->returns, i) { Ast *return_stmt = context->returns[i]; Expr *ret_expr = return_stmt->return_stmt.expr; - if (!cast_implicit(ret_expr, to)) + // 8. All casts should work. + if (!cast_implicit(ret_expr, common_type)) { + assert(false); return NULL; } } } - return to; + + // 8. On no common type -> return void + return common_type ? common_type : type_void; } -static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr *expr, Decl *decl, Expr *struct_var) +static inline bool sema_expr_analyse_func_call(Context *context, Expr *expr, Decl *decl, Expr *struct_var, bool failable) { expr->call_expr.is_pointer_call = false; - return sema_expr_analyse_func_invocation(context, &decl->func_decl.function_signature, expr, decl, to, struct_var); + return sema_expr_analyse_func_invocation(context, &decl->func_decl.function_signature, expr, decl, struct_var, failable); } -static bool sema_check_stmt_compile_time(Context *context, Ast *ast); static bool sema_check_expr_compile_time(Context *context, Expr *expr) { @@ -1587,11 +1505,10 @@ static bool sema_check_stmt_compile_time(Context *context, Ast *ast) } } -static bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_expr, Expr *struct_var, Decl *decl) +static bool sema_expr_analyse_macro_call(Context *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable) { assert(decl->decl_kind == DECL_MACRO); - // TODO failable if (context->macro_scope.depth >= MAX_MACRO_NESTING) { SEMA_ERROR(call_expr, "Too deep nesting (more than %d levels) when evaluating this macro.", MAX_MACRO_NESTING); @@ -1606,7 +1523,7 @@ static bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_ .struct_var = struct_var }; - if (!sema_expr_analyse_call_invocation(context, call_expr, callee)) return false; + if (!sema_expr_analyse_call_invocation(context, call_expr, callee, &failable)) return false; Decl **func_params = decl->macro_decl.parameters; Expr **args = call_expr->call_expr.arguments; VECEACH(params, i) @@ -1681,10 +1598,11 @@ static bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_ Ast *body = copy_ast(decl->macro_decl.body); - TypeInfo *foo = decl->macro_decl.rtype; + Type *rtype = decl->macro_decl.rtype ? decl->macro_decl.rtype->type : NULL; Ast **saved_returns = context_push_returns(context); - context->expected_block_type = foo ? foo->type : to; + Type *previous_block_type = context->expected_block_type; + context->expected_block_type = rtype; SCOPE_START_WITH_FLAGS(SCOPE_MACRO); @@ -1705,24 +1623,26 @@ static bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_ if (!vec_size(context->returns)) { - if (to) + if (rtype && type_no_fail(rtype) != type_void) { - SEMA_ERROR(decl, "Missing return in macro that evaluates to %s.", type_to_error_string(to)); + SEMA_ERROR(decl, "Missing return in macro that should evaluate to %s.", type_quoted_error_string(rtype)); ok = false; goto EXIT; } } - Expr *first_return_expr = vec_size(context->returns) ? context->returns[0]->return_stmt.expr : NULL; - Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void; - // Let's unify the return statements. - left_canonical = unify_returns(context, left_canonical); - if (!left_canonical) + Type *sum_returns = unify_returns(context); + if (!sum_returns) { ok = false; goto EXIT; } - expr_set_type(call_expr, left_canonical); + if (rtype) + { + assert(type_no_fail(rtype)->canonical == type_no_fail(sum_returns)->canonical); + sum_returns = rtype; + } + call_expr->type = type_get_opt_fail(sum_returns, failable); if (vec_size(context->returns) == 1) { Expr *result = context->returns[0]->return_stmt.expr; @@ -1735,7 +1655,6 @@ static bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_ } } } - call_expr->expr_kind = EXPR_MACRO_BLOCK; call_expr->macro_block.stmts = body->compound_stmt.stmts; call_expr->macro_block.params = params; @@ -1743,6 +1662,7 @@ static bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_ EXIT: SCOPE_END; + context->expected_block_type = previous_block_type; context_pop_returns(context, saved_returns); SCOPE_OUTER_END; @@ -1755,7 +1675,7 @@ static inline Decl *sema_generate_generic_function(Context *context, Expr *call_ return NULL; } -static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Expr *call_expr, Expr *struct_var, Decl *decl) +static inline bool sema_expr_analyse_generic_call(Context *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable) { assert(decl->decl_kind == DECL_GENERIC); @@ -1790,7 +1710,7 @@ static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Ex Decl *param = func_params[i + offset]; if (param->var.kind == VARDECL_PARAM_CT_TYPE) { - if (!sema_analyse_expr_value(context, NULL, arg)) return false; + if (!sema_analyse_expr_lvalue(context, arg)) return false; if (arg->expr_kind != EXPR_TYPEINFO) { SEMA_ERROR(arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name); @@ -1802,12 +1722,11 @@ static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Ex } if (param->var.type_info) { - if (!sema_analyse_expr_of_required_type(context, param->var.type_info->type, arg, true)) return false; + if (!sema_analyse_assigned_expr(context, param->var.type_info->type, arg, true)) return false; } else { - if (!sema_analyse_expr(context, NULL, arg)) return false; - if (!cast_implicitly_to_runtime(arg)) return false; + if (!sema_analyse_expr(context, arg)) return false; } scratch_buffer_append_char(','); scratch_buffer_append(arg->type->canonical->name); @@ -1843,7 +1762,7 @@ static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Ex } vec_resize(args, explicit_args); // Perform the normal func call on the found declaration. - return sema_expr_analyse_func_call(context, to, call_expr, found, struct_var); + return sema_expr_analyse_func_call(context, call_expr, found, struct_var, failable); } static bool sema_analyse_body_expansion(Context *context, Expr *call) @@ -1874,7 +1793,7 @@ static bool sema_analyse_body_expansion(Context *context, Expr *call) { Expr *expr = args[i]; Decl *param = context->macro_scope.yield_args[i]; - if (!sema_analyse_expr(context, param->type, expr)) return false; + if (!sema_analyse_expr(context, expr)) return false; } assert(call_expr->function->expr_kind == EXPR_MACRO_BODY_EXPANSION); expr_replace(call, call_expr->function); @@ -1888,7 +1807,7 @@ static bool sema_analyse_body_expansion(Context *context, Expr *call) return success; } -bool sema_expr_analyse_general_call(Context *context, Type *to, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro) +bool sema_expr_analyse_general_call(Context *context, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro, bool failable) { int force_inline = -1; VECEACH(expr->call_expr.attributes, i) @@ -1923,7 +1842,7 @@ bool sema_expr_analyse_general_call(Context *context, Type *to, Expr *expr, Decl return false; } expr->call_expr.func_ref = decl; - return sema_expr_analyse_macro_call(context, to, expr, struct_var, decl); + return sema_expr_analyse_macro_call(context, expr, struct_var, decl, failable); case DECL_VAR: if (is_macro) { @@ -1931,7 +1850,7 @@ bool sema_expr_analyse_general_call(Context *context, Type *to, Expr *expr, Decl return false; } assert(struct_var == NULL); - return sema_expr_analyse_var_call(context, to, expr, decl); + return sema_expr_analyse_var_call(context, expr, decl, failable); case DECL_FUNC: if (is_macro) { @@ -1941,7 +1860,7 @@ bool sema_expr_analyse_general_call(Context *context, Type *to, Expr *expr, Decl expr->call_expr.func_ref = decl; expr->call_expr.force_inline = force_inline == 1; expr->call_expr.force_noinline = force_inline == 0; - return sema_expr_analyse_func_call(context, to, expr, decl, struct_var); + return sema_expr_analyse_func_call(context, expr, decl, struct_var, failable); case DECL_GENERIC: if (is_macro) { @@ -1949,7 +1868,7 @@ bool sema_expr_analyse_general_call(Context *context, Type *to, Expr *expr, Decl return false; } expr->call_expr.func_ref = decl; - return sema_expr_analyse_generic_call(context, to, expr, struct_var, decl); + return sema_expr_analyse_generic_call(context, expr, struct_var, decl, failable); case DECL_POISONED: return false; default: @@ -1959,17 +1878,16 @@ bool sema_expr_analyse_general_call(Context *context, Type *to, Expr *expr, Decl } -static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_call(Context *context, Expr *expr) { - expr->pure = false; Expr *func_expr = expr->call_expr.function; - if (!sema_analyse_expr_value(context, NULL, func_expr)) return false; + if (!sema_analyse_expr_lvalue(context, func_expr)) return false; if (func_expr->expr_kind == EXPR_MACRO_BODY_EXPANSION) { return sema_analyse_body_expansion(context, expr); } - expr->failable = func_expr->failable; + bool failable = func_expr->type && IS_FAILABLE(func_expr); Decl *decl; Expr *struct_var = NULL; bool macro = false; @@ -2006,7 +1924,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr return false; } decl = decl_flatten(decl); - return sema_expr_analyse_general_call(context, to, expr, decl, struct_var, macro); + return sema_expr_analyse_general_call(context, expr, decl, struct_var, macro, failable); } @@ -2014,17 +1932,19 @@ static bool expr_check_index_in_range(Context *context, Type *type, Expr *index_ { assert(type == type->canonical); if (index_expr->expr_kind != EXPR_CONST) return true; - if (!bigint_fits_in_bits(&index_expr->const_expr.i, 64, true)) + + Int index = index_expr->const_expr.ixx; + if (!int_fits(index, TYPE_I64)) { - SEMA_ERROR(index_expr, "Index does not fit into an 64-signed integer."); + SEMA_ERROR(index_expr, "The index cannot be stored in a 64-signed integer, which isn't supported."); return false; } - int64_t index = bigint_as_signed(&index_expr->const_expr.i); - if (from_end && index < 0) + if (from_end && int_is_neg(index)) { SEMA_ERROR(index_expr, "Negative numbers are not allowed when indexing from the end."); return false; } + ArrayIndex idx = index.i.low; switch (type->type_kind) { case TYPE_POINTER: @@ -2037,22 +1957,22 @@ static bool expr_check_index_in_range(Context *context, Type *type, Expr *index_ bool is_vector = type->type_kind == TYPE_VECTOR; if (from_end) { - index = len - index; + idx = len - idx; } // Checking end can only be done for arrays. - if (end_index && index >= len) + if (end_index && idx >= len) { SEMA_ERROR(index_expr, is_vector ? "End index out of bounds, was %lld, exceeding vector width %lld." : "Array end index out of bounds, was %lld, exceeding array length %lld.", - (long long)index, (long long)len); + (long long)idx, (long long)len); return false; } - if (!end_index && index >= len) + if (!end_index && idx >= len) { SEMA_ERROR(index_expr, is_vector ? "Index out of bounds, was %lld, exceeding max vector width %lld." - : "Array index out of bounds, was %lld, exceeding max array index %lld.", (long long)index, (long long)len - 1); + : "Array index out of bounds, was %lld, exceeding max array index %lld.", (long long)idx, (long long)len - 1); return false; } break; @@ -2062,18 +1982,18 @@ static bool expr_check_index_in_range(Context *context, Type *type, Expr *index_ // If not from end, just check the negative values. if (!from_end) break; // From end we can only do sanity checks ^0 is invalid for non-end index. ^-1 and less is invalid for all. - if (index == 0 && !end_index) + if (idx == 0 && !end_index) { SEMA_ERROR(index_expr, "Array index out of bounds, index from end (%lld) must be greater than zero or it will exceed the max array index.", - (long long) index); + (long long) idx); return false; } return true; default: UNREACHABLE } - if (index < 0) + if (idx < 0) { SEMA_ERROR(index_expr, "Array index out of bounds, using a negative array index is only allowed with pointers."); return false; @@ -2098,61 +2018,93 @@ static Type *sema_expr_find_indexable_type_recursively(Type **type, Expr **paren } static inline bool sema_expr_analyse_subscript(Context *context, Expr *expr) { - Expr *subscripted = expr->subscript_expr.expr; - if (!sema_analyse_expr_value(context, NULL, subscripted)) return false; - - Expr *index = expr->subscript_expr.index; - if (!sema_analyse_expr(context, NULL, index)) return false; - - expr->failable = expr->subscript_expr.expr->failable; assert(expr->expr_kind == EXPR_SUBSCRIPT); - Type *type = type_flatten(subscripted->type); - Type *current_type = type; + + // 1. Evaluate the expression to index. + Expr *subscripted = expr->subscript_expr.expr; + if (!sema_analyse_expr_lvalue(context, subscripted)) return false; + + // 2. Evaluate the index. + Expr *index = expr->subscript_expr.index; + if (!sema_analyse_expr(context, index)) return false; + + // 3. Check failability due to value. + bool failable = IS_FAILABLE(subscripted); + + + Type *underlying_type = type_flatten(subscripted->type); + Type *current_type = underlying_type; Expr *current_expr = subscripted; - if (type == type_complist) + // 4. If we are indexing into a complist + if (current_type == type_complist) { - assert(current_expr->expr_kind == EXPR_CT_IDENT); - current_expr = current_expr->ct_ident_expr.decl->var.init_expr; + // 4a. This may either be an initializer list or a CT value + while (current_expr->expr_kind == EXPR_CT_IDENT) current_expr = current_expr->ct_ident_expr.decl->var.init_expr; + assert(current_expr->expr_kind == EXPR_INITIALIZER_LIST); + + // 4b. Now we need to check that we actually have a valid type. if (index->expr_kind != EXPR_CONST || index->const_expr.const_kind != CONST_INTEGER) { SEMA_ERROR(index, "To subscript a compile time list a compile time integer index is needed."); return false; } - if (!bigint_fits_in_bits(&index->const_expr.i, 64, true)) + // 4c. And that it's in range. + if (int_is_neg(index->const_expr.ixx)) { - SEMA_ERROR(index, "Index does not fit into an 64-signed integer."); + SEMA_ERROR(index, "The index may not be negative."); return false; } - int64_t i = bigint_as_signed(&index->const_expr.i); - unsigned count = vec_size(current_expr->initializer_list); + int64_t size = vec_size(current_expr->initializer_list); + assert(size >= 0 && "Unexpected overflow"); + if (!int_fits(index->const_expr.ixx, TYPE_I64)) + { + SEMA_ERROR(index, "The index is out of range.", size); + return false; + } + int64_t i = int_to_i64(index->const_expr.ixx); if (expr->subscript_expr.from_back) { - i = count - i; + i = size - i; } - if (i < 0 || i >= count) + if (i < 0 || i >= size) { - SEMA_ERROR(index, "Index %lld is out of range.", (long long)i); + if (expr->subscript_expr.from_back) + { + SEMA_ERROR(index, + size > 1 + ? "An index of '%lld' from the end is out of range, a value between 1 and %lld was expected." + : "An index of '%lld' from the end is out of range, a value of %lld was expected.", + (long long)(size - i), + (long long)size); + } + else + { + SEMA_ERROR(index, + size > 1 + ? "An index of '%lld' is out of range, a value between 0 and %lld was expected." + : "An index of '%lld' is out of range, a value of %lld was expected.", + (long long)i, + (long long)size - 1); + } return false; } Expr *indexed_expr = current_expr->initializer_list[i]; - if (!sema_cast_rvalue(context, NULL, indexed_expr)) return false; + if (!sema_cast_rvalue(context, indexed_expr)) return false; expr_replace(expr, indexed_expr); return true; } - if (!sema_cast_rvalue(context, NULL, current_expr)) return false; + if (!sema_cast_rvalue(context, current_expr)) return false; Type *inner_type = sema_expr_find_indexable_type_recursively(¤t_type, ¤t_expr); if (!inner_type) { - SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(type)); + SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(subscripted->type)); return false; } - expr->pure = index->pure & subscripted->pure; - // Cast to an appropriate type for index. if (!expr_cast_to_index(index)) return false; @@ -2160,18 +2112,16 @@ static inline bool sema_expr_analyse_subscript(Context *context, Expr *expr) if (!expr_check_index_in_range(context, current_type, index, false, expr->subscript_expr.from_back)) return false; expr->subscript_expr.expr = current_expr; - expr->failable |= index->failable; - expr_set_type(expr, inner_type); + expr->type = type_get_opt_fail(inner_type, failable); return true; } static inline bool sema_expr_analyse_slice(Context *context, Expr *expr) { - if (!sema_analyse_expr(context, NULL, expr->slice_expr.expr)) return false; - expr->failable = expr->slice_expr.expr->failable; + if (!sema_analyse_expr(context, expr->slice_expr.expr)) return false; + bool failable = IS_FAILABLE(expr->slice_expr.expr); assert(expr->expr_kind == EXPR_SLICE); Expr *subscripted = expr->slice_expr.expr; - expr->pure = subscripted->pure; Type *type = type_flatten(subscripted->type); Expr *start = expr->slice_expr.start; Expr *end = expr->slice_expr.end; @@ -2186,10 +2136,8 @@ static inline bool sema_expr_analyse_slice(Context *context, Expr *expr) } expr->slice_expr.expr = current_expr; - if (!sema_analyse_expr(context, type_int, start)) return false; - expr->pure &= start->pure; - if (end && !sema_analyse_expr(context, type_int, end)) return false; - expr->pure &= !end || end->pure; + if (!sema_analyse_expr(context, start)) return false; + if (end && !sema_analyse_expr(context, end)) return false; // Fix index sizes if (!expr_cast_to_index(start)) return false; @@ -2221,19 +2169,15 @@ static inline bool sema_expr_analyse_slice(Context *context, Expr *expr) { if (type->type_kind == TYPE_ARRAY) { - BigInt len; - bigint_init_unsigned(&len, type->array.len); - BigInt result; + Int128 len = { 0, type->array.len }; if (expr->slice_expr.start_from_back) { - bigint_sub(&result, &len, &start->const_expr.i); - start->const_expr.i = result; + start->const_expr.ixx.i = i128_sub(len, start->const_expr.ixx.i); expr->slice_expr.start_from_back = false; } if (expr->slice_expr.end_from_back) { - bigint_sub(&result, &len, &end->const_expr.i); - end->const_expr.i = result; + end->const_expr.ixx.i = i128_sub(len, end->const_expr.ixx.i); expr->slice_expr.end_from_back = false; } } @@ -2255,26 +2199,26 @@ static inline bool sema_expr_analyse_slice(Context *context, Expr *expr) } } - expr->failable |= start->failable; - expr_set_type(expr, type_get_subarray(inner_type)); + + expr->type = type_get_opt_fail(type_get_subarray(inner_type), failable); return true; } -static inline bool sema_expr_analyse_group(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_group(Context *context, Expr *expr) { - if (!sema_analyse_expr(context, NULL, expr->group_expr)) return false; + if (!sema_analyse_expr(context, expr->group_expr)) return false; *expr = *expr->group_expr; return true; } -static inline void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value) +static inline void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value, bool narrowable) { expr_to_rewrite->expr_kind = EXPR_CONST; expr_const_set_int(&expr_to_rewrite->const_expr, value, type->canonical->type_kind); - expr_set_type(expr_to_rewrite, type); - expr_to_rewrite->pure = true; + expr_to_rewrite->type = type; + expr_to_rewrite->const_expr.narrowable = narrowable; expr_to_rewrite->resolve_status = RESOLVE_DONE; } @@ -2285,21 +2229,11 @@ static inline void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *str expr_to_rewrite->const_expr.const_kind = CONST_STRING; expr_to_rewrite->const_expr.string.chars = (char *)string; expr_to_rewrite->const_expr.string.len = (int)strlen(string); - expr_to_rewrite->pure = true; expr_to_rewrite->resolve_status = RESOLVE_DONE; - expr_set_type(expr_to_rewrite, type_compstr); + expr_to_rewrite->type = type_compstr; } -static bool sema_expr_analyse_typeinfo(Context *context, Expr *expr) -{ - expr->pure = true; - - TypeInfo *type_info = expr->type_expr; - if (!sema_resolve_type_info(context, type_info)) return false; - expr_set_type(expr, type_typeinfo); - return true; -} @@ -2408,13 +2342,12 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T return false; } - expr->pure = true; TokenType type = TOKTYPE(identifier_token); // 2. Handle Foo.typeid => return a typeid expression. if (type == TOKEN_TYPEID) { - expr_set_type(expr, type_typeid); + expr->type = type_typeid; expr->expr_kind = EXPR_CONST; expr->const_expr.const_kind = CONST_TYPEID; expr->const_expr.typeid = parent->type->canonical; @@ -2431,26 +2364,22 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T if (name == kw_nan) { expr->expr_kind = EXPR_CONST; - expr->const_expr.float_type = canonical->type_kind; expr->const_expr.const_kind = CONST_FLOAT; #if LONG_DOUBLE - expr->const_expr.f = nanl(""); + expr->const_expr.fxx = (Float) { nanl(""), canonical->type_kind }; #else - expr->const_expr.f = nan(""); + expr->const_expr.fxx = (Float) { nan(""), canonical->type_kind }; #endif - expr_set_type(expr, parent->type); - expr->pure = true; + expr->type = parent->type; expr->resolve_status = RESOLVE_DONE; return true; } if (name == kw_inf) { expr->expr_kind = EXPR_CONST; - expr->const_expr.float_type = parent->type->canonical->type_kind;; expr->const_expr.const_kind = CONST_FLOAT; - expr->const_expr.f = INFINITY; - expr_set_type(expr, parent->type->canonical); - expr->pure = true; + expr->const_expr.fxx = (Float) { INFINITY, parent->type->canonical->type_kind }; + expr->type = parent->type->canonical; expr->resolve_status = RESOLVE_DONE; return true; } @@ -2478,7 +2407,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T } if (name == kw_elements) { - expr_rewrite_to_int_const(expr, type_compint, vec_size(decl->enums.values)); + expr_rewrite_to_int_const(expr, type_isize, vec_size(decl->enums.values), true); return true; } if (name == kw_max) @@ -2486,7 +2415,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T Expr *max = enum_minmax_value(decl, BINARYOP_GT); if (!max) { - expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0); + expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0, false); return true; } expr_replace(expr, max); @@ -2497,7 +2426,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T Expr *min = enum_minmax_value(decl, BINARYOP_LT); if (!min) { - expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0); + expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0, false); return true; } expr_replace(expr, min); @@ -2518,7 +2447,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T } if (name == kw_elements) { - expr_rewrite_to_int_const(expr, type_compint, vec_size(decl->enums.values)); + expr_rewrite_to_int_const(expr, type_isize, vec_size(decl->enums.values), true); return true; } if (name == kw_max) @@ -2526,7 +2455,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T Expr *max = enum_minmax_value(decl, BINARYOP_GT); if (!max) { - expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0); + expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0, false); return true; } expr_replace(expr, max); @@ -2537,7 +2466,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T Expr *min = enum_minmax_value(decl, BINARYOP_LT); if (!min) { - expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0); + expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0, false); return true; } expr_replace(expr, min); @@ -2582,8 +2511,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T expr->expr_kind = EXPR_TYPEINFO; expr->type_expr->type = member->type; expr->type_expr->resolve_status = RESOLVE_DONE; - expr_set_type(expr, type_typeinfo); - expr->pure = true; + expr->type = type_typeinfo; return true; } @@ -2597,7 +2525,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T expr->identifier_expr.identifier = identifier_token; expr->expr_kind = EXPR_IDENTIFIER; expr->identifier_expr.decl = member; - expr_set_type(expr, member->type); + expr->type = member->type; return true; } @@ -2610,7 +2538,7 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) bool was_group = parent->expr_kind == EXPR_GROUP; // 1. Resolve the left hand - if (!sema_analyse_expr_value(context, NULL, parent)) return false; + if (!sema_analyse_expr_lvalue(context, parent)) return false; // 2. The right hand side may be a @ident or ident Expr *child = expr->access_expr.child; @@ -2628,16 +2556,16 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) // 6. Copy failability - expr->failable = parent->failable; + bool failable = IS_FAILABLE(parent); assert(expr->expr_kind == EXPR_ACCESS); assert(parent->resolve_status == RESOLVE_DONE); // 7. Is this a pointer? If so we insert a deref. - bool is_pointer = parent->type->canonical->type_kind == TYPE_POINTER; + bool is_pointer = type_no_fail(parent->type)->canonical->type_kind == TYPE_POINTER; if (is_pointer) { - if (!sema_cast_rvalue(context, NULL, parent)) return false; + if (!sema_cast_rvalue(context, parent)) return false; expr_insert_deref(expr->access_expr.parent); parent = expr->access_expr.parent; } @@ -2646,7 +2574,7 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) const char *kw = TOKSTR(identifier_token); Expr *current_parent = parent; - Type *type = parent->type->canonical; + Type *type = type_no_fail(parent->type)->canonical; Type *flat_type = type_flatten(type); CHECK_DEEPER: @@ -2657,14 +2585,13 @@ CHECK_DEEPER: { expr->expr_kind = EXPR_LEN; expr->len_expr.inner = parent; - expr->original_type = type_compint; expr->type = type_usize; expr->resolve_status = RESOLVE_DONE; return true; } if (flat_type->type_kind == TYPE_ARRAY) { - expr_rewrite_to_int_const(expr, type_compint, flat_type->array.len); + expr_rewrite_to_int_const(expr, type_isize, flat_type->array.len, true); return true; } } @@ -2672,7 +2599,7 @@ CHECK_DEEPER: // 9. At this point we may only have distinct, struct, union, error, enum if (!type_may_have_sub_elements(type)) { - SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", kw, type_to_error_string(parent->type)); + SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", kw, type_to_error_string(type)); return false; } @@ -2726,8 +2653,7 @@ CHECK_DEEPER: // 13. Copy properties. expr->access_expr.parent = current_parent; - expr->pure = expr->access_expr.parent->pure; - expr_set_type(expr, member->type); + expr->type = type_get_opt_fail(member->type, failable); expr->access_expr.ref = member; return true; } @@ -2779,7 +2705,7 @@ static Decl *sema_resolve_element_for_name(Decl** decls, DesignatorElement **ele static int64_t sema_analyse_designator_index(Context *context, Expr *index) { - if (!sema_analyse_expr_value(context, type_uint, index)) + if (!sema_analyse_expr_lvalue(context, index)) { return -1; } @@ -2794,12 +2720,12 @@ static int64_t sema_analyse_designator_index(Context *context, Expr *index) SEMA_ERROR(index, "The index must be a constant value."); return -1; } - if (!bigint_fits_in_bits(&index->const_expr.i, 64, true)) + if (!int_fits(index->const_expr.ixx, TYPE_I64)) { SEMA_ERROR(index, "The value of the index does not fit in a long."); return -1; } - int64_t index_val = bigint_as_signed(&index->const_expr.i); + int64_t index_val = int_to_i64(index->const_expr.ixx); if (index_val < 0) { SEMA_ERROR(index, "Negative index values is not allowed."); @@ -2900,7 +2826,6 @@ static Type *sema_expr_analyse_designator(Context *context, Type *current, Expr } current = new_current; } - expr->pure = is_constant; return current; } @@ -3203,18 +3128,16 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass Type *original = assigned->canonical; bool is_structlike = type_is_structlike(assigned->canonical); - initializer->pure = true; ArrayIndex max_index = -1; + bool failable = false; VECEACH(init_expressions, i) { Expr *expr = init_expressions[i]; Type *result = sema_expr_analyse_designator(context, original, expr, &max_index); if (!result) return false; - if (!sema_analyse_expr_of_required_type(context, result, expr->designator_expr.value, true)) return false; - expr->pure &= expr->designator_expr.value->pure; - expr->failable |= expr->designator_expr.value->failable; + if (!sema_analyse_assigned_expr(context, result, expr->designator_expr.value, true)) return false; + failable = failable || IS_FAILABLE(expr->designator_expr.value); expr->resolve_status = RESOLVE_DONE; - initializer->pure &= expr->pure; } if (!is_structlike && initializer->type->type_kind == TYPE_INFERRED_ARRAY) @@ -3238,8 +3161,6 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass */ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, Decl *assigned, Expr *initializer) { - initializer->pure = true; - Expr **elements = initializer->initializer_list; Decl **members = assigned->strukt.members; unsigned size = vec_size(elements); @@ -3259,6 +3180,7 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, // 2. In case of a union, only expect a single entry. if (assigned->decl_kind == DECL_UNION) expected_members = 1; + bool failable = false; // 3. Loop through all elements. VECEACH(elements, i) @@ -3273,10 +3195,11 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, return false; } // 5. We know the required type, so resolve the expression. - if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i], 0)) return false; - initializer->pure &= element->pure; - initializer->failable |= element->failable; + if (!sema_analyse_assigned_expr(context, members[i]->type, elements[i], true)) return false; + failable = failable || IS_FAILABLE(element); } + assert(initializer->type); + if (failable) initializer->type = type_get_failable(initializer->type); // 6. There's the case of too few values as well. Mark the last field as wrong. if (expected_members > size) @@ -3318,8 +3241,6 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, */ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, Type *assigned, Expr *initializer) { - initializer->pure = true; - Expr **elements = initializer->initializer_list; Type *inner_type = type_get_indexed_type(assigned); @@ -3337,6 +3258,7 @@ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, T return false; } + bool failable = false; VECEACH(elements, i) { Expr *element = elements[i]; @@ -3345,10 +3267,11 @@ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, T SEMA_ERROR(element, "Too many elements in initializer, expected only %d.", expected_members); return false; } - if (!sema_analyse_expr_of_required_type(context, inner_type, element, true)) return false; - initializer->failable |= element->failable; - initializer->pure &= element->pure; + if (!sema_analyse_assigned_expr(context, inner_type, element, true)) return false; + failable = failable || IS_FAILABLE(element); } + assert(initializer->type); + if (failable) initializer->type = type_get_failable(initializer->type); if (expected_members > size) { @@ -3384,34 +3307,40 @@ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, T static inline bool sema_expr_analyse_untyped_initializer(Context *context, Expr *initializer) { - initializer->pure = true; - Expr **elements = initializer->initializer_list; Type *element_type = NULL; bool no_common_elements = false; unsigned element_count = vec_size(elements); + bool is_const = true; + Expr *failable_expr = NULL; for (unsigned i = 0; i < element_count; i++) { Expr *element = elements[i]; - if (!sema_analyse_expr(context, NULL, element)) return false; - initializer->failable |= element->failable; - initializer->pure &= element->pure; + if (!sema_analyse_expr(context, element)) return false; + if (is_const && element->expr_kind != EXPR_CONST) is_const = false; + if (!failable_expr && IS_FAILABLE(element)) failable_expr = element; if (no_common_elements) continue; + Type *current_element_type = type_no_fail(element->type); if (element_type == NULL) { element_type = element->type; continue; } - element_type = type_find_max_type(element->type, element_type); + element_type = type_find_max_type(element->type, current_element_type); if (!element_type) no_common_elements = true; } - if (no_common_elements || type_is_ct(element_type)) + if (no_common_elements && failable_expr) { - expr_set_type(initializer, type_complist); + SEMA_ERROR(failable_expr, "An untyped initializer can't have failable values."); + return false; + } + if (no_common_elements || is_const) + { + initializer->type = type_complist; return true; } - expr_set_type(initializer, type_get_array(element_type, element_count)); + initializer->type = type_get_opt_fail(type_get_array(element_type, element_count), failable_expr != NULL); return true; } @@ -3423,7 +3352,7 @@ static inline bool sema_expr_analyse_initializer(Context *context, Type *externa if (expr->expr_kind == EXPR_DESIGNATED_INITIALIZER_LIST) { - expr_set_type(expr, external_type); + expr->type = external_type; return sema_expr_analyse_designated_initializer(context, assigned, expr); } @@ -3437,18 +3366,17 @@ static inline bool sema_expr_analyse_initializer(Context *context, Type *externa if (init_expression_count == 0) { external_type = sema_type_lower_by_size(external_type, 0); - expr_set_type(expr, external_type); + expr->type = external_type; ConstInitializer *initializer = CALLOCS(ConstInitializer); initializer->kind = CONST_INIT_ZERO; initializer->type = type_flatten(expr->type); expr_set_as_const_list(expr, initializer); - expr->pure = true; return true; } external_type = sema_type_lower_by_size(external_type, init_expression_count); assigned = sema_type_lower_by_size(assigned, init_expression_count); - expr_set_type(expr, external_type); + expr->type = external_type; if (external_type == type_complist) { @@ -3490,7 +3418,7 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to if (!sema_expr_analyse_initializer(context, type, type, expr)) return false; expr->resolve_status = RESOLVE_DONE; expr_insert_addr(expr); - if (!sema_analyse_expr(context, NULL, expr)) return false; + if (!sema_analyse_expr(context, expr)) return false; return cast(expr, to); } case TYPE_POINTER: @@ -3506,7 +3434,7 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to -static inline bool sema_expr_analyse_expr_list(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_expr_list(Context *context, Expr *expr) { bool success = true; ByteSize last = vec_size(expr->expression_list) - 1; @@ -3515,43 +3443,42 @@ static inline bool sema_expr_analyse_expr_list(Context *context, Type *to, Expr VECEACH(expr->expression_list, i) { Expr *checked_expr = expr->expression_list[i]; - success &= sema_analyse_expr_of_required_type(context, i == last ? to : NULL, checked_expr, 0); - expr->failable |= checked_expr->failable; - pure &= checked_expr->pure; + success &= sema_analyse_expr(context, checked_expr); } - expr->pure = pure; + expr->type = expr->expression_list[last]->type; return success; } -static inline bool sema_expr_analyse_cast(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_cast(Context *context, Expr *expr) { Expr *inner = expr->cast_expr.expr; bool success = sema_resolve_type_info(context, expr->cast_expr.type_info); - success &= sema_analyse_expr(context, NULL, inner); - expr->pure = inner->pure; - if (!success) return false; + if (!sema_analyse_expr(context, inner) || !success) return false; Type *target_type = expr->cast_expr.type_info->type; if (!cast_may_explicit(inner->type, target_type)) { + if (inner->expr_kind == EXPR_CONST && type_is_integer(inner->type->canonical) && target_type->canonical->type_kind == TYPE_POINTER) + { + goto OK; + } SEMA_ERROR(expr, "Cannot cast %s to %s.", type_quoted_error_string(inner->type), type_quoted_error_string(target_type)); return false; } - + OK: cast(inner, target_type); - expr_replace(expr, inner); - return true; } -static inline bool sema_expr_analyse_slice_assign(Context *context, Expr *expr, Type *left_type, Expr *right, ExprFailableStatus lhs_is_failable) +static inline bool sema_expr_analyse_slice_assign(Context *context, Expr *expr, Type *left_type, Expr *right, bool is_unwrapped) { // 1. Evaluate right side to required type. - if (!sema_analyse_expr_of_required_type(context, left_type->array.base, right, lhs_is_failable != FAILABLE_NO)) return false; + Type *left_no_fail = type_no_fail(left_type); + if (!sema_analyse_assigned_expr(context, left_no_fail->array.base, right, TYPE_IS_FAILABLE(left_type))) return false; Expr *left = expr->binary_expr.left; - expr_copy_types(expr, right); + expr->type = right->type; expr->expr_kind = EXPR_SLICE_ASSIGN; expr->slice_assign_expr.left = left; expr->slice_assign_expr.right = right; @@ -3559,41 +3486,41 @@ static inline bool sema_expr_analyse_slice_assign(Context *context, Expr *expr, return true; } -bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *left_type, Expr *right, ExprFailableStatus lhs_is_failable) +bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *left_type, Expr *right, bool is_unwrapped) { if (expr && expr->binary_expr.left->expr_kind == EXPR_SLICE) { - return sema_expr_analyse_slice_assign(context, expr, left_type, right, lhs_is_failable); + return sema_expr_analyse_slice_assign(context, expr, left_type, right, is_unwrapped); } // 1. Evaluate right side to required type. - if (!sema_analyse_expr(context, left_type, right)) return false; - if (right->failable && lhs_is_failable != FAILABLE_YES) + Type *no_fail_left_type = type_no_fail(left_type); + if (!sema_analyse_inferred_expr(context, no_fail_left_type, right)) return false; + if (IS_FAILABLE(right) && !TYPE_IS_FAILABLE(left_type)) { - if (lhs_is_failable == FAILABLE_UNWRAPPED) + if (is_unwrapped) { SEMA_ERROR(expr->binary_expr.left, "The variable is unwrapped in this context, if you don't want to unwrap it, use () around the variable to suppress unwrapping, like 'catch err = (x)' and 'try (x)'."); return false; } - if (!left_type) left_type = right->type; - SEMA_ERROR(right, "'%s!' cannot be converted into '%s'.", type_to_error_string(right->type), type_to_error_string(left_type)); + if (!left_type) left_type = type_no_fail(right->type); + SEMA_ERROR(right, "%s cannot be converted to %s.", type_quoted_error_string(right->type), type_quoted_error_string(left_type)); return false; } - + Type *right_type = type_no_fail(right->type)->canonical; // 2. Evaluate right hand side, making special concession for inferred arrays. - do - { - if (!left_type) break; - if (left_type->canonical->type_kind == TYPE_INFERRED_ARRAY && right->type->canonical->type_kind == TYPE_ARRAY) - { - if (left_type->canonical->array.base == right->type->canonical->array.base) break; - } - assert(right->type->canonical->type_kind != TYPE_INFERRED_ARRAY); - if (!cast_implicit(right, left_type)) return false; - } while (0); + if (!no_fail_left_type) goto DONE; + + if (no_fail_left_type->canonical->type_kind == TYPE_INFERRED_ARRAY && right_type->type_kind == TYPE_ARRAY) + { + if (no_fail_left_type->canonical->array.base == right_type->array.base) goto DONE; + } + if (!cast_implicit_ignore_failable(right, no_fail_left_type)) return false; + + DONE: // 3. Set the result to the type on the right side. - if (expr) expr_copy_types(expr, right); + if (expr) expr->type = right->type; return true; } @@ -3650,7 +3577,7 @@ static bool sema_expr_analyse_ct_identifier_assign(Context *context, Expr *expr, if (!sema_expr_analyse_ct_identifier_lvalue(context, left)) return false; // 3. Evaluate right side to required type. - if (!sema_expr_analyse_assign_right_side(context, expr, left->type, right, true)) return false; + if (!sema_expr_analyse_assign_right_side(context, expr, left->type, right, false)) return false; left->ct_ident_expr.decl->var.init_expr = right; expr_replace(expr, right); @@ -3669,7 +3596,7 @@ static bool sema_expr_analyse_ct_type_identifier_assign(Context *context, Expr * TokenId token = info->unresolved.name_loc; - if (!sema_analyse_expr_value(context, NULL, right)) return false; + if (!sema_analyse_expr_lvalue(context, right)) return false; if (right->expr_kind != EXPR_TYPEINFO) { @@ -3701,9 +3628,6 @@ static bool sema_expr_analyse_ct_type_identifier_assign(Context *context, Expr * */ static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, Expr *right) { - expr->pure = false; - - // 1. Evaluate left side if (left->expr_kind == EXPR_CT_IDENT) { @@ -3715,7 +3639,7 @@ static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, E return sema_expr_analyse_ct_type_identifier_assign(context, expr, left, right); } - if (!sema_analyse_expr_value(context, NULL, left)) return false; + if (!sema_analyse_expr_lvalue(context, left)) return false; // 2. Check assignability @@ -3725,12 +3649,12 @@ static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, E return false; } - ExprFailableStatus failable_status = expr_is_failable(left); + bool is_unwrapped_var = expr_is_unwrapped_ident(left); // 3. Evaluate right side to required type. - if (!sema_expr_analyse_assign_right_side(context, expr, left->type, right, failable_status)) return false; + if (!sema_expr_analyse_assign_right_side(context, expr, left->type, right, is_unwrapped_var)) return false; - if (failable_status == FAILABLE_UNWRAPPED && right->failable) + if (is_unwrapped_var && IS_FAILABLE(right)) { return sema_rewrap_var(context, left->identifier_expr.decl); } @@ -3753,13 +3677,13 @@ static bool sema_expr_analyse_ct_common_assign(Context *context, Expr *expr, Exp Expr *left_value = left_var->var.init_expr; assert(left_value); - assert(!left_value->failable); + assert(!IS_FAILABLE(left_value)); expr->binary_expr.left = left_value; expr->binary_expr.operator = binaryop_assign_base_op(expr->binary_expr.operator); - if (!sema_expr_analyse_binary(context, NULL, expr)) return false; + if (!sema_expr_analyse_binary(context, expr)) return false; if (expr->expr_kind != EXPR_CONST) { @@ -3779,15 +3703,13 @@ static bool sema_expr_analyse_ct_common_assign(Context *context, Expr *expr, Exp */ static bool sema_expr_analyse_common_assign(Context *context, Expr *expr, Expr *left, Expr *right, bool int_only) { - expr->pure = false; - if (left->expr_kind == EXPR_CT_IDENT) { return sema_expr_analyse_ct_common_assign(context, expr, left); } // 1. Analyse left side. - if (!sema_analyse_expr_value(context, NULL, left)) return false; + if (!sema_analyse_expr_lvalue(context, left)) return false; // 2. Verify that the left side is assignable. if (!expr_is_ltype(left)) @@ -3796,22 +3718,24 @@ static bool sema_expr_analyse_common_assign(Context *context, Expr *expr, Expr * return false; } + Type *no_fail = type_no_fail(left->type); + // 3. If this is only defined for ints (*%, ^= |= &= %=) verify that this is an int. - if (int_only && !type_is_any_integer(left->type)) + if (int_only && !type_is_integer(no_fail)) { SEMA_ERROR(left, "Expected an integer here."); return false; } // 4. In any case, these ops are only defined on numbers. - if (!type_underlying_is_numeric(left->type)) + if (!type_underlying_is_numeric(no_fail)) { SEMA_ERROR(left, "Expected a numeric type here."); 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, expr_is_failable(left))) return false; + if (!sema_analyse_assigned_expr(context, no_fail, right, IS_FAILABLE(left))) return false; // 6. Check for zero in case of div or mod. if (right->expr_kind == EXPR_CONST) @@ -3821,14 +3745,14 @@ static bool sema_expr_analyse_common_assign(Context *context, Expr *expr, Expr * switch (right->const_expr.const_kind) { case CONST_INTEGER: - if (bigint_cmp_zero(&right->const_expr.i) == CMP_EQ) + if (int_is_zero(right->const_expr.ixx)) { SEMA_ERROR(right, "Division by zero not allowed."); return false; } break; case CONST_FLOAT: - if (right->const_expr.f == 0) + if (right->const_expr.fxx.f == 0) { SEMA_ERROR(right, "Division by zero not allowed."); return false; @@ -3843,7 +3767,7 @@ static bool sema_expr_analyse_common_assign(Context *context, Expr *expr, Expr * switch (right->const_expr.const_kind) { case CONST_INTEGER: - if (bigint_cmp_zero(&right->const_expr.i) == CMP_EQ) + if (int_is_zero(right->const_expr.ixx)) { SEMA_ERROR(right, "% by zero not allowed."); return false; @@ -3856,7 +3780,7 @@ static bool sema_expr_analyse_common_assign(Context *context, Expr *expr, Expr * } // 7. Assign type - expr_copy_types(expr, left); + expr->type = left->type; return true; } @@ -3873,7 +3797,7 @@ static bool sema_expr_analyse_add_sub_assign(Context *context, Expr *expr, Expr } // 1. Analyse the left hand side - if (!sema_analyse_expr(context, NULL, left)) return false; + if (!sema_analyse_expr(context, left)) return false; // 2. Ensure the left hand side is assignable if (!expr_is_ltype(left)) @@ -3884,22 +3808,19 @@ static bool sema_expr_analyse_add_sub_assign(Context *context, Expr *expr, Expr Type *left_type_canonical = left->type->canonical; - // 4. Attempt to analyse and cast this side to the same type if possible. - if (!sema_analyse_expr(context, left->type, right)) return false; + // 4. Analyse right hand side + REMINDER("Possible deep cast here."); + if (!sema_analyse_expr(context, right)) return false; // 3. Copy type & set properties. - expr_copy_types(expr, left); - expr->pure = false; - expr->failable = left->failable | right->failable; + expr->type = left->type; + bool failable = IS_FAILABLE(left) || IS_FAILABLE(right); // 5. In the pointer case we have to treat this differently. if (left_type_canonical->type_kind == TYPE_POINTER) { - // 6. Convert any compile time values to runtime - if (!cast_implicitly_to_runtime(right)) return false; - // 7. Finally, check that the right side is indeed an integer. if (!type_is_integer(right->type->canonical)) { @@ -3920,7 +3841,8 @@ static bool sema_expr_analyse_add_sub_assign(Context *context, Expr *expr, Expr SEMA_ERROR(left, "Expected a numeric type here."); return false; } - + REMINDER("Check if can remove"); + expr->type = type_get_opt_fail(expr->type, failable); return true; } @@ -3938,6 +3860,8 @@ static Type *numeric_arithmetic_promotion(Type *type) case TYPE_F16: // Promote F16 to a real type. return type_float; + case TYPE_FAILABLE: + UNREACHABLE default: return type; } @@ -3945,7 +3869,6 @@ static Type *numeric_arithmetic_promotion(Type *type) static bool binary_arithmetic_promotion(Context *context, Expr *left, Expr *right, Type *left_type, Type *right_type, Expr *parent, const char *error_message) { - Type *max = numeric_arithmetic_promotion(type_find_max_type(left_type, right_type)); if (!max || !type_underlying_is_numeric(max)) { @@ -3956,14 +3879,14 @@ static bool binary_arithmetic_promotion(Context *context, Expr *left, Expr *righ SEMA_ERROR(parent, error_message, type_quoted_error_string(left->type), type_quoted_error_string(right->type)); return false; } - return (int)cast_implicit(left, max) & (int)cast_implicit(right, max); + return cast_implicit_ignore_failable(left, max) && cast_implicit_ignore_failable(right, max); } static bool sema_check_int_type_fit(Expr *expr, Type *target_type) { if (!target_type) return true; Type *type = expr->type->canonical; - if (!type_is_any_integer(target_type->canonical) || !type_is_any_integer(type)) return true; + if (!type_is_integer(target_type->canonical) || !type_is_integer(type)) return true; if (type_size(type) > type_size(target_type)) { SEMA_ERROR(expr, "A '%s' cannot implicitly convert into '%s'.", type_to_error_string(expr->type), type_to_error_string(target_type)); @@ -4008,17 +3931,17 @@ static Type *defer_iptr_cast(Expr *maybe_pointer, Expr *maybe_diff) * Analyse a - b * @return true if analysis succeeded */ -static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +static bool sema_expr_analyse_sub(Context *context, Expr *expr, Expr *left, Expr *right) { // 1. Analyse a and b. - if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false; + if (!sema_expr_analyse_binary_subexpr(context, left, right)) return false; // Do we have (iptr)(ptr) - rhs? If so we change it to // (iptr)((char*)(ptr) - 1) Type *cast_to_iptr = defer_iptr_cast(left, right); - Type *left_type = left->type->canonical; - Type *right_type = right->type->canonical; + Type *left_type = type_no_fail(left->type)->canonical; + Type *right_type = type_no_fail(right->type)->canonical; // 2. Handle the ptr - x and ptr - other_pointer if (left_type->type_kind == TYPE_POINTER) @@ -4035,19 +3958,15 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * } // 3b. Set the type - expr_set_type(expr, type_iptrdiff); + expr->type = type_iptrdiff; - // 3c. Set all other properties - expr_unify_binary_properties(expr, left, right); - - // 3d. Otherwise check against assigned type so for example short x = &p - &q is an error. - return sema_check_int_type_fit(expr, to); + return true; } right_type = right->type->canonical; // 4. Check that the right hand side is an integer. - if (!type_is_any_integer(right_type)) + if (!type_is_integer(right_type)) { SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(right_type), type_to_error_string(left_type)); return false; @@ -4064,8 +3983,7 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * if (!cast_implicit(right, type_iptrdiff)) return true; // 7. Assign the type of the left side. - expr_copy_types(expr, left); - expr_unify_binary_properties(expr, left, right); + expr->type = left->type; if (cast_to_iptr) { expr->resolve_status = RESOLVE_DONE; @@ -4082,19 +4000,19 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * left_type = left->type->canonical; - expr_unify_binary(expr, left, right); + expr->type = type_get_opt_fail(left->type, IS_FAILABLE(right)); // 8. Handle constant folding. - if (both_const(left, right)) + if (expr_both_const(left, right)) { expr_replace(expr, left); switch (left_type->type_kind) { case ALL_INTS: - bigint_sub(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + expr->const_expr.ixx = int_sub(left->const_expr.ixx, right->const_expr.ixx); break; case ALL_FLOATS: - expr->const_expr.f = left->const_expr.f - right->const_expr.f; + expr->const_expr.fxx = float_sub(left->const_expr.fxx, right->const_expr.fxx); break; default: UNREACHABLE @@ -4109,17 +4027,17 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * * Analyse a + b * @return true if it succeeds. */ -static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +static bool sema_expr_analyse_add(Context *context, Expr *expr, Expr *left, Expr *right) { // 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, to, left, right)) return false; + if (!sema_expr_analyse_binary_subexpr(context, left, right)) return false; Type *cast_to_iptr = defer_iptr_cast(left, right); if (!cast_to_iptr) cast_to_iptr = defer_iptr_cast(right, left); - Type *left_type = left->type->canonical; - Type *right_type = right->type->canonical; + Type *left_type = type_no_fail(left->type)->canonical; + Type *right_type = type_no_fail(right->type)->canonical; // 2. To detect pointer additions, reorder if needed if (right_type->type_kind == TYPE_POINTER && left_type->type_kind != TYPE_POINTER) @@ -4138,7 +4056,7 @@ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr * if (left_type->type_kind == TYPE_POINTER) { // 3a. Check that the other side is an integer of some sort. - if (!type_is_any_integer(right_type)) + if (!type_is_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), @@ -4155,8 +4073,7 @@ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr * (void)success; // 3c. Set the type and other properties. - expr_copy_types(expr, left); - expr_unify_binary_properties(expr, left, right); + expr->type = left->type; if (cast_to_iptr) { @@ -4174,23 +4091,22 @@ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr * } // 5. Handle the "both const" case. We should only see ints and floats at this point. - if (both_const(left, right)) + if (expr_both_const(left, right)) { expr_replace(expr, left); switch (left->const_expr.const_kind) { case CONST_INTEGER: - bigint_add(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + expr->const_expr.ixx = int_add(left->const_expr.ixx, right->const_expr.ixx); break; case CONST_FLOAT: - expr->const_expr.f = left->const_expr.f + right->const_expr.f; + expr->const_expr.fxx = float_add(left->const_expr.fxx, right->const_expr.fxx); break; default: UNREACHABLE } } - // 6. Set the type & other properties. expr_unify_binary(expr, left, right); @@ -4206,41 +4122,31 @@ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr * * * @return true if analysis worked. */ -static bool sema_expr_analyse_mult(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +static bool sema_expr_analyse_mult(Context *context, Expr *expr, Expr *left, Expr *right) { - // 1. Analyse the sub expressions. - if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false; + // 1. Analyse the sub expressions and promote to a common type + if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, "It is not possible to multiply %s by %s.")) return false; - Type *left_type = left->type->canonical; - Type *right_type = right->type->canonical; - - // 2. Perform promotion to a common type. - if (!binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, "Cannot multiply %s by %s")) - { - return false; - } - - // 3. Handle constant folding. - if (both_const(left, right)) + // 2. Handle constant folding. + if (expr_both_const(left, right)) { expr_replace(expr, left); - switch (left->const_expr.const_kind) { case CONST_INTEGER: - bigint_mul(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + expr->const_expr.ixx = int_mul(left->const_expr.ixx, right->const_expr.ixx); break; case CONST_FLOAT: - expr->const_expr.f = left->const_expr.f * right->const_expr.f; + expr->const_expr.fxx = float_mul(left->const_expr.fxx, right->const_expr.fxx); break; default: UNREACHABLE } } - // 4. Update failable / pure / type etc. - expr_unify_binary(expr, left, right); + // 3. Set the new type + expr->type = type_get_opt_fail(left->type, IS_FAILABLE(right)); return true; } @@ -4249,59 +4155,50 @@ static bool sema_expr_analyse_mult(Context *context, Type *to, Expr *expr, Expr * 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) +static bool sema_expr_analyse_div(Context *context, Expr *expr, Expr *left, Expr *right) { - // 1. Analyse sub expressions. - if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false; + // 1. Analyse sub expressions and promote to a common type + if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, "Cannot divide %s by %s.")) return false; - Type *left_type = left->type->canonical; - Type *right_type = right->type->canonical; - - // 2. Perform promotion to a common type. - if (!binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, "Cannot divide %s by %s.")) - { - return false; - } - - // 3. Check for a constant 0 on the right hand side. - if (is_const(right)) + // 2. Check for a constant 0 on the rhs. + if (IS_CONST(right)) { switch (right->const_expr.const_kind) { case CONST_INTEGER: - if (bigint_cmp_zero(&right->const_expr.i) == CMP_EQ) + if (int_is_zero(right->const_expr.ixx)) { SEMA_ERROR(right, "This expression evaluates to zero and division by zero is not allowed."); return false; } break; case CONST_FLOAT: + // This is allowed, as it will generate a NaN break; default: UNREACHABLE } } - // 4. Perform constant folding. - if (both_const(left, right)) + // 3. Perform constant folding. + if (expr_both_const(left, right)) { expr_replace(expr, left); switch (left->const_expr.const_kind) { case CONST_INTEGER: - bigint_div_floor(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + expr->const_expr.ixx = int_div(left->const_expr.ixx, right->const_expr.ixx); break; case CONST_FLOAT: - expr->const_expr.f = left->const_expr.f / right->const_expr.f; + expr->const_expr.fxx = float_div(left->const_expr.fxx, right->const_expr.fxx); break; default: UNREACHABLE } } - // 5. Done. - expr_unify_binary(expr, left, right); - + // 4. Done. + expr->type = type_get_opt_fail(left->type, IS_FAILABLE(right)); return true; } @@ -4310,37 +4207,27 @@ static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr * * Analyse a % b * @return true if analysis succeeds. */ -static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) +static bool sema_expr_analyse_mod(Context *context, Expr *expr, Expr *left, Expr *right) { - // 1. Analyse both sides. - 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; - - // 2. Perform promotion to a common type. - if (!binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, "Cannot divide %s by %s.")) - { - return false; - } + // 1. Analyse both sides and promote to a common type + if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, NULL)) return false; // 3. a % 0 is not valid, so detect it. - if (is_const(right) && bigint_cmp_zero(&right->const_expr.i) == CMP_EQ) + if (IS_CONST(right) && int_is_zero(right->const_expr.ixx)) { - SEMA_ERROR(expr->binary_expr.right, "Cannot perform %% with a constant zero."); + SEMA_ERROR(right, "Cannot perform %% with a constant zero."); return false; } // 4. Constant fold - if (both_const(left, right)) + if (expr_both_const(left, right)) { expr_replace(expr, left); // 4a. Remember this is remainder. - bigint_rem(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + expr->const_expr.ixx = int_rem(left->const_expr.ixx, right->const_expr.ixx); } - expr_unify_binary(expr, left, right); - + expr->type = type_get_opt_fail(left->type, IS_FAILABLE(right)); return true; } @@ -4348,10 +4235,10 @@ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr * * 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) +static bool sema_expr_analyse_bit(Context *context, Expr *expr, Expr *left, Expr *right) { - // 1. Convert to top down type if possible. - if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false; + // 1. Convert to common type if possible. + if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, NULL)) return false; // 2. Check that both are integers. if (!both_any_integer_or_integer_vector(left, right)) @@ -4359,30 +4246,20 @@ static bool sema_expr_analyse_bit(Context *context, Type *to, Expr *expr, Expr * 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(context, left, right, left_type, right_type, expr, NULL)) - { - return false; - } - - // 4. Do constant folding if both sides are constant. - if (both_const(left, right)) + // 3. Do constant folding if both sides are constant. + if (expr_both_const(left, right)) { expr_replace(expr, left); switch (expr->binary_expr.operator) { case BINARYOP_BIT_AND: - bigint_and(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + expr->const_expr.ixx = int_and(left->const_expr.ixx, right->const_expr.ixx); break; case BINARYOP_BIT_XOR: - bigint_xor(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + expr->const_expr.ixx = int_xor(left->const_expr.ixx, right->const_expr.ixx); break; case BINARYOP_BIT_OR: - bigint_or(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + expr->const_expr.ixx = int_or(left->const_expr.ixx, right->const_expr.ixx); break; default: UNREACHABLE; @@ -4398,92 +4275,58 @@ static bool sema_expr_analyse_bit(Context *context, Type *to, Expr *expr, Expr * * 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) +static bool sema_expr_analyse_shift(Context *context, Expr *expr, Expr *left, Expr *right) { - // 1. Analyze lhs with target type. - if (!sema_analyse_expr(context, to, left)) return false; + // 1. Analyze both sides. + if (!sema_expr_analyse_binary_subexpr(context, left, right)) return false; - // 2. Analyse rhs without target type. - if (!sema_analyse_expr(context, NULL, right)) return false; - - // 3. Only integers may be shifted. + // 2. Only integers or integer vectors may be shifted. if (!both_any_integer_or_integer_vector(left, right)) { return sema_type_error_on_binop(expr); } - // 4. Promote lhs using the usual numeric promotion. - if (!cast_implicit(left, numeric_arithmetic_promotion(left->type))) return false; + // 3. Promote lhs using the usual numeric promotion. + if (!cast_implicit_ignore_failable(left, numeric_arithmetic_promotion(type_no_fail(left->type)))) return false; - expr->pure = left->pure & right->pure; - expr->failable = left->failable | right->failable; - - // 5. For a constant right hand side we will make a series of checks. - if (is_const(right)) + // 4. For a constant rhs side we will make a series of checks. + if (IS_CONST(right)) { - // 5a. Make sure the value does not exceed the bitsize of + // 4a. 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) + Type *left_type_no_fail = type_no_fail(left->type)->canonical; + assert(type_kind_is_any_integer(left_type_no_fail->type_kind)); + if (int_ucomp(right->const_expr.ixx, left_type_no_fail->builtin.bitsize, BINARYOP_GT)) { - 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; - } + SEMA_ERROR(right, "The shift exceeds bitsize of %s.", type_quoted_error_string(type_no_fail(left->type))); + return false; } - // 5b. Make sure that the right hand side is positive. - if (bigint_cmp_zero(&right->const_expr.i) == CMP_LT) + + // 4b. Make sure that the RHS is positive. + if (int_is_neg(right->const_expr.ixx)) { SEMA_ERROR(right, "A shift must be a positive number."); return false; } - // 6. Fold constant expressions. - if (is_const(left)) + // 5. Fold constant expressions. + if (IS_CONST(left)) { - // 6a. For >> this is always an arithmetic shift. + expr_replace(expr, left); 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; - } - // 6b. The << case needs to behave differently for bigints and fixed bit integers. - expr_replace(expr, left); - if (left->const_expr.int_type == TYPE_IXX) - { - bigint_shl(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); + expr->const_expr.ixx = int_shr64(left->const_expr.ixx, right->const_expr.ixx.i.low); } else { - int bit_count = left->type->canonical->builtin.bitsize; - bool is_signed = !type_kind_is_unsigned(left->const_expr.int_type); - bigint_shl_trunc(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i, bit_count, is_signed); + expr->const_expr.ixx = int_shl64(left->const_expr.ixx, right->const_expr.ixx.i.low); } return true; } } - // 7. In the case of 2 >> x, we will want to cast to the target type if available, otherwise we use a ct->runtime promotion. - if (type_is_ct(left->type)) - { - if (to) - { - // 7a. The target type exists, convert to this type after arithmetic promotion. - if (!cast_implicit(left, numeric_arithmetic_promotion(to))) return false; - left->original_type = to; - } - else - { - // 7b. Otherwise, use runtime promotion. - if (!cast_implicitly_to_runtime(left)) return false; - } - } - - expr_copy_types(expr, left); - + // 6. Set the type + expr->type = type_get_opt_fail(left->type, IS_FAILABLE(right)); return true; } @@ -4495,12 +4338,11 @@ static bool sema_expr_analyse_shift_assign(Context *context, Expr *expr, Expr *l { // 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_subexpr(context, left, right)) return false; - expr->pure = false; - expr->failable = left->failable | right->failable; + bool failable = IS_FAILABLE(left) || IS_FAILABLE(right); - // 2. Ensure the left hand side is assignable + // 2. Ensure the lhs side is assignable if (!expr_is_ltype(left)) { SEMA_ERROR(left, "Expression is not assignable."); @@ -4511,60 +4353,49 @@ static bool sema_expr_analyse_shift_assign(Context *context, Expr *expr, Expr *l if (!both_any_integer_or_integer_vector(left, right)) return sema_type_error_on_binop(expr); // 4. For a constant right hand side we will make a series of checks. - if (is_const(right)) + if (IS_CONST(right)) { // 4a. 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) + if (int_ucomp(right->const_expr.ixx, left->type->canonical->builtin.bitsize, BINARYOP_GT)) { SEMA_ERROR(right, "The shift exceeds bitsize of '%s'.", type_to_error_string(left->type)); return false; } // 4b. Make sure that the right hand side is positive. - if (bigint_cmp_zero(&right->const_expr.i) == CMP_LT) + if (int_is_neg(right->const_expr.ixx)) { SEMA_ERROR(right, "A shift must be a positive number."); return false; } } - // 5. Set the type using the left hand side. - expr_copy_types(expr, left); + // 5. Set the type using the lhs side. + expr->type = type_get_opt_fail(left->type, failable); return true; } -static bool sema_expr_analyse_and(Context *context, Expr *expr, Expr *left, Expr *right) +static bool sema_expr_analyse_and_or(Context *context, Expr *expr, Expr *left, Expr *right) { - if (!sema_analyse_expr(context, type_bool, left) || !sema_analyse_expr(context, type_bool, right)) return false; + if (!sema_expr_analyse_binary_subexpr(context, left, right)) return false; if (!cast_implicit(left, type_bool) || !cast_implicit(right, type_bool)) return false; - expr_set_type(expr, type_bool); - if (both_const(left, right)) + if (expr_both_const(left, right)) { expr_replace(expr, left); - expr->const_expr.b &= right->const_expr.b; + if (expr->binary_expr.operator == BINARYOP_AND) + { + expr->const_expr.b &= right->const_expr.b; + } + else + { + expr->const_expr.b |= right->const_expr.b; + } } - expr_unify_binary_properties(expr, left, right); - return true; -} - -static bool sema_expr_analyse_or(Context *context, Expr *expr, Expr *left, Expr *right) -{ - 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)) - { - expr_replace(expr, left); - expr->const_expr.b |= right->const_expr.b; - } - expr_unify_binary_properties(expr, left, right); - expr_set_type(expr, type_bool); + expr->type = type_bool; return true; } @@ -4581,14 +4412,14 @@ static void cast_to_max_bit_size(Context *context, Expr *left, Expr *right, Type Type *to = left->type->type_kind < TYPE_U8 ? type_int_signed_by_bitsize(bit_size_right) : type_int_unsigned_by_bitsize(bit_size_right); - bool success = cast_implicit(left, to); + bool success = cast_implicit_ignore_failable(left, to); assert(success); return; } Type *to = right->type->type_kind < TYPE_U8 ? type_int_signed_by_bitsize(bit_size_left) : type_int_unsigned_by_bitsize(bit_size_left); - bool success = cast_implicit(right, to); + bool success = cast_implicit_ignore_failable(right, to); assert(success); } @@ -4599,10 +4430,11 @@ static void cast_to_max_bit_size(Context *context, Expr *left, Expr *right, Type static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Expr *right) { // 1. Analyse left and right side without any conversions. - if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false; + if (!sema_expr_analyse_binary_subexpr(context, left, right)) return false; bool is_equality_type_op = expr->binary_expr.operator == BINARYOP_NE || expr->binary_expr.operator == BINARYOP_EQ; + // Flatten enum/distinct/failable Type *left_type = type_flatten(left->type); Type *right_type = type_flatten(right->type); @@ -4628,19 +4460,19 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp if (type_size(left_vec) != type_size(right_vec)) goto DONE; if (type_is_integer(left_vec) && type_is_integer(right_vec)) goto DONE; } - SEMA_ERROR(expr, "Vector types '%s' and '%s' cannot be compared.", - type_to_error_string(left->type), type_to_error_string(right->type)); + SEMA_ERROR(expr, "Vector types %s and %s cannot be compared.", + type_quoted_error_string(left->type), type_quoted_error_string(right->type)); return false; } // 3. In the normal case, treat this as a binary op, finding the max type. - Type *max = type_find_max_type(left->type->canonical, right->type->canonical); + Type *max = type_find_max_type(type_no_fail(left->type)->canonical, type_no_fail(right->type)->canonical); // 4. If no common type, then that's an error: if (!max) { - SEMA_ERROR(expr, "'%s' and '%s' are different types and cannot be compared.", - type_to_error_string(left->type), type_to_error_string(right->type)); + SEMA_ERROR(expr, "%s and %s are different types and cannot be compared.", + type_quoted_error_string(left->type), type_quoted_error_string(right->type)); return false; } @@ -4671,12 +4503,12 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp } // 6. Do the implicit cast. - if (!cast_implicit(left, max)) goto ERR; - if (!cast_implicit(right, max)) goto ERR; + bool success = cast_implicit_ignore_failable(left, max) && cast_implicit_ignore_failable(right, max); + assert(success); DONE: // 7. Do constant folding. - if (both_const(left, right)) + if (expr_both_const(left, right)) { expr->const_expr.b = expr_const_compare(&left->const_expr, &right->const_expr, expr->binary_expr.operator); expr->const_expr.const_kind = CONST_BOOL; @@ -4684,33 +4516,30 @@ DONE: } // 8. Set the type to bool - expr_unify_binary_properties(expr, left, right); - // 8a. Except for vector, set to signed type with the correct size. - if (left_type->type_kind == TYPE_VECTOR) - { - expr_set_type(expr, type_get_vector_bool(left_type)); - return true; - } - expr_set_type(expr, type_bool); + Type *return_type = left_type->type_kind == TYPE_VECTOR ? type_get_vector_bool(left_type) : type_bool; + expr->type = type_get_opt_fail(return_type, IS_FAILABLE(left) || IS_FAILABLE(right)); + 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; } /** * Analyse *a * @return true if analysis succeeds. */ -static bool sema_expr_analyse_deref(Context *context, Expr *expr, Expr *inner) +static bool sema_expr_analyse_deref(Context *context, Expr *expr) { - Type *canonical = inner->type->canonical; - // 1. Check that we have a pointer, or dereference is not allowed. + // 1. Check the inner expression + Expr *inner = expr->unary_expr.expr; + if (!sema_analyse_expr(context, inner)) return false; + + Type *inner_type_nofail = type_no_fail(inner->type); + Type *canonical = inner_type_nofail->canonical; + + // 2. Check that we have a pointer, or dereference is not allowed. if (canonical->type_kind != TYPE_POINTER) { - SEMA_ERROR(inner, "Cannot dereference a value of type '%s', it must be a pointer.", type_to_error_string(inner->type)); + SEMA_ERROR(inner, "Cannot dereference a value of type %s, it must be a pointer.", type_quoted_error_string(inner_type_nofail)); return false; } if (canonical->pointer == type_void) @@ -4718,57 +4547,53 @@ static bool sema_expr_analyse_deref(Context *context, Expr *expr, Expr *inner) SEMA_ERROR(inner, "A 'void*' cannot be dereferenced, you need to first cast it to a concrete type."); return false; } - // 2. This could be a constant, in which case it is a null which is an error. + + // 3. This could be a constant, in which case it is a null which is an error. if (inner->expr_kind == EXPR_CONST) { SEMA_ERROR(inner, "Dereferencing null is not allowed, did you do it by mistake?"); return false; } - // 3. Now the type might not be a pointer because of a typedef, - // otherwise we need to use the canonical representation. - Type *deref_type = inner->type->type_kind != TYPE_POINTER ? inner->type : canonical; - // 4. And... set the type. - expr_set_type(expr, deref_type->pointer); - expr_copy_properties(expr, inner); + // 4. Now the type might not be a pointer because of a typedef, + // otherwise we need to use the canonical representation. + Type *deref_type = inner_type_nofail->type_kind == TYPE_POINTER ? inner_type_nofail : canonical; + + // 5. And... set the type. + expr->type = type_get_opt_fail(deref_type->pointer, IS_FAILABLE(inner)); return true; } -static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl, bool *is_constant) +static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl) { if (decl->decl_kind != DECL_VAR) return false; - *is_constant = false; + bool is_void = type_flatten(decl->type) == type_void; switch (decl->var.kind) { case VARDECL_GLOBAL: - if (decl->type == type_void) + if (is_void) { - SEMA_ERROR(expr, "You cannot take the address of a global of type '%s'.", - decl->var.failable ? "void!" : "void"); + SEMA_ERROR(expr, "You cannot take the address of a global of type %s.", type_quoted_error_string(decl->type)); return false; } - *is_constant = true; return true; case VARDECL_LOCAL: - if (decl->type == type_void) + if (is_void) { - SEMA_ERROR(expr, "You cannot take the address of a variable with type '%s'.", - decl->var.failable ? "void!" : "void"); + SEMA_ERROR(expr, "You cannot take the address of a variable with type %s.", type_quoted_error_string(decl->type)); return false; } return true; case VARDECL_PARAM: case VARDECL_PARAM_REF: - if (decl->type == type_void) + if (is_void) { - SEMA_ERROR(expr, "You cannot take the address of a parameter with type '%s'.", - decl->var.failable ? "void!" : "void"); + SEMA_ERROR(expr, "You cannot take the address of a parameter with type %s.", type_quoted_error_string(decl->type)); return false; } return true; case VARDECL_CONST: - *is_constant = true; if (!decl->var.type_info) { SEMA_ERROR(expr, "The constant is not typed, either type it or use && to take the reference to a temporary."); @@ -4796,25 +4621,28 @@ static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl, bool *is_consta } -static inline bool sema_take_addr_of_ident(Expr *inner, bool *is_constant) +static inline bool sema_take_addr_of_ident(Expr *inner) { - Decl *decl = inner->identifier_expr.decl; - decl = decl_raw(decl); + Decl *decl = decl_raw(inner->identifier_expr.decl); switch (decl->decl_kind) { - case DECL_ENUM_CONSTANT: case DECL_FUNC: - *is_constant = true; return true; case DECL_VAR: - return sema_take_addr_of_var(inner, decl, is_constant); - default: - SEMA_ERROR(inner, "It is not possible to take the address of a '%s'.", type_to_error_string(inner->type)); + return sema_take_addr_of_var(inner, decl); + case DECL_MACRO: + SEMA_ERROR(inner, "It is not possible to take the address of a macro."); return false; + case DECL_GENERIC: + SEMA_ERROR(inner, "It is not possible to take the address of a generic function."); + return false; + default: + UNREACHABLE } + UNREACHABLE } -static bool sema_take_addr_of(Expr *inner, bool *is_constant) +static bool sema_take_addr_of(Expr *inner) { switch (inner->expr_kind) { @@ -4826,25 +4654,19 @@ static bool sema_take_addr_of(Expr *inner, bool *is_constant) return false; case EXPR_IDENTIFIER: case EXPR_CONST_IDENTIFIER: - return sema_take_addr_of_ident(inner, is_constant); + return sema_take_addr_of_ident(inner); case EXPR_UNARY: if (inner->unary_expr.operator == UNARYOP_DEREF) return true; break; case EXPR_ACCESS: - return sema_take_addr_of(inner->access_expr.parent, is_constant); + return sema_take_addr_of(inner->access_expr.parent); case EXPR_GROUP: - return sema_take_addr_of(inner->group_expr, is_constant); + return sema_take_addr_of(inner->group_expr); case EXPR_SUBSCRIPT: - { - bool index_was_const = false; - if (!sema_take_addr_of(inner->subscript_expr.expr, &index_was_const)) return false; - return true; - } - case EXPR_SLICE: - { - bool dummy; - return sema_take_addr_of(inner->slice_expr.expr, &dummy); - } + return sema_take_addr_of(inner->subscript_expr.expr); + case EXPR_TYPEINFO: + SEMA_ERROR(inner, "It is not possible to take the address of a type."); + return false; default: break; } @@ -4856,9 +4678,10 @@ static bool sema_take_addr_of(Expr *inner, bool *is_constant) * Analyse &a * @return true if analysis succeeds. */ -static bool sema_expr_analyse_addr(Context *context, Type *to, Expr *expr, Expr *inner) +static bool sema_expr_analyse_addr(Context *context, Expr *expr) { // 1. Evaluate the expression + Expr *inner = expr->unary_expr.expr; REDO: switch (inner->expr_kind) { @@ -4869,49 +4692,56 @@ static bool sema_expr_analyse_addr(Context *context, Type *to, Expr *expr, Expr expr_replace(inner, inner->group_expr); goto REDO; default: - if (!sema_analyse_expr_value(context, NULL, inner)) return expr_poison(expr); + { + if (!sema_analyse_expr_lvalue(context, inner)) return expr_poison(expr); + } } // 2. Take the address. - bool is_constant = false; - if (!sema_take_addr_of(inner, &is_constant)) return expr_poison(expr); + if (!sema_take_addr_of(inner)) return expr_poison(expr); // 3. Get the pointer of the underlying type. - expr_set_type(expr, type_get_ptr(inner->type)); - expr->pure = inner->pure; - expr->failable = inner->failable; + expr->type = type_get_ptr_recurse(inner->type); return true; } -static bool sema_expr_analyse_neg(Context *context, Type *to, Expr *expr, Expr *inner) +/** + * Test -a + */ +static bool sema_expr_analyse_neg(Context *context, Expr *expr) { - if (!type_may_negate(inner->type)) + // 1. Check the inner expression + Expr *inner = expr->unary_expr.expr; + if (!sema_analyse_expr(context, inner)) return false; + + // 2. Check if it's possible to negate this (i.e. is it an int, float or vector) + Type *no_fail = type_no_fail(inner->type); + if (!type_may_negate(no_fail)) { - SEMA_ERROR(expr, "Cannot negate %s.", type_to_error_string(inner->type)); + SEMA_ERROR(expr, "Cannot negate an expression of type %s.", type_quoted_error_string(no_fail)); return false; } - expr_copy_properties(expr, inner); + // 3. Promote the type + Type *result_type = numeric_arithmetic_promotion(no_fail); + if (!cast_implicit_ignore_failable(inner, result_type)) return false; + // 4. If it's non-const, we're done. if (inner->expr_kind != EXPR_CONST) { - expr_copy_types(expr, inner); + expr->type = inner->type; return true; } - expr_replace(expr, inner); + // 5. Otherwise constant fold. + expr_replace(expr, inner); switch (expr->const_expr.const_kind) { case CONST_INTEGER: - bigint_negate(&expr->const_expr.i, &inner->const_expr.i); - if (expr_const_int_overflowed(&expr->const_expr)) - { - SEMA_ERROR(expr, "%s does not fit in '%s'.", expr_const_to_error_string(&expr->const_expr), type_to_error_string(expr->type)); - return false; - } + expr->const_expr.ixx = int_neg(inner->const_expr.ixx); return true; case CONST_FLOAT: - expr->const_expr.f = -expr->const_expr.f; + expr->const_expr.fxx = float_neg(expr->const_expr.fxx); break; default: UNREACHABLE @@ -4924,72 +4754,75 @@ static bool sema_expr_analyse_neg(Context *context, Type *to, Expr *expr, Expr * * * @return */ -static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Expr *inner) +static bool sema_expr_analyse_bit_not(Context *context, Expr *expr) { - expr_copy_properties(expr, inner); + // 1. Analyse the inner expression. + Expr *inner = expr->unary_expr.expr; + if (!sema_analyse_expr(context, inner)) return false; - Type *canonical = inner->type->canonical; - if (canonical->type_kind == TYPE_IXX) - { - SEMA_ERROR(expr, "Cannot bit negate an untyped integer literal, please first cast it to a concrete type.", type_to_error_string(inner->type)); - return false; - } - if (!type_is_any_integer(canonical) && canonical != type_bool) + // 2. Check that it's a vector, bool + Type *canonical = type_no_fail(inner->type)->canonical; + if (!type_is_integer_or_bool_kind(type_flatten_distinct(canonical))) { Type *vector_type = type_vector_type(canonical); - if (type_is_any_integer(vector_type) || vector_type == type_bool) goto VALID_VEC; + if (vector_type && (type_is_integer(vector_type) || vector_type == type_bool)) goto VALID_VEC; SEMA_ERROR(expr, "Cannot bit negate '%s'.", type_to_error_string(inner->type)); return false; } VALID_VEC: - // The simple case, non-const. + // 3. The simple case, non-const. if (inner->expr_kind != EXPR_CONST) { - expr_copy_types(expr, inner); + expr->type = inner->type; return true; } + // 4. Otherwise handle const bool expr_replace(expr, inner); if (expr->const_expr.const_kind == CONST_BOOL) { expr->const_expr.b = !expr->const_expr.b; return true; } - switch (expr->const_expr.int_type) - { - case ALL_SIGNED_INTS: - bigint_not(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize, true); - break; - case ALL_UNSIGNED_INTS: - bigint_not(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize, false); - break; - case TYPE_IXX: - default: - UNREACHABLE - } + + // 5. Perform ~ constant folded + // TODO arithmetic promotion? + expr->const_expr.ixx = int_not(inner->const_expr.ixx); return true; } -static bool sema_expr_analyse_not(Expr *expr, Expr *inner) +/** + * Evaluate !a + */ +static bool sema_expr_analyse_not(Context *context, Expr *expr) { - if (type_is_vector(inner->type)) + // 1. Evaluate inner + Expr *inner = expr->unary_expr.expr; + if (!sema_analyse_expr(context, inner)) return false; + + // 2. Check whether the type is a vector + Type *type = type_no_fail(inner->type); + if (type_is_vector(type)) { - expr_copy_properties(expr, inner); - expr_set_type(expr, type_get_vector_bool(inner->type)); + // 3. This always works, so we're done. + expr->type = type_get_opt_fail(type_get_vector_bool(type), IS_FAILABLE(inner)); return true; } - if (!cast_may_implicit(inner->type, type_bool)) + + // 4. Let's see if it's possible to cast it implicitly + if (!cast_may_implicit(type, type_bool)) { SEMA_ERROR(expr, "The use of '!' on %s is not allowed as it can't be converted to a boolean value.", type_quoted_error_string(inner->type)); return false; } - if (!cast_implicit(inner, type_bool)) return false; - expr_copy_properties(expr, inner); - expr_set_type(expr, type_bool); + + expr->type = type_get_opt_fail(type_bool, IS_FAILABLE(inner)); if (inner->expr_kind == EXPR_CONST) { + bool success = cast_implicit(inner, expr->type); + assert(success); assert(inner->const_expr.const_kind == CONST_BOOL); expr->const_expr.const_kind = CONST_BOOL; expr->expr_kind = EXPR_CONST; @@ -5020,13 +4853,15 @@ static inline bool sema_expr_analyse_ct_incdec(Context *context, Expr *expr, Exp Expr *end_value = expr_copy(start_value); // Make the change. - BigInt change; - bigint_init_signed(&change, expr->unary_expr.operator == UNARYOP_DEC ? -1 : 1); - bigint_add(&end_value->const_expr.i, &start_value->const_expr.i, &change); - if (!expr_const_int_valid(end_value, start_value->type)) return false; - + if (expr->unary_expr.operator == UNARYOP_DEC) + { + end_value->const_expr.ixx = int_sub64(start_value->const_expr.ixx, 1); + } + else + { + end_value->const_expr.ixx = int_add64(start_value->const_expr.ixx, 1); + } var->var.init_expr = end_value; - if (expr->expr_kind == EXPR_POST_UNARY) { expr_replace(expr, start_value); @@ -5042,44 +4877,50 @@ static inline bool sema_expr_analyse_ct_incdec(Context *context, Expr *expr, Exp * Analyse foo++ foo-- --foo ++foo * @return false if analysis fails. */ -static inline bool sema_expr_analyse_incdec(Context *context, Expr *expr, Expr *inner) -{; - expr->pure = false; - expr->failable = inner->failable; +static inline bool sema_expr_analyse_incdec(Context *context, Expr *expr) +{ + // 1. Analyse the lvalue to update + Expr *inner = expr->unary_expr.expr; + if (!sema_analyse_expr_lvalue(context, inner)) return false; + // 2. Assert it's an l-value if (!expr_is_ltype(inner)) { - SEMA_ERROR(inner, "Expression cannot be assigned to."); + SEMA_ERROR(inner, "An assignable expression, like a variable, was expected here."); return false; } + + // 3. This might be a $foo, if to handle it. if (inner->expr_kind == EXPR_CT_IDENT) { return sema_expr_analyse_ct_incdec(context, expr, inner); } + // 4. Flatten typedef, enum, distinct, failable Type *type = type_flatten(inner->type); + // 5. We can only inc/dec numbers or pointers. if (!type_underlying_is_numeric(type) && type->type_kind != TYPE_POINTER) { - SEMA_ERROR(inner, "Expression must be a number or a pointer."); + SEMA_ERROR(inner, "The expression must be a number or a pointer."); return false; } - expr_copy_types(expr, inner); + + // 6. Done, the result is same as the inner type. + expr->type = inner->type; return true; } -static inline bool sema_expr_analyse_taddr(Context *context, Type *to, Expr *expr, Expr *inner) +/** + * Take an address of a temporary &&x. + */ +static inline bool sema_expr_analyse_taddr(Context *context, Expr *expr) { - Type *inferred_type = NULL; - if (to->type_kind == TYPE_POINTER) - { - inferred_type = to->pointer; - } + Expr *inner = expr->unary_expr.expr; + if (!sema_analyse_expr(context, inner)) return false; - if (!sema_analyse_expr(context, inferred_type, inner)) return false; - - expr_copy_properties(expr, inner); - expr_set_type(expr, type_get_ptr(inner->type)); + // 2. The type is the resulting type of the expression. + expr->type = type_get_ptr_recurse(inner->type); return true; } @@ -5099,7 +4940,7 @@ static bool unclear_op_precedence(Expr *left_side, Expr * main_expr, Expr *right return false; } -static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_binary(Context *context, Expr *expr) { assert(expr->resolve_status == RESOLVE_RUNNING); Expr *left = expr->binary_expr.left; @@ -5110,23 +4951,21 @@ static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *ex SEMA_ERROR(expr, "You need to add explicit parentheses to clarify precedence."); return expr_poison(expr); } - // Don't push down bool conversions for example. - if (to && !type_underlying_is_numeric(to)) to = NULL; switch (expr->binary_expr.operator) { case BINARYOP_ASSIGN: return sema_expr_analyse_assign(context, expr, left, right); case BINARYOP_MULT: - return sema_expr_analyse_mult(context, to, expr, left, right); + return sema_expr_analyse_mult(context, expr, left, right); case BINARYOP_ADD: - return sema_expr_analyse_add(context, to, expr, left, right); + return sema_expr_analyse_add(context, expr, left, right); case BINARYOP_ADD_ASSIGN: case BINARYOP_SUB_ASSIGN: return sema_expr_analyse_add_sub_assign(context, expr, left, right); case BINARYOP_SUB: - return sema_expr_analyse_sub(context, to, expr, left, right); + return sema_expr_analyse_sub(context, expr, left, right); case BINARYOP_DIV: - return sema_expr_analyse_div(context, to, expr, left, right); + return sema_expr_analyse_div(context, expr, left, right); case BINARYOP_MULT_ASSIGN: case BINARYOP_DIV_ASSIGN: return sema_expr_analyse_common_assign(context, expr, left, right, false); @@ -5136,15 +4975,14 @@ static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *ex 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); + return sema_expr_analyse_mod(context, expr, left, right); case BINARYOP_AND: - return sema_expr_analyse_and(context, expr, left, right); case BINARYOP_OR: - return sema_expr_analyse_or(context, expr, left, right); + return sema_expr_analyse_and_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); + return sema_expr_analyse_bit(context, expr, left, right); case BINARYOP_NE: case BINARYOP_EQ: case BINARYOP_GT: @@ -5154,7 +4992,7 @@ static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *ex return sema_expr_analyse_comp(context, expr, left, right); case BINARYOP_SHR: case BINARYOP_SHL: - return sema_expr_analyse_shift(context, to, expr, left, right); + return sema_expr_analyse_shift(context, expr, left, right); case BINARYOP_SHR_ASSIGN: case BINARYOP_SHL_ASSIGN: return sema_expr_analyse_shift_assign(context, expr, left, right); @@ -5164,47 +5002,42 @@ static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *ex UNREACHABLE } -static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *expr) +/** + * Analyse: + * *x + * &x + * x++/++x/x--/--x + * ~x + * !x + * &&x + * -x + */ +static inline bool sema_expr_analyse_unary(Context *context, Expr *expr) { assert(expr->resolve_status == RESOLVE_RUNNING); - Expr *inner = expr->unary_expr.expr; switch (expr->unary_expr.operator) { case UNARYOP_DEREF: - if (!sema_analyse_expr(context, NULL, inner)) return false; - return sema_expr_analyse_deref(context, expr, inner); + return sema_expr_analyse_deref(context, expr); case UNARYOP_ADDR: - return sema_expr_analyse_addr(context, to, expr, inner); + return sema_expr_analyse_addr(context, expr); case UNARYOP_NEG: - if (!sema_analyse_expr(context, to, inner)) return false; - return sema_expr_analyse_neg(context, to, expr, inner); + return sema_expr_analyse_neg(context, expr); case UNARYOP_BITNEG: - if (!sema_analyse_expr(context, to, inner)) return false; - return sema_expr_analyse_bit_not(context, to, expr, inner); + return sema_expr_analyse_bit_not(context, expr); case UNARYOP_NOT: - if (!sema_analyse_expr(context, type_bool, inner)) return false; - return sema_expr_analyse_not(expr, inner); + return sema_expr_analyse_not(context, expr); case UNARYOP_DEC: case UNARYOP_INC: - if (!sema_analyse_expr_value(context, NULL, inner)) return false; - return sema_expr_analyse_incdec(context, expr, inner); + return sema_expr_analyse_incdec(context, expr); case UNARYOP_TADDR: - return sema_expr_analyse_taddr(context, to, expr, inner); + return sema_expr_analyse_taddr(context, expr); case UNARYOP_ERROR: return false; } UNREACHABLE } -static inline bool sema_expr_analyse_post_unary(Context *context, Type *to, Expr *expr) -{ - assert(expr->resolve_status == RESOLVE_RUNNING); - Expr *inner = expr->post_expr.expr; - - if (!sema_analyse_expr_value(context, NULL, inner)) return false; - - return sema_expr_analyse_incdec(context, expr, inner); -} @@ -5234,19 +5067,18 @@ static inline bool sema_expr_analyse_try_assign(Context *context, Expr *expr, bo create_variable = false; if (expr->try_assign_expr.is_try) { - if (!sema_analyse_expr_value(context, NULL, lhs)) return false; + if (!sema_analyse_expr_lvalue(context, lhs)) return false; } else { - if (!sema_analyse_expr_of_required_type(context, type_anyerr, lhs, true)) return false; + if (!sema_analyse_assigned_expr(context, type_anyerr, lhs, true)) return false; } if (!expr_is_ltype(lhs)) { SEMA_ERROR(lhs, "This expression is not assignable, did you make a mistake?"); return false; } - ExprFailableStatus failable_status = expr_is_failable(lhs); - if (failable_status == FAILABLE_YES) + if (IS_FAILABLE(lhs)) { SEMA_ERROR(lhs, "A 'try' assignment is not possible with failable on the left hand side, did you intend 'try (variable = expr)'?"); return false; @@ -5254,16 +5086,17 @@ static inline bool sema_expr_analyse_try_assign(Context *context, Expr *expr, bo lhs_type = lhs->type; } + lhs_type = type_no_fail(lhs_type); if (expr->try_assign_expr.is_try) { - if (!sema_analyse_expr_of_required_type(context, lhs_type, init, true)) return false; + if (!sema_analyse_assigned_expr(context, lhs_type, init, true)) return false; } else { - if (!sema_analyse_expr_of_required_type(context, NULL, init, true)) return false; + if (!sema_analyse_assigned_expr(context, NULL, init, true)) return false; } - if (!init->failable) + if (!IS_FAILABLE(init)) { SEMA_ERROR(init, "Expected a failable expression to '%s'.", expr->try_assign_expr.is_try ? "try" : "catch"); return false; @@ -5276,88 +5109,94 @@ static inline bool sema_expr_analyse_try_assign(Context *context, Expr *expr, bo TODO // try_expr->try_expr.implicit_decl = decl; if (!sema_add_local(context, decl)) return false; - if (!sema_analyse_expr_value(context, NULL, lhs)) return false; + if (!sema_analyse_expr_lvalue(context, lhs)) return false; } - - expr->pure = init->pure & lhs->pure; - expr_set_type(expr, type_bool); + expr->type = type_bool; return true; } static inline bool sema_expr_analyse_try(Context *context, Expr *expr) { Expr *inner = expr->try_expr.expr; - if (!sema_analyse_expr(context, NULL, inner)) return false; - expr->pure = inner->pure; - - if (!inner->failable) + if (!sema_analyse_expr(context, inner)) return false; + if (!IS_FAILABLE(inner)) { SEMA_ERROR(expr->try_expr.expr, "Expected a failable expression to '%s'.", expr->expr_kind == EXPR_TRY ? "try" : "catch"); return false; } - expr_set_type(expr, expr->try_expr.is_try ? type_bool : type_anyerr); + expr->type = expr->try_expr.is_try ? type_bool : type_anyerr; return true; } -static inline bool sema_expr_analyse_else(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_else(Context *context, Expr *expr) { Expr *inner = expr->else_expr.expr; - bool success = sema_analyse_expr(context, to, inner); - expr->pure = inner->pure; + bool success = sema_analyse_expr(context, inner); if (!success) return false; Type *type = inner->type; - if (!inner->failable) + if (type->type_kind != TYPE_FAILABLE) { SEMA_ERROR(inner, "No failable to 'else' in the expression, please remove the 'else'."); return false; } + type = type->failable; if (expr->else_expr.is_jump) { - expr->pure = false; if (!sema_analyse_statement(context, expr->else_expr.else_stmt)) return false; - expr_copy_types(expr, inner); + expr->type = inner->type; return true; } // First we analyse the "else" and try to implictly cast. - if (!sema_analyse_expr(context, type, expr->else_expr.else_expr)) return false; - expr->pure &= expr->else_expr.else_expr->pure; + Expr *else_expr = expr->else_expr.else_expr; + if (!sema_analyse_expr(context, else_expr)) return false; // Here we might need to insert casts. - Type *common = type_find_max_type(type, expr->else_expr.else_expr->type); - if (!cast_implicit(expr->else_expr.else_expr, common)) return false; + Type *else_type = else_expr->type; + if (else_type->type_kind == TYPE_FAILABLE) + { + SEMA_ERROR(else_expr, "The default value may not be a failable."); + return false; + } + Type *common = type_find_max_type(type, else_type); + if (!common) + { + SEMA_ERROR(else_expr, "Cannot find a common type for %s and %s.", type_quoted_error_string(type), + type_quoted_error_string(else_type)); + return false; + } + if (!cast_implicit(inner, type_get_failable(common))) return false; + if (!cast_implicit(else_expr, common)) return false; expr->type = common; - expr->original_type = type_find_max_type(inner->original_type, expr->else_expr.else_expr->original_type); - return cast_implicit(expr->else_expr.expr, common); + return true; } -static inline bool sema_expr_analyse_guard(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_guard(Context *context, Expr *expr) { Expr *inner = expr->guard_expr.inner; - bool success = sema_analyse_expr(context, to, inner); + if (!sema_analyse_expr(context, inner)) return false; expr->guard_expr.defer = context->active_scope.defer_last; - if (!success) return false; - expr_copy_types(expr, inner); - expr->pure = false; + if (inner->type == type_anyfail) + { + SEMA_ERROR(expr, "This expression will always throw, which isn't allowed."); + return false; + } + expr->type = type_no_fail(inner->type); - if (!inner->failable) + if (!IS_FAILABLE(inner)) { SEMA_ERROR(expr, "No failable to rethrow before '!!' in the expression, please remove '!!'."); return false; } - if (!context->failable_return) + + if (context->rtype->type_kind != TYPE_FAILABLE) { SEMA_ERROR(expr, "This expression implicitly returns with a failable result, but the function does not allow failable results. Did you mean to use 'else' instead?"); return false; } - return true; -} -static inline bool sema_expr_analyse_const(Type *to, Expr *expr) -{ - expr->pure = true; return true; } @@ -5365,31 +5204,28 @@ static inline bool sema_expr_analyse_const(Type *to, Expr *expr) -static inline bool sema_expr_analyse_type(Context *context, Expr *expr) + +static inline bool sema_expr_analyse_typeid(Context *context, Expr *expr) { - if (!sema_resolve_type_info(context, expr->typeid_expr)) - { - return expr_poison(expr); - } + if (!sema_resolve_type_info(context, expr->typeid_expr)) return expr_poison(expr); Type *type = expr->type_expr->type; expr->expr_kind = EXPR_CONST; expr->const_expr.const_kind = CONST_TYPEID; expr->const_expr.typeid = type->canonical; - expr_set_type(expr, type_typeid); + expr->type = type_typeid; return true; } -static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_expr_block(Context *context, Expr *expr) { bool success = true; - expr_set_type(expr, type_void); + expr->type = type_void; bool saved_expr_failable_return = context->expr_failable_return; Type *prev_expected_block_type = context->expected_block_type; Ast **saved_returns = context_push_returns(context); - context->expected_block_type = to; - - + Type *stored_block_type = context->expected_block_type; + context->expected_block_type = NULL; SCOPE_START_WITH_FLAGS(SCOPE_EXPR_BLOCK) PUSH_CONTINUE(NULL); @@ -5405,35 +5241,30 @@ static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr } } - if (!context->active_scope.jump_end && to) + if (!vec_size(context->returns)) { - SEMA_ERROR(expr, "Expected the block to return with a value of type %s.", type_to_error_string(to)); - success = false; + expr->type = type_void; + goto EXIT; } - if (!vec_size(context->returns)) goto EXIT; - Expr *first_return_expr = context->returns[0]->return_stmt.expr; - Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void; // Let's unify the return statements. - left_canonical = unify_returns(context, left_canonical); - if (!left_canonical) + Type *sum_returns = unify_returns(context); + if (!sum_returns) { success = false; goto EXIT; } - expr_set_type(expr, left_canonical); + expr->type = sum_returns; EXIT: POP_BREAKCONT(); POP_NEXT(); SCOPE_END; + context->expected_block_type = stored_block_type; context_pop_returns(context, saved_returns); - expr->failable = context->expr_failable_return; context->expr_failable_return = saved_expr_failable_return; - context->expected_block_type = prev_expected_block_type; - expr->pure = false; return success; } @@ -5442,6 +5273,12 @@ static inline bool sema_expr_analyse_compound_literal(Context *context, Expr *ex { if (!sema_resolve_type_info(context, expr->expr_compound_literal.type_info)) return false; Type *type = expr->expr_compound_literal.type_info->type; + if (TYPE_IS_FAILABLE(type)) + { + SEMA_ERROR(expr->expr_compound_literal.type_info, + "The type here should always be written as a plain type and not a failable, please remove the '!'."); + return false; + } if (!sema_expr_analyse_initializer_list(context, type, expr->expr_compound_literal.initializer)) return false; expr_replace(expr, expr->expr_compound_literal.initializer); return true; @@ -5450,51 +5287,44 @@ static inline bool sema_expr_analyse_compound_literal(Context *context, Expr *ex static inline bool sema_expr_analyse_typeof(Context *context, Expr *expr) { TODO - if (!sema_analyse_expr(context, NULL, expr->typeof_expr)) return false; - expr->pure = expr->typeof_expr->pure; + if (!sema_analyse_expr(context, expr->typeof_expr)) return false; Type *type = expr->typeof_expr->type->canonical; expr->expr_kind = EXPR_TYPEID; expr->typeid_expr = type_info_new_base(type, expr->typeof_expr->span); - expr_set_type(expr, type_typeid); + expr->type = type_typeid; return true; } -static inline bool sema_expr_analyse_failable(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_failable(Context *context, Expr *expr) { Expr *inner = expr->failable_expr; + if (!sema_analyse_expr(context, inner)) return false; - - if (!sema_analyse_expr(context, NULL, inner)) return false; - expr->pure = inner->pure; - - if (inner->failable) + if (IS_FAILABLE(inner)) { SEMA_ERROR(inner, "The inner expression is already a failable."); + return false; } - Type *type = inner->type->canonical; + if (inner->expr_kind == EXPR_FAILABLE) { SEMA_ERROR(inner, "It looks like you added one too many '!' after the error."); return false; } + + Type *type = inner->type->canonical; if (type->type_kind != TYPE_ERRTYPE && type->type_kind != TYPE_ANYERR) { - SEMA_ERROR(inner, "You cannot use the '!' operator on expressions of type '%s'", type_to_error_string(type)); + SEMA_ERROR(inner, "You cannot use the '!' operator on expressions of type %s", type_quoted_error_string(type)); return false; } - if (!to) - { - expr_set_type(expr, type_void); - return true; - } - expr->failable = true; - expr_set_type(expr, to); + expr->type = type_anyfail; return true; } -static inline bool sema_expr_analyse_placeholder(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_placeholder(Context *context, Expr *expr) { const char *string = TOKSTR(expr->placeholder_expr.identifier); if (string == kw_FILE) @@ -5514,18 +5344,18 @@ static inline bool sema_expr_analyse_placeholder(Context *context, Type *to, Exp } if (string == kw_LINEREAL) { - expr_rewrite_to_int_const(expr, type_compint, TOKLOC(expr->placeholder_expr.identifier)->line); + expr_rewrite_to_int_const(expr, type_isize, TOKLOC(expr->placeholder_expr.identifier)->line, true); return true; } if (string == kw_LINE) { if (context->macro_scope.depth) { - expr_rewrite_to_int_const(expr, type_compint, context->macro_scope.original_inline_line); + expr_rewrite_to_int_const(expr, type_isize, context->macro_scope.original_inline_line, true); } else { - expr_rewrite_to_int_const(expr, type_compint, TOKLOC(expr->placeholder_expr.identifier)->line); + expr_rewrite_to_int_const(expr, type_isize, TOKLOC(expr->placeholder_expr.identifier)->line, true); } return true; } @@ -5536,10 +5366,6 @@ static inline bool sema_expr_analyse_placeholder(Context *context, Type *to, Exp return false; } expr_replace(expr, value); - if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INTEGER && value->const_expr.i.digit_count > 1) - { - bigint_init_bigint(&expr->const_expr.i, &value->const_expr.i); - } return true; } @@ -5826,7 +5652,7 @@ static inline bool decl_is_local(Decl *decl) static bool sema_expr_analyse_type_var_path(Context *context, Expr *expr, ExprFlatElement **elements, Type **type_ref, Decl **decl_ref) { - if (!sema_analyse_expr_value(context, NULL, expr)) return false; + if (!sema_analyse_expr_lvalue(context, expr)) return false; Expr *current = expr; Decl *decl = NULL; Type *type = NULL; @@ -5864,7 +5690,7 @@ RETRY: return true; } -static inline bool sema_expr_analyse_ct_alignof(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_ct_alignof(Context *context, Expr *expr) { Expr *main_var = expr->ct_call_expr.main_var; Type *type = NULL; @@ -5916,12 +5742,12 @@ static inline bool sema_expr_analyse_ct_alignof(Context *context, Type *to, Expr align = type_min_alignment(member->offset, align); } - expr_rewrite_to_int_const(expr, type_compint, align); + expr_rewrite_to_int_const(expr, type_isize, align, true); return true; } -static inline bool sema_expr_analyse_ct_sizeof(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_ct_sizeof(Context *context, Expr *expr) { Expr *main_var = expr->ct_call_expr.main_var; Type *type = NULL; @@ -5970,11 +5796,11 @@ static inline bool sema_expr_analyse_ct_sizeof(Context *context, Type *to, Expr type = member->type; } - expr_rewrite_to_int_const(expr, type_compint, type_size(type)); + expr_rewrite_to_int_const(expr, type_isize, type_size(type), true); return true; } -static inline bool sema_expr_analyse_ct_nameof(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_ct_nameof(Context *context, Expr *expr) { Expr *main_var = expr->ct_call_expr.main_var; @@ -6191,7 +6017,7 @@ static inline bool sema_expr_analyse_ct_defined(Context *context, Expr *expr) break; } default: - if (!sema_analyse_expr_value(context, NULL, main_var)) return false; + if (!sema_analyse_expr_lvalue(context, main_var)) return false; if (main_var->expr_kind == EXPR_TYPEINFO) { type = expr->type_expr->type; @@ -6239,20 +6065,22 @@ static inline bool sema_expr_analyse_ct_defined(Context *context, Expr *expr) type = member->type; } - expr_set_type(expr, type_bool); + expr->type = type_bool; expr->expr_kind = EXPR_CONST; expr_const_set_bool(&expr->const_expr, true); return true; NOT_DEFINED: - expr_set_type(expr, type_bool); + { + expr->type = type_bool; + } expr->expr_kind = EXPR_CONST; expr_const_set_bool(&expr->const_expr, false); return true; } -static inline bool sema_expr_analyse_ct_offsetof(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_ct_offsetof(Context *context, Expr *expr) { Expr *main_var = expr->ct_call_expr.main_var; Type *type = NULL; @@ -6307,41 +6135,34 @@ static inline bool sema_expr_analyse_ct_offsetof(Context *context, Type *to, Exp offset += member->offset; } - expr_rewrite_to_int_const(expr, type_compint, offset); + expr_rewrite_to_int_const(expr, type_iptrdiff, offset, true); return true; } -static inline bool sema_expr_analyse_ct_call(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_ct_call(Context *context, Expr *expr) { switch (expr->ct_call_expr.token_type) { case TOKEN_CT_DEFINED: return sema_expr_analyse_ct_defined(context, expr); case TOKEN_CT_SIZEOF: - return sema_expr_analyse_ct_sizeof(context, to, expr); + return sema_expr_analyse_ct_sizeof(context, expr); case TOKEN_CT_ALIGNOF: - return sema_expr_analyse_ct_alignof(context, to, expr); + return sema_expr_analyse_ct_alignof(context, expr); case TOKEN_CT_OFFSETOF: - return sema_expr_analyse_ct_offsetof(context, to, expr); + return sema_expr_analyse_ct_offsetof(context, expr); case TOKEN_CT_QNAMEOF: case TOKEN_CT_NAMEOF: case TOKEN_CT_EXTNAMEOF: - return sema_expr_analyse_ct_nameof(context, to, expr); + return sema_expr_analyse_ct_nameof(context, expr); default: UNREACHABLE } } -static inline bool sema_expr_analyse_decl(Context *context, Type *to, Expr *expr) -{ - if (!sema_analyse_var_decl(context, expr->decl_expr)) return false; - expr_set_type(expr, expr->decl_expr->type); - expr->pure = !expr->decl_expr->var.init_expr || expr->decl_expr->var.init_expr->pure; - return true; -} -static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *expr) +static inline bool sema_analyse_expr_dispatch(Context *context, Expr *expr) { switch (expr->expr_kind) { @@ -6356,17 +6177,19 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_CATCH_UNWRAP: UNREACHABLE case EXPR_DECL: - return sema_expr_analyse_decl(context, to, expr); + if (!sema_analyse_var_decl(context, expr->decl_expr)) return false; + expr->type = expr->decl_expr->type; + return true; case EXPR_CT_CALL: - return sema_expr_analyse_ct_call(context, to, expr); + return sema_expr_analyse_ct_call(context, expr); case EXPR_HASH_IDENT: - return sema_expr_analyse_hash_identifier(context, to, expr); + return sema_expr_analyse_hash_identifier(context, expr); case EXPR_CT_IDENT: return sema_expr_analyse_ct_identifier(context, expr); case EXPR_FAILABLE: - return sema_expr_analyse_failable(context, to, expr); + return sema_expr_analyse_failable(context, expr); case EXPR_PLACEHOLDER: - return sema_expr_analyse_placeholder(context, to, expr); + return sema_expr_analyse_placeholder(context, expr); case EXPR_POISONED: return false; case EXPR_LEN: @@ -6377,7 +6200,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_SCOPED_EXPR: UNREACHABLE case EXPR_TYPEINFO: - return sema_expr_analyse_typeinfo(context, expr); + expr->type = type_typeinfo; + return sema_resolve_type_info(context, expr->type_expr); case EXPR_SLICE: return sema_expr_analyse_slice(context, expr); case EXPR_TRY: @@ -6387,82 +6211,105 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_TYPEOF: return sema_expr_analyse_typeof(context, expr); case EXPR_ELSE: - return sema_expr_analyse_else(context, to, expr); + return sema_expr_analyse_else(context, expr); case EXPR_COMPOUND_LITERAL: return sema_expr_analyse_compound_literal(context, expr); case EXPR_EXPR_BLOCK: - return sema_expr_analyse_expr_block(context, to, expr); + return sema_expr_analyse_expr_block(context, expr); case EXPR_GUARD: - return sema_expr_analyse_guard(context, to, expr); + return sema_expr_analyse_guard(context, expr); case EXPR_CONST: - return sema_expr_analyse_const(to, expr); + return true; case EXPR_BINARY: - if (!sema_expr_analyse_binary(context, to, expr)) return false; - if (expr->expr_kind == EXPR_BINARY) - { - expr->failable = expr->binary_expr.left->failable | expr->binary_expr.right->failable; - } - return true; + return sema_expr_analyse_binary(context, expr); case EXPR_TERNARY: - return sema_expr_analyse_ternary(context, to, expr); + return sema_expr_analyse_ternary(context, expr); case EXPR_UNARY: - if (!sema_expr_analyse_unary(context, to, expr)) return false; - if (expr->expr_kind == EXPR_UNARY) expr->failable = expr->unary_expr.expr->failable; - return true; case EXPR_POST_UNARY: - if (!sema_expr_analyse_post_unary(context, to, expr)) return false; - if (expr->expr_kind == EXPR_UNARY) expr->failable = expr->unary_expr.expr->failable; - return true; + return sema_expr_analyse_unary(context, expr); case EXPR_TYPEID: - return sema_expr_analyse_type(context, expr); + return sema_expr_analyse_typeid(context, expr); case EXPR_MACRO_EXPANSION: return sema_expr_analyse_macro_expansion(context, expr); case EXPR_CONST_IDENTIFIER: case EXPR_IDENTIFIER: - return sema_expr_analyse_identifier(context, to, expr); + return sema_expr_analyse_identifier(context, NULL, expr); case EXPR_CALL: - return sema_expr_analyse_call(context, to, expr); + return sema_expr_analyse_call(context, expr); case EXPR_SUBSCRIPT: return sema_expr_analyse_subscript(context, expr); case EXPR_GROUP: - return sema_expr_analyse_group(context, to, expr); + return sema_expr_analyse_group(context, expr); case EXPR_ACCESS: return sema_expr_analyse_access(context, expr); case EXPR_INITIALIZER_LIST: case EXPR_DESIGNATED_INITIALIZER_LIST: - return sema_expr_analyse_initializer_list(context, to, expr); + return sema_expr_analyse_initializer_list(context, NULL, expr); case EXPR_CAST: - return sema_expr_analyse_cast(context, to, expr); + return sema_expr_analyse_cast(context, expr); case EXPR_EXPRESSION_LIST: - return sema_expr_analyse_expr_list(context, to, expr); + return sema_expr_analyse_expr_list(context, expr); } UNREACHABLE } - -bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, bool may_be_failable) +bool sema_analyse_assigned_expr(Context *context, Type *to, Expr *expr, bool may_be_failable) { - if (!sema_analyse_expr(context, to, expr)) return false; - if (expr->failable && !may_be_failable) + if (!sema_analyse_inferred_expr(context, to, expr)) return false; + if (IS_FAILABLE(expr)) { - if (!to) to = expr->type; - SEMA_ERROR(expr, "'%s!' cannot be converted into '%s'.", type_to_error_string(expr->type), type_to_error_string(to)); + if (!may_be_failable) + { + if (!to) to = expr->type; + SEMA_ERROR(expr, "%s cannot be converted to %s.", type_quoted_error_string(type_no_fail(expr->type)), type_quoted_error_string(to)); + return false; + } + if (to && to->type_kind != TYPE_FAILABLE) + { + to = type_get_failable(to); + } + } + return to ? cast_implicit(expr, to) : true; +} + +bool sema_analyse_cond_expr(Context *context, Expr *expr) +{ + if (!sema_analyse_expr(context, expr)) return false; + if (IS_FAILABLE(expr)) + { + SEMA_ERROR(expr, "%s cannot be converted to %s.", type_quoted_error_string(expr->type), type_quoted_error_string(type_bool)); return false; } + return cast_implicit(expr, type_bool); +} + +bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr) +{ + Type *no_fail_type = type_no_fail(to); + if (!sema_analyse_inferred_expr(context, no_fail_type, expr)) return false; + if (IS_FAILABLE(expr)) + { + if (no_fail_type && to->type_kind != TYPE_FAILABLE) + { + if (!to) to = expr->type; + SEMA_ERROR(expr, "%s cannot be converted to %s.", type_quoted_error_string(expr->type), type_quoted_error_string(to)); + return false; + } + } return to ? cast_implicit(expr, to) : true; } -static inline bool sema_cast_ct_ident_rvalue(Context *context, Type *to, Expr *expr) +static inline bool sema_cast_ct_ident_rvalue(Context *context, Expr *expr) { Decl *decl = expr->ct_ident_expr.decl; Expr *copy = copy_expr(decl->var.init_expr); - if (!sema_analyse_expr(context, to, copy)) return false; + if (!sema_analyse_expr(context, copy)) return false; expr_replace(expr, copy); return true; } -static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr) +static inline bool sema_cast_rvalue(Context *context, Expr *expr) { if (!expr_ok(expr)) return false; switch (expr->expr_kind) @@ -6492,11 +6339,11 @@ static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr) SEMA_ERROR(expr, "Expected macro followed by (...).", expr->ct_macro_ident_expr.identifier); return expr_poison(expr); case EXPR_CT_IDENT: - if (!sema_cast_ct_ident_rvalue(context, to, expr)) return false; + if (!sema_cast_ct_ident_rvalue(context, expr)) return false; break; case EXPR_IDENTIFIER: case EXPR_CONST_IDENTIFIER: - if (!sema_cast_ident_rvalue(context, to, expr)) return false; + if (!sema_cast_ident_rvalue(context, expr)) return false; break; default: break; @@ -6504,13 +6351,13 @@ static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr) return true; } -bool sema_analyse_expr_value(Context *context, Type *to, Expr *expr) +bool sema_analyse_expr_lvalue(Context *context, Expr *expr) { switch (expr->resolve_status) { case RESOLVE_NOT_DONE: expr->resolve_status = RESOLVE_RUNNING; - if (!sema_analyse_expr_dispatch(context, to, expr)) return expr_poison(expr); + if (!sema_analyse_expr_dispatch(context, expr)) return expr_poison(expr); expr->resolve_status = RESOLVE_DONE; return true; case RESOLVE_RUNNING: @@ -6617,8 +6464,42 @@ ArrayIndex sema_get_initializer_const_array_size(Context *context, Expr *initial return size; } -bool sema_analyse_expr(Context *context, Type *to, Expr *expr) +bool sema_analyse_expr(Context *context, Expr *expr) { - return sema_analyse_expr_value(context, to, expr) && sema_cast_rvalue(context, to, expr); + return sema_analyse_expr_lvalue(context, expr) && sema_cast_rvalue(context, expr); +} + +bool sema_analyse_inferred_expr(Context *context, Type *infer_type, Expr *expr) +{ + switch (expr->resolve_status) + { + case RESOLVE_NOT_DONE: + break; + case RESOLVE_RUNNING: + SEMA_ERROR(expr, "Recursive resolution of list."); + return expr_poison(expr); + case RESOLVE_DONE: + return expr_ok(expr); + default: + UNREACHABLE + } + + expr->resolve_status = RESOLVE_RUNNING; + + switch (expr->expr_kind) + { + case EXPR_INITIALIZER_LIST: + case EXPR_DESIGNATED_INITIALIZER_LIST: + if (!sema_expr_analyse_initializer_list(context, infer_type, expr)) return expr_poison(expr); + break; + case EXPR_CONST_IDENTIFIER: + if (!sema_expr_analyse_identifier(context, infer_type, expr)) return expr_poison(expr); + break; + default: + if (!sema_analyse_expr_dispatch(context, expr)) return expr_poison(expr); + break; + } + expr->resolve_status = RESOLVE_DONE; + return sema_cast_rvalue(context, expr); } diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 500f23b71..59797cf47 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -54,5 +54,5 @@ void context_change_scope_with_flags(Context *context, ScopeFlags flags); void c_abi_func_create(FunctionSignature *signature); AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDomain domain); bool expr_is_ltype(Expr *expr); -bool sema_analyse_expr_value(Context *context, Type *to, Expr *expr); +bool sema_analyse_expr_lvalue(Context *context, Expr *expr); diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 968744f38..50a2f714d 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -475,14 +475,14 @@ bool sema_unwrap_var(Context *context, Decl *decl) Decl *alias = decl_copy(decl); alias->var.kind = VARDECL_UNWRAPPED; alias->var.alias = decl; - alias->var.failable = false; + alias->type = alias->type->failable; alias->resolve_status = RESOLVE_DONE; return sema_append_local(context, alias); } bool sema_rewrap_var(Context *context, Decl *decl) { - assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED && decl->var.alias->var.failable); + assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED && decl->var.alias->type->type_kind == TYPE_FAILABLE); return sema_append_local(context, decl->var.alias); } @@ -497,11 +497,11 @@ bool sema_erase_var(Context *context, Decl *decl) bool sema_erase_unwrapped(Context *context, Decl *decl) { - assert(decl->var.failable); + assert(IS_FAILABLE(decl)); Decl *rewrapped = decl_copy(decl); rewrapped->var.kind = VARDECL_REWRAPPED; rewrapped->var.alias = decl; - rewrapped->var.failable = true; + rewrapped->type = decl->type; rewrapped->resolve_status = RESOLVE_DONE; return sema_append_local(context, rewrapped); } diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index de8f0b86c..ed5c3f36b 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -27,7 +27,7 @@ static void sema_unwrappable_from_catch_in_else(Context *c, Expr *cond) if (expr->expr_kind != EXPR_IDENTIFIER) continue; Decl *decl = expr->identifier_expr.decl; if (decl->decl_kind != DECL_VAR) continue; - assert(decl->var.failable && "The variable should always be failable at this point."); + assert(decl->type->type_kind == TYPE_FAILABLE && "The variable should always be failable at this point."); // 5. Locals and globals may be unwrapped switch (decl->var.kind) @@ -54,10 +54,10 @@ static inline bool sema_analyse_block_return_stmt(Context *context, Ast *stateme context->active_scope.jump_end = true; if (statement->return_stmt.expr) { - if (!sema_analyse_expr_of_required_type(context, - context->expected_block_type, - statement->return_stmt.expr, true)) return false; - context->expr_failable_return |= statement->return_stmt.expr->failable; + if (!sema_analyse_expr(context, statement->return_stmt.expr)) + { + return false; + } } vec_add(context->returns, statement); return true; @@ -94,7 +94,7 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) // 2. First handle the plain return. if (return_expr == NULL) { - if (expected_rtype->canonical != type_void) + if (type_no_fail(expected_rtype)->canonical != type_void) { SEMA_ERROR(statement, "Expected to return a result of type %s.", type_to_error_string(expected_rtype)); return false; @@ -103,9 +103,9 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) } // 3. Evaluate the return value to be the expected return type. - if (!sema_analyse_expr_of_required_type(context, expected_rtype, return_expr, context->failable_return)) return false; + if (!sema_analyse_expr_of_required_type(context, expected_rtype, return_expr)) return false; - assert(statement->return_stmt.expr->type->canonical == expected_rtype->canonical); + assert(type_no_fail(statement->return_stmt.expr->type)->canonical == type_no_fail(expected_rtype)->canonical); return true; } @@ -125,7 +125,7 @@ static inline bool sema_analyse_try_unwrap(Context *context, Expr *expr) // Case A. Unwrapping a single variable. if (!failable) { - if (!sema_analyse_expr(context, NULL, ident)) return false; + if (!sema_analyse_expr(context, ident)) return false; if (ident->expr_kind != EXPR_IDENTIFIER) { SEMA_ERROR(ident, "Only single identifiers may be unwrapped using 'try var', maybe you wanted 'try (expr)' instead?"); @@ -137,7 +137,7 @@ static inline bool sema_analyse_try_unwrap(Context *context, Expr *expr) SEMA_ERROR(ident, "Expected this to be the name of a failable variable, but it isn't. Did you mistype?"); return false; } - if (!decl->var.failable) + if (!IS_FAILABLE(decl)) { if (decl->var.kind == VARDECL_UNWRAPPED) { @@ -148,7 +148,7 @@ static inline bool sema_analyse_try_unwrap(Context *context, Expr *expr) return false; } expr->try_unwrap_expr.decl = decl; - expr_set_type(expr, type_bool); + expr->type = type_bool; sema_unwrap_var(context, decl); expr->resolve_status = RESOLVE_DONE; return true; @@ -166,13 +166,21 @@ static inline bool sema_analyse_try_unwrap(Context *context, Expr *expr) } // 2. If we have a type for the variable, resolve it. - if (var_type && !sema_resolve_type_info(context, var_type)) return false; + if (var_type) + { + if (!sema_resolve_type_info(context, var_type)) return false; + if (IS_FAILABLE(var_type)) + { + SEMA_ERROR(var_type, "Only non-failable types may be used as types for 'try', please remove the '!'."); + return false; + } + } // 3. We interpret this as an assignment to an existing variable. if (!var_type && !implicit_declaration) { // 3a. Resolve the identifier. - if (!sema_analyse_expr_value(context, NULL, ident)) return false; + if (!sema_analyse_expr_lvalue(context, ident)) return false; // 3b. Make sure it's assignable if (!expr_is_ltype(ident)) @@ -182,7 +190,7 @@ static inline bool sema_analyse_try_unwrap(Context *context, Expr *expr) } // 3c. It can't be failable either. - if (ident->failable) + if (IS_FAILABLE(ident)) { if (ident->expr_kind == EXPR_IDENTIFIER) { @@ -196,7 +204,15 @@ static inline bool sema_analyse_try_unwrap(Context *context, Expr *expr) } // 3d. We can now analyse the expression using the variable type. - if (!sema_analyse_expr_of_required_type(context, ident->type, failable, true)) return false; + if (!sema_analyse_expr(context, failable)) return false; + + if (!IS_FAILABLE(failable)) + { + SEMA_ERROR(failable, "Expected a failable expression to 'try' here. If it isn't a failable, remove 'try'."); + return false; + } + + if (!cast_implicit_ignore_failable(failable, ident->type)) return false; expr->try_unwrap_expr.assign_existing = true; expr->try_unwrap_expr.lhs = ident; @@ -226,16 +242,24 @@ static inline bool sema_analyse_try_unwrap(Context *context, Expr *expr) return false; } - // 4a. Type may be assigned or inferred. - Type *type = var_type ? var_type->type : NULL; - // 4b. Evaluate the expression - if (!sema_analyse_expr_of_required_type(context, type, failable, true)) return false; + if (!sema_analyse_expr(context, failable)) return false; + + if (!IS_FAILABLE(failable)) + { + SEMA_ERROR(failable, "Expected a failable expression to 'try' here. If it isn't a failable, remove 'try'."); + return false; + } + + if (var_type) + { + if (!cast_implicit_ignore_failable(failable, var_type->type)) return false; + } // 4c. Create a type_info if needed. if (!var_type) { - var_type = type_info_new_base(failable->type, failable->span); + var_type = type_info_new_base(failable->type->failable, failable->span); } // 4d. A new declaration is created. @@ -247,14 +271,8 @@ static inline bool sema_analyse_try_unwrap(Context *context, Expr *expr) expr->try_unwrap_expr.decl = decl; } - if (!failable->failable) - { - SEMA_ERROR(failable, "Expected a failable expression to 'try' here. If it isn't a failable, remove 'try'."); - return false; - } - expr->try_unwrap_expr.failable = failable; - expr_set_type(expr, type_bool); + expr->type = type_bool; expr->resolve_status = RESOLVE_DONE; return true; } @@ -269,9 +287,9 @@ static inline bool sema_analyse_try_unwrap_chain(Context *context, Expr *expr) if (!sema_analyse_try_unwrap(context, chain)) return false; continue; } - if (!sema_analyse_expr_of_required_type(context, type_bool, chain, false)) return false; + if (!sema_analyse_cond_expr(context, chain)) return false; } - expr_set_type(expr, type_bool); + expr->type = type_bool; expr->resolve_status = RESOLVE_DONE; return true; } @@ -296,7 +314,7 @@ static inline bool sema_analyse_catch_unwrap(Context *context, Expr *expr) if (!type && !implicit_declaration) { - if (!sema_analyse_expr_value(context, NULL, ident)) return false; + if (!sema_analyse_expr_lvalue(context, ident)) return false; if (!expr_is_ltype(ident)) { @@ -361,14 +379,14 @@ RESOLVE_EXPRS:; VECEACH(exprs, i) { Expr *fail = exprs[i]; - if (!sema_analyse_expr(context, NULL, fail)) return false; - if (!fail->failable) + if (!sema_analyse_expr(context, fail)) return false; + if (fail->type->type_kind != TYPE_FAILABLE) { SEMA_ERROR(fail, "This expression is not failable, did you add it by mistake?"); return false; } } - expr_set_type(expr, type_anyerr); + expr->type = type_anyerr; expr->resolve_status = RESOLVE_DONE; return true; } @@ -414,7 +432,7 @@ static inline bool sema_analyse_last_cond(Context *context, Expr *expr, bool may } return sema_analyse_catch_unwrap(context, expr); default: - return sema_analyse_expr(context, NULL, expr); + return sema_analyse_expr(context, expr); } } /** @@ -435,19 +453,19 @@ static inline bool sema_analyse_cond_list(Context *context, Expr *expr, bool may // 1. Special case, there are no entries, so the type is void if (entries == 0) { - expr_set_type(expr, type_void); + expr->type = type_void; return true; } // 2. Walk through each of our declarations / expressions as if they were regular expressions. for (unsigned i = 0; i < entries - 1; i++) { - if (!sema_analyse_expr(context, NULL, dexprs[i])) return false; + if (!sema_analyse_expr(context, dexprs[i])) return false; } if (!sema_analyse_last_cond(context, dexprs[entries - 1], may_unwrap)) return false; - expr_set_type(expr, dexprs[entries - 1]->type); + expr->type = dexprs[entries - 1]->type; expr->resolve_status = RESOLVE_DONE; return true; } @@ -495,11 +513,12 @@ static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_ return false; } // 3e. Expect that it isn't a failable - if (init->failable && !decl->var.unwrap) + if (IS_FAILABLE(init) && !decl->var.unwrap) { - SEMA_ERROR(last, "'%s!' cannot be converted into '%s'.", - type_to_error_string(last->type), - cast_to_bool ? "bool" : type_to_error_string(init->type)); + SEMA_ERROR(last, "%s cannot be converted to %s.", + type_quoted_error_string(last->type), + cast_to_bool ? "'bool'" : type_quoted_error_string(init->type)); + return false; } // TODO document if (!decl->var.unwrap && cast_to_bool && cast_to_bool_kind(decl->var.type_info->type) == CAST_ERROR) @@ -510,11 +529,12 @@ static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_ return true; } // 3a. Check for failables in case of an expression. - if (last->failable) + if (IS_FAILABLE(last)) { - SEMA_ERROR(last, "'%s!' cannot be converted into '%s'.", - type_to_error_string(last->type), - cast_to_bool ? "bool" : type_to_error_string(last->type)); + SEMA_ERROR(last, "%s cannot be converted to %s.", + type_quoted_error_string(last->type), + cast_to_bool ? "'bool'" : type_quoted_error_string(type_no_fail(last->type))); + return false; } // 3b. Cast to bool if that is needed if (cast_to_bool) @@ -618,7 +638,7 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) SCOPE_START // 10. Try to evaluate and implicitly cast to boolean. - if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) + if (!sema_analyse_cond_expr(context, expr)) { // 10a. On failure, pop and return false. return SCOPE_POP_ERROR(); @@ -669,7 +689,7 @@ bool sema_analyse_local_decl(Context *context, Decl *decl) } if (!decl->var.type_info) { - if (!sema_analyse_expr(context, NULL, init_expr)) return false; + if (!sema_analyse_expr(context, init_expr)) return false; decl->type = init_expr->type; // Skip further evaluation. goto EXIT_OK; @@ -692,7 +712,7 @@ bool sema_analyse_local_decl(Context *context, Decl *decl) goto EXIT_OK; } - if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, decl->var.failable || decl->var.unwrap ? FAILABLE_YES : FAILABLE_NO)) return decl_poison(decl); + if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, false)) return decl_poison(decl); if (type_is_inferred) { @@ -700,18 +720,13 @@ bool sema_analyse_local_decl(Context *context, Decl *decl) assert(right_side_type->type_kind == TYPE_ARRAY); decl->type = type_get_array(decl->type->array.base, right_side_type->array.len); } - else if (decl->type) - { - expr_set_type(decl->var.init_expr, decl->type); - } - - if (decl->var.unwrap && !init->failable) + if (decl->var.unwrap && init->type->type_kind != TYPE_FAILABLE) { SEMA_ERROR(decl->var.init_expr, "A failable expression was expected here."); return decl_poison(decl); } - + if (init->expr_kind == EXPR_CONST) init->const_expr.narrowable = false; } EXIT_OK: if (decl->var.is_static) @@ -739,15 +754,37 @@ static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) return sema_analyse_local_decl(context, statement->declare_stmt); } -static inline bool sema_analyse_define_stmt(Context *context, Ast *statement) +/** + * Check "var $foo = ... " and "var $Foo = ..." + */ +static inline bool sema_analyse_var_stmt(Context *context, Ast *statement) { - Decl *decl = statement->declare_stmt; + // 1. Pick the declaration. + Decl *decl = statement->var_stmt; + + // 2. Convert it to a NOP early statement->ast_kind = AST_NOP_STMT; + assert(decl->decl_kind == DECL_VAR); switch (decl->var.kind) { case VARDECL_LOCAL_CT_TYPE: - if (decl->var.type_info && !sema_resolve_type_info(context, decl->var.type_info)) return false; + // Locally declared compile time type. + if (decl->var.type_info) + { + SEMA_ERROR(decl->var.type_info, "Compile time type variables may not have a type."); + return false; + } + Expr *init = decl->var.init_expr; + if (init) + { + if (!sema_analyse_expr_lvalue(context, init)) return false; + if (init->expr_kind != EXPR_TYPEINFO) + { + SEMA_ERROR(decl->var.init_expr, "Expected a type assigned to %s.", decl->name); + return false; + } + } break; case VARDECL_LOCAL_CT: if (decl->var.type_info && !sema_resolve_type_info(context, decl->var.type_info)) return false; @@ -761,7 +798,7 @@ static inline bool sema_analyse_define_stmt(Context *context, Ast *statement) } if (decl->var.init_expr) { - if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr, false)) return false; + if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr)) return false; if (!expr_is_constant_eval(decl->var.init_expr, CONSTANT_EVAL_ANY)) { SEMA_ERROR(decl->var.init_expr, "Expected a constant expression assigned to %s.", decl->name); @@ -779,7 +816,7 @@ static inline bool sema_analyse_define_stmt(Context *context, Ast *statement) Expr *init = decl->var.init_expr; if (init) { - if (!sema_analyse_expr(context, NULL, init)) return false; + if (!sema_analyse_expr(context, init)) return false; if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY)) { SEMA_ERROR(decl->var.init_expr, "Expected a constant expression assigned to %s.", decl->name); @@ -803,7 +840,7 @@ static inline bool sema_analyse_define_stmt(Context *context, Ast *statement) static inline bool sema_analyse_expr_stmt(Context *context, Ast *statement) { - if (!sema_analyse_expr(context, NULL, statement->expr_stmt)) return false; + if (!sema_analyse_expr(context, statement->expr_stmt)) return false; return true; } @@ -860,7 +897,7 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) // Conditional scope start SCOPE_START Expr *cond = statement->for_stmt.cond; - success = sema_analyse_expr_of_required_type(context, type_bool, cond, false); + success = sema_analyse_cond_expr(context, cond); statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond); // If this is const true, then set this to infinite and remove the expression. if (statement->for_stmt.cond->expr_kind == EXPR_CONST && statement->for_stmt.cond->const_expr.b) @@ -876,7 +913,7 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) // Incr scope start SCOPE_START Expr *incr = statement->for_stmt.incr; - success = sema_analyse_expr(context, NULL, incr); + success = sema_analyse_expr(context, incr); statement->for_stmt.incr = context_pop_defers_and_wrap_expr(context, incr); // Incr scope end SCOPE_END; @@ -919,7 +956,8 @@ static inline bool sema_inline_default_iterator(Context *context, Expr *expr, De expr->call_expr = (ExprCall) { .is_type_method = true, }; - return sema_expr_analyse_general_call(context, NULL, expr, decl, inner, decl->decl_kind == DECL_MACRO); + REMINDER("Failability"); + return sema_expr_analyse_general_call(context, expr, decl, inner, decl->decl_kind == DECL_MACRO, false); } static Decl *find_iterator(Context *context, Expr *enumerator) @@ -1085,7 +1123,7 @@ static bool sema_rewrite_foreach_to_for(Context *context, Ast *statement, Expr * expr->decl_expr = iterator; vec_add(init_expr->cond_expr, expr); init_expr->resolve_status = RESOLVE_DONE; - expr_set_type(init_expr, iterator->type); + init_expr->type = iterator->type; // Generate "next": it.next(&value) Expr *call = expr_new(EXPR_CALL, enumerator->span); @@ -1101,7 +1139,12 @@ static bool sema_rewrite_foreach_to_for(Context *context, Ast *statement, Expr * Expr *iterator_access = expr_variable(iterator); expr_insert_addr(iterator_access); - if (!sema_expr_analyse_general_call(context, NULL, call, next_method, iterator_access, next_method->decl_kind == DECL_MACRO)) return false; + REMINDER("Failability"); + if (!sema_expr_analyse_general_call(context, + call, + next_method, + iterator_access, + next_method->decl_kind == DECL_MACRO, false)) return false; call->resolve_status = RESOLVE_DONE; statement->for_stmt = (AstForStmt){ .init = init_expr, @@ -1179,7 +1222,7 @@ static inline bool sema_analyse_foreach_stmt(Context *context, Ast *statement) } // because we don't want the index + variable to move into the internal scope - if (!sema_analyse_expr(context, inferred_type, enumerator)) + if (!sema_analyse_inferred_expr(context, inferred_type, enumerator)) { // Exit early here, because semantic checking might be messed up otherwise. return SCOPE_POP_ERROR(); @@ -1242,6 +1285,12 @@ static inline bool sema_analyse_foreach_stmt(Context *context, Ast *statement) // Analyse the declaration. if (!sema_analyse_local_decl(context, index)) return SCOPE_POP_ERROR(); + if (index->type->type_kind == TYPE_FAILABLE) + { + SEMA_ERROR(index->var.type_info, "The index may not be a failable."); + return SCOPE_POP_ERROR(); + } + // Make sure that the index is an integer. if (!type_is_integer(type_flatten(index->type))) { @@ -1250,11 +1299,6 @@ static inline bool sema_analyse_foreach_stmt(Context *context, Ast *statement) type_to_error_string(index->type)); return SCOPE_POP_ERROR(); } - if (index->var.failable) - { - SEMA_ERROR(index->var.type_info, "The index may not be a failable."); - return SCOPE_POP_ERROR(); - } } // Find the expected type for the value. @@ -1272,7 +1316,7 @@ static inline bool sema_analyse_foreach_stmt(Context *context, Ast *statement) // Analyse the value declaration. if (!sema_analyse_local_decl(context, var)) return SCOPE_POP_ERROR(); - if (var->var.failable) + if (IS_FAILABLE(var)) { SEMA_ERROR(var->var.type_info, "The variable may not be a failable."); return SCOPE_POP_ERROR(); @@ -1287,7 +1331,7 @@ static inline bool sema_analyse_foreach_stmt(Context *context, Ast *statement) { // This is hackish, replace when cast is refactored. Expr dummy = { .resolve_status = RESOLVE_DONE, .span = { var->var.type_info->span.loc, - var->span.end_loc }, .expr_kind = EXPR_IDENTIFIER, .type = expected_var_type, .original_type = expected_var_type }; + var->span.end_loc }, .expr_kind = EXPR_IDENTIFIER, .type = expected_var_type }; if (!cast_implicit(&dummy, var->type)) return SCOPE_POP_ERROR(); assert(dummy.expr_kind == EXPR_CAST); @@ -1418,7 +1462,7 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) static bool sema_analyse_asm_stmt(Context *context, Ast *stmt) { - if (!sema_analyse_expr(context, NULL, stmt->asm_stmt.body)) return false; + if (!sema_analyse_expr(context, stmt->asm_stmt.body)) return false; if (stmt->asm_stmt.body->expr_kind != EXPR_CONST || stmt->asm_stmt.body->const_expr.const_kind != CONST_STRING) { @@ -1594,7 +1638,7 @@ static bool sema_analyse_nextcase_stmt(Context *context, Ast *statement) Type *expected_type = parent->ast_kind == AST_SWITCH_STMT ? parent->switch_stmt.cond->type : type_anyerr; - if (!sema_analyse_expr_of_required_type(context, expected_type, target, false)) return false; + if (!sema_analyse_expr_of_required_type(context, expected_type, target)) return false; if (target->expr_kind == EXPR_CONST) { @@ -1720,9 +1764,9 @@ static bool sema_analyse_case_expr(Context *context, Type* to_type, Ast *case_st Expr *case_expr = case_stmt->case_stmt.expr; // 1. Try to do implicit conversion to the correct type. - if (!sema_analyse_expr(context, to_type, case_expr)) return false; + if (!sema_analyse_inferred_expr(context, to_type, case_expr)) return false; - Type *case_type = case_expr->original_type->canonical; + Type *case_type = case_expr->type->canonical; Type *to_type_canonical = to_type->canonical; // 3. If we already have the same type we're done. @@ -1730,7 +1774,7 @@ static bool sema_analyse_case_expr(Context *context, Type* to_type, Ast *case_st // 4. Otherwise check if we have an enum receiving type and a number on // in the case. In that case we do an implicit conversion. - if (to_type_canonical->type_kind == TYPE_ENUM && type_is_any_integer(case_type)) + if (to_type_canonical->type_kind == TYPE_ENUM && type_is_integer(case_type)) { return cast(case_expr, to_type); } @@ -1758,7 +1802,7 @@ static inline bool sema_analyse_compound_statement_no_scope(Context *context, As static inline bool sema_check_type_case(Context *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index) { Expr *expr = case_stmt->case_stmt.expr; - if (!sema_analyse_expr_of_required_type(context, type_typeid, expr, false)) return false; + if (!sema_analyse_expr_of_required_type(context, type_typeid, expr)) return false; if (expr->expr_kind == EXPR_CONST) { @@ -1810,7 +1854,7 @@ static bool sema_analyse_switch_body(Context *context, Ast *statement, SourceSpa return false; } // We need an if chain if this isn't an integer type. - bool if_chain = !type_is_any_integer(type_flatten(switch_type)); + bool if_chain = !type_is_integer(type_flatten(switch_type)); Ast *default_case = NULL; assert(context->active_scope.defer_start == context->active_scope.defer_last); @@ -1895,7 +1939,7 @@ static bool sema_analyse_ct_switch_body(Context *context, Ast *statement) if (use_type_id) { Expr *expr = stmt->case_stmt.expr; - if (!sema_analyse_expr_value(context, NULL, expr)) return false; + if (!sema_analyse_expr_lvalue(context, expr)) return false; if (stmt->case_stmt.expr->expr_kind != EXPR_TYPEINFO) { SEMA_ERROR(stmt, "Unexpectedly encountered a value rather than a type in the $case"); @@ -1921,8 +1965,7 @@ static bool sema_analyse_ct_switch_body(Context *context, Ast *statement) { if (!sema_analyse_expr_of_required_type(context, cond->type, - stmt->case_stmt.expr, - false)) + stmt->case_stmt.expr)) { return false; } @@ -1956,7 +1999,7 @@ static bool sema_analyse_ct_switch_body(Context *context, Ast *statement) static bool sema_analyse_ct_switch_stmt(Context *context, Ast *statement) { Expr *cond = statement->ct_switch_stmt.cond; - if (!sema_analyse_expr(context, NULL, cond)) return false; + if (!sema_analyse_expr(context, cond)) return false; if (cond->expr_kind != EXPR_CONST && cond->expr_kind != EXPR_TYPEID) { SEMA_ERROR(cond, "A compile time $switch must be over a constant value."); @@ -2018,7 +2061,7 @@ bool sema_analyse_ct_assert_stmt(Context *context, Ast *statement) Expr *message = statement->ct_assert_stmt.message; if (message) { - if (!sema_analyse_expr(context, type_compstr, message)) return false; + if (!sema_analyse_expr(context, message)) return false; if (message->type->type_kind != TYPE_STRLIT) { SEMA_ERROR(message, "Expected a string as the error message."); @@ -2049,7 +2092,7 @@ bool sema_analyse_assert_stmt(Context *context, Ast *statement) Expr *message = statement->assert_stmt.message; if (message) { - if (!sema_analyse_expr(context, type_compstr, message)) return false; + if (!sema_analyse_expr(context, message)) return false; if (message->type->type_kind != TYPE_STRLIT) { SEMA_ERROR(message, "Expected a string as the error message."); @@ -2061,7 +2104,7 @@ bool sema_analyse_assert_stmt(Context *context, Ast *statement) } else { - if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) return false; + if (!sema_analyse_assigned_expr(context, type_bool, expr, false)) return false; } return true; } @@ -2144,8 +2187,8 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement return false; case AST_DEFER_STMT: return sema_analyse_defer_stmt(context, statement); - case AST_DEFINE_STMT: - return sema_analyse_define_stmt(context, statement); + case AST_VAR_STMT: + return sema_analyse_var_stmt(context, statement); case AST_DO_STMT: return sema_analyse_do_stmt(context, statement); case AST_EXPR_STMT: @@ -2219,7 +2262,7 @@ static bool sema_analyse_requires(Context *context, Ast *docs, Ast ***asserts) SEMA_ERROR(expr, "Only expressions are allowed."); return false; } - if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) return false; + if (!sema_analyse_cond_expr(context, expr)) return false; Ast *assert = new_ast(AST_ASSERT_STMT, expr->span); assert->assert_stmt.expr = expr; assert->assert_stmt.message = comment; @@ -2243,12 +2286,10 @@ bool sema_analyse_function_body(Context *context, Decl *func) .current_local = &context->locals[0] }; context->macro_scope = (MacroScope) { 0 }; - context->failable_return = signature->failable; // Clear returns vec_resize(context->returns, 0); context->scope_id = 0; - context->expected_block_type = NULL; context->in_volatile_section = 0; context->continue_target = 0; context->next_target = 0; @@ -2283,11 +2324,11 @@ bool sema_analyse_function_body(Context *context, Decl *func) assert(context->active_scope.depth == 1); if (!context->active_scope.jump_end) { - Type *canonical_rtype = signature->rtype->type->canonical; + Type *canonical_rtype = type_no_fail(signature->rtype->type)->canonical; if (canonical_rtype != type_void) { // IMPROVE better pointer to end. - SEMA_ERROR(func, "Missing return statement at the end of the function."); + SEMA_TOKID_ERROR(func->name_token, "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 4b4573f95..8a92774bb 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -18,40 +18,34 @@ static inline bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info) bool sema_resolve_array_like_len(Context *context, TypeInfo *type_info, ArrayIndex *len_ref) { Expr *len_expr = type_info->array.len; - if (!sema_analyse_expr(context, type_usize, len_expr)) return type_info_poison(type_info); + if (!sema_analyse_expr(context, len_expr)) return type_info_poison(type_info); if (len_expr->expr_kind != EXPR_CONST) { SEMA_ERROR(len_expr, "Expected a constant value as size."); return type_info_poison(type_info); } - if (!type_is_any_integer(len_expr->type->canonical)) + if (!type_is_integer(len_expr->type->canonical)) { SEMA_ERROR(len_expr, "Expected an integer size."); return type_info_poison(type_info); } - BigInt *big_len = &len_expr->const_expr.i; + bool is_vector = type_info->kind == TYPE_INFO_VECTOR; - switch (bigint_cmp_zero(big_len)) + Int len = len_expr->const_expr.ixx; + if (int_is_neg(len)) { - case CMP_LT: - SEMA_ERROR(len_expr, - is_vector ? "A vector may not have a negative width." : - "An array may not have a negative size."); - return type_info_poison(type_info); - case CMP_EQ: - if (is_vector) - { - SEMA_ERROR(len_expr, "A vector may not have a zero width."); - return type_info_poison(type_info); - } - break; - case CMP_GT: - break; + SEMA_ERROR(len_expr, + is_vector ? "A vector may not have a negative width." : + "An array may not have a negative size."); + return type_info_poison(type_info); } - BigInt max; - bigint_init_unsigned(&max, is_vector ? MAX_VECTOR_WIDTH : MAX_ARRAY_SIZE); - if (bigint_cmp(big_len, &max) == CMP_GT) + if (is_vector && int_is_zero(len)) + { + SEMA_ERROR(len_expr, "A vector may not have a zero width."); + return type_info_poison(type_info); + } + if (int_icomp(len, is_vector ? MAX_VECTOR_WIDTH : MAX_ARRAY_SIZE, BINARYOP_GT)) { if (is_vector) { @@ -63,7 +57,7 @@ bool sema_resolve_array_like_len(Context *context, TypeInfo *type_info, ArrayInd } return type_info_poison(type_info); } - *len_ref = bigint_as_signed(big_len); + *len_ref = len.i.low; return true; } @@ -148,6 +142,11 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) case DECL_VAR: if (decl->var.kind == VARDECL_PARAM_CT_TYPE || decl->var.kind == VARDECL_LOCAL_CT_TYPE) { + if (!decl->var.init_expr) + { + SEMA_ERROR(type_info, "The variable '%s' is not defined yet.", decl->name); + return false; + } assert(decl->var.init_expr->expr_kind == EXPR_TYPEINFO); assert(decl->var.init_expr->resolve_status == RESOLVE_DONE); *type_info = *decl->var.init_expr->type_expr; @@ -213,6 +212,8 @@ bool sema_resolve_type(Context *context, Type *type) return sema_resolve_type(context, type->array.base); case TYPE_VIRTUAL: TODO; + case TYPE_FAILABLE: + return sema_resolve_type(context, type->failable); } return sema_analyse_decl(context, type->decl); } @@ -243,17 +244,13 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info, bool allow case TYPE_INFO_EXPRESSION: { Expr *expr = type_info->unresolved_type_expr; - if (!sema_analyse_expr(context, NULL, expr)) + if (!sema_analyse_expr(context, expr)) { return type_info_poison(type_info); } - if (!cast_implicitly_to_runtime(expr)) - { - SEMA_ERROR(expr, "The expression does not fit any runtime type."); - return false; - } type_info->type = expr->type; type_info->resolve_status = RESOLVE_DONE; + assert(!type_info->failable); return true; } case TYPE_INFO_INFERRED_ARRAY: @@ -272,6 +269,10 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info, bool allow if (!sema_resolve_ptr_type(context, type_info)) return false; break; } + if (type_info->failable) + { + type_info->type = type_get_failable(type_info->type); + } return true; } diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 5bfd48f0d..64f00549e 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -13,7 +13,7 @@ void sema_shadow_error(Decl *decl, Decl *old) bool sema_resolve_type_info_maybe_inferred(Context *context, TypeInfo *type_info, bool allow_inferred_type) { if (!sema_resolve_type_shallow(context, type_info, allow_inferred_type, false)) return false; - Type *type = type_info->type; + Type *type = type_no_fail(type_info->type); // usize and similar typedefs will not have a decl. if (type->type_kind == TYPE_TYPEDEF && type->decl == NULL) return true; if (!type_is_user_defined(type)) return true; @@ -95,7 +95,7 @@ Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr) context_pop_defers_to(context, &defers); if (defers.end == defers.start) return expr; Expr *wrap = expr_new(EXPR_SCOPED_EXPR, expr->span); - expr_copy_types(wrap, expr); + wrap->type = expr->type; wrap->resolve_status = RESOLVE_DONE; wrap->expr_scope.expr = expr; wrap->expr_scope.defers = defers; diff --git a/src/compiler/types.c b/src/compiler/types.c index 3c43e9de3..a0222b702 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -12,7 +12,7 @@ static struct Type f16, f32, f64, f128, fxx; Type usz, isz, uptr, iptr, uptrdiff, iptrdiff; Type voidstar, typeid, anyerr, typeinfo, ctlist; - Type str, virtual, virtual_generic; + Type str, virtual, virtual_generic, anyfail; } t; Type *type_bool = &t.u1; @@ -47,6 +47,7 @@ Type *type_compfloat = &t.fxx; Type *type_compstr = &t.str; Type *type_anyerr = &t.anyerr; Type *type_complist = &t.ctlist; +Type *type_anyfail = &t.anyfail; static unsigned size_subarray; static AlignSize alignment_subarray; @@ -57,7 +58,8 @@ static AlignSize max_alignment_vector; #define PTR_OFFSET 0 #define INFERRED_ARRAY_OFFSET 1 #define SUB_ARRAY_OFFSET 2 -#define ARRAY_OFFSET 3 +#define FAILABLE_OFFSET 3 +#define ARRAY_OFFSET 4 Type *type_cint(void) { @@ -144,6 +146,10 @@ const char *type_to_error_string(Type *type) } asprintf(&buffer, "%s*", type_to_error_string(type->pointer)); return buffer; + case TYPE_FAILABLE: + if (!type->failable) return "void!"; + asprintf(&buffer, "%s!", type_to_error_string(type->failable)); + return buffer; case TYPE_STRLIT: return "string literal"; case TYPE_ARRAY: @@ -187,12 +193,15 @@ void type_append_signature_name(Type *type, char *dst, size_t *offset) ByteSize type_size(Type *type) { +RETRY: switch (type->type_kind) { case TYPE_BITSTRUCT: - return type_size(type->decl->bitstruct.base_type->type); + type = type->decl->bitstruct.base_type->type; + goto RETRY; case TYPE_DISTINCT: - return type_size(type->decl->distinct_decl.base_type); + type = type->decl->distinct_decl.base_type; + goto RETRY; case TYPE_VECTOR: { ByteSize width = type_size(type->vector.base) * type->vector.len; @@ -205,10 +214,16 @@ ByteSize type_size(Type *type) } case CT_TYPES: UNREACHABLE; + case TYPE_FAILABLE: + type = type->failable; + if (!type) return 1; + goto RETRY; case TYPE_TYPEDEF: - return type_size(type->canonical); + type = type->canonical; + goto RETRY; case TYPE_ERRTYPE: - return type_size(type_usize->canonical); + type = type_iptr->canonical; + goto RETRY; case TYPE_ENUM: return type->decl->enums.type_info->type->canonical->builtin.bytesize; case TYPE_STRUCT: @@ -229,7 +244,7 @@ ByteSize type_size(Type *type) case TYPE_STRLIT: case TYPE_FUNC: case TYPE_POINTER: - return t.usz.canonical->builtin.bytesize; + return t.iptr.canonical->builtin.bytesize; case TYPE_ARRAY: return type_size(type->array.base) * type->array.len; case TYPE_SUBARRAY: @@ -332,16 +347,22 @@ Type *type_abi_find_single_struct_element(Type *type) bool type_is_abi_aggregate(Type *type) { + RETRY: switch (type->type_kind) { case TYPE_POISONED: return false; + case TYPE_FAILABLE: + type = type->failable; + if (!type) return false; + goto RETRY; case TYPE_DISTINCT: - return type_is_abi_aggregate(type->decl->distinct_decl.base_type); + type = type->decl->distinct_decl.base_type; + goto RETRY; case TYPE_TYPEDEF: - return type_is_abi_aggregate(type->canonical); + type = type->canonical; + goto RETRY; case TYPE_BITSTRUCT: - return type_is_abi_aggregate(type->decl->bitstruct.base_type->type); case ALL_FLOATS: case TYPE_VOID: case ALL_INTS: @@ -495,14 +516,16 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements) RETRY: switch (type->type_kind) { + case TYPE_FAILABLE: + type = type->failable; + if (!type) return false; + goto RETRY; case TYPE_DISTINCT: type = type->decl->distinct_decl.base_type; goto RETRY; case TYPE_BITSTRUCT: type = type->decl->bitstruct.base_type->type; goto RETRY; - case TYPE_FXX: - case TYPE_IXX: case TYPE_VOID: case TYPE_TYPEID: case TYPE_FUNC: @@ -584,7 +607,7 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements) type = type_int_unsigned_by_bitsize(type->builtin.bitsize); break; case ALL_UNSIGNED_INTS: - case ALL_REAL_FLOATS: + case ALL_FLOATS: case TYPE_VECTOR: break; case TYPE_POINTER: @@ -700,6 +723,7 @@ bool type_is_comparable(Type *type) AlignSize type_abi_alignment(Type *type) { + RETRY: switch (type->type_kind) { case TYPE_POISONED: @@ -707,7 +731,8 @@ AlignSize type_abi_alignment(Type *type) case TYPE_UNTYPED_LIST: UNREACHABLE; case TYPE_BITSTRUCT: - return type_abi_alignment(type->decl->bitstruct.base_type->type); + type = type->decl->bitstruct.base_type->type; + goto RETRY; case TYPE_VECTOR: { ByteSize width = type_size(type->vector.base) * type->vector.len; @@ -721,10 +746,16 @@ AlignSize type_abi_alignment(Type *type) } case TYPE_VOID: return 1; + case TYPE_FAILABLE: + type = type->failable; + if (!type) return 1; + goto RETRY; case TYPE_DISTINCT: - return type_abi_alignment(type->decl->distinct_decl.base_type); + type = type->decl->distinct_decl.base_type; + goto RETRY; case TYPE_TYPEDEF: - return type_abi_alignment(type->canonical); + type = type->canonical; + goto RETRY; case TYPE_ENUM: return type->decl->enums.type_info->type->canonical->builtin.abi_alignment; case TYPE_ERRTYPE: @@ -732,8 +763,6 @@ AlignSize type_abi_alignment(Type *type) case TYPE_STRUCT: case TYPE_UNION: return type->decl->alignment; - case TYPE_TYPEID: - return type_abi_alignment(type_usize); case TYPE_BOOL: case ALL_INTS: case ALL_FLOATS: @@ -745,10 +774,12 @@ AlignSize type_abi_alignment(Type *type) case TYPE_FUNC: case TYPE_POINTER: case TYPE_STRLIT: + case TYPE_TYPEID: return t.iptr.canonical->builtin.abi_alignment; case TYPE_ARRAY: case TYPE_INFERRED_ARRAY: - return type_abi_alignment(type->array.base); + type = type->array.base; + goto RETRY; case TYPE_SUBARRAY: return alignment_subarray; } @@ -789,6 +820,30 @@ static Type *type_generate_ptr(Type *ptr_type, bool canonical) return ptr; } +static Type *type_generate_failable(Type *failable_type, bool canonical) +{ + if (canonical) failable_type = failable_type->canonical; + if (!failable_type->type_cache) + { + create_type_cache(failable_type); + } + Type *failable = failable_type->type_cache[FAILABLE_OFFSET]; + if (failable == NULL) + { + failable = type_new(TYPE_FAILABLE, strformat("%s!", failable_type->name)); + failable->pointer = failable_type; + failable_type->type_cache[FAILABLE_OFFSET] = failable; + if (failable_type == failable_type->canonical) + { + failable->canonical = failable; + } + else + { + failable->canonical = type_generate_failable(failable_type->canonical, true); + } + } + return failable; +} static Type *type_generate_subarray(Type *arr_type, bool canonical) { @@ -843,12 +898,28 @@ static Type *type_generate_inferred_array(Type *arr_type, bool canonical) } +Type *type_get_ptr_recurse(Type *ptr_type) +{ + if (ptr_type->type_kind == TYPE_FAILABLE) + { + ptr_type = ptr_type->failable; + return type_get_failable(type_get_ptr(ptr_type)); + } + return type_get_ptr(ptr_type); +} Type *type_get_ptr(Type *ptr_type) { + assert(ptr_type->type_kind != TYPE_FAILABLE); return type_generate_ptr(ptr_type, false); } +Type *type_get_failable(Type *failable_type) +{ + assert(failable_type->type_kind != TYPE_FAILABLE); + return type_generate_failable(failable_type, false); +} + Type *type_get_subarray(Type *arr_type) { return type_generate_subarray(arr_type, false); @@ -1050,9 +1121,8 @@ bool type_is_valid_for_vector(Type *type) RETRY: switch (type->type_kind) { - case ALL_SIGNED_INTS: - case ALL_UNSIGNED_INTS: - case ALL_REAL_FLOATS: + case ALL_INTS: + case ALL_FLOATS: case TYPE_BOOL: return true; case TYPE_TYPEDEF: @@ -1143,6 +1213,17 @@ static void type_append_name_to_scratch(Type *type) type_append_name_to_scratch(type->pointer); scratch_buffer_append_char('*'); break; + case TYPE_FAILABLE: + if (type->failable) + { + type_append_name_to_scratch(type->failable); + } + else + { + scratch_buffer_append("void"); + } + scratch_buffer_append_char('!'); + break; case TYPE_SUBARRAY: type_append_name_to_scratch(type->pointer); scratch_buffer_append("[]"); @@ -1168,8 +1249,6 @@ static void type_append_name_to_scratch(Type *type) case TYPE_VECTOR: scratch_buffer_append(type->name); break; - case TYPE_IXX: - case TYPE_FXX: case TYPE_STRLIT: case TYPE_UNTYPED_LIST: case TYPE_INFERRED_ARRAY: @@ -1191,7 +1270,6 @@ Type *type_find_function_type(FunctionSignature *signature) { scratch_buffer_clear(); type_append_name_to_scratch(signature->rtype->type); - if (signature->failable) scratch_buffer_append_char('!'); scratch_buffer_append_char('('); unsigned elements = vec_size(signature->params); for (unsigned i = 0; i < elements; i++) @@ -1267,6 +1345,7 @@ void type_setup(PlatformTarget *target) type_create("typeinfo", &t.typeinfo, TYPE_TYPEINFO, 1, 1, 1); type_create("complist", &t.ctlist, TYPE_UNTYPED_LIST, 1, 1, 1); + t.anyfail = (Type){ .type_kind = TYPE_FAILABLE, .failable = type_void }; type_init("typeid", &t.typeid, TYPE_TYPEID, target->width_pointer, target->align_pointer); type_init("void*", &t.voidstar, TYPE_POINTER, target->width_pointer, target->align_pointer); @@ -1276,8 +1355,6 @@ void type_setup(PlatformTarget *target) type_init("virtual*", &t.virtual, TYPE_VIRTUAL_ANY, target->width_pointer * 2, target->align_pointer); type_init("virtual_generic", &t.virtual_generic, TYPE_VIRTUAL, target->width_pointer * 2, target->align_pointer); - type_create("compint", &t.ixx, TYPE_IXX, 1, 1, 1); - type_create("compfloat", &t.fxx, TYPE_FXX, 1, 1, 1); type_create_alias("usize", &t.usz, type_int_unsigned_by_bitsize(target->width_pointer)); type_create_alias("isize", &t.isz, type_int_signed_by_bitsize(target->width_pointer)); @@ -1293,6 +1370,33 @@ void type_setup(PlatformTarget *target) type_init("anyerr", &t.anyerr, TYPE_ANYERR, target->width_pointer, target->align_pointer); } +int type_kind_bitsize(TypeKind kind) +{ + switch (kind) + { + case TYPE_I8: + case TYPE_U8: + return 8; + case TYPE_I16: + case TYPE_U16: + case TYPE_F16: + return 16; + case TYPE_I32: + case TYPE_U32: + case TYPE_F32: + return 32; + case TYPE_I64: + case TYPE_U64: + case TYPE_F64: + return 64; + case TYPE_I128: + case TYPE_U128: + case TYPE_F128: + return 128; + default: + UNREACHABLE; + } +} bool type_is_scalar(Type *type) { RETRY: @@ -1326,6 +1430,10 @@ bool type_is_scalar(Type *type) case TYPE_DISTINCT: type = type->decl->distinct_decl.base_type; goto RETRY; + case TYPE_FAILABLE: + type = type->failable; + if (!type) return false; + goto RETRY; case TYPE_TYPEDEF: type = type->canonical; goto RETRY; @@ -1412,46 +1520,6 @@ Type *type_from_token(TokenType type) } } -bool type_may_convert_to_boolean(Type *type) -{ - RETRY: - switch (type->type_kind) - { - case TYPE_POISONED: - return false; - case TYPE_TYPEDEF: - type = type->canonical; - goto RETRY; - case TYPE_DISTINCT: - type = type->decl->distinct_decl.base_type; - goto RETRY; - case ALL_INTS: - case ALL_FLOATS: - case TYPE_POINTER: - case TYPE_VIRTUAL: - case TYPE_VIRTUAL_ANY: - case TYPE_BOOL: - case TYPE_SUBARRAY: - case TYPE_ENUM: - case TYPE_ANYERR: - case TYPE_ERRTYPE: - case TYPE_STRLIT: - case TYPE_UNTYPED_LIST: - return true; - case TYPE_FUNC: - case TYPE_ARRAY: - case TYPE_INFERRED_ARRAY: - case TYPE_BITSTRUCT: - case TYPE_VECTOR: - case TYPE_STRUCT: - case TYPE_TYPEID: - case TYPE_TYPEINFO: - case TYPE_UNION: - case TYPE_VOID: - return false; - } - UNREACHABLE -} bool type_may_have_sub_elements(Type *type) { // An alias is not ok. @@ -1476,15 +1544,13 @@ Type *type_find_max_num_type(Type *num_type, Type *other_num) assert(kind != other_kind); // 1. The only conversions need to happen if the other type is a number. - if (other_kind < TYPE_I8 || other_kind > TYPE_FXX) return NULL; + if (other_kind < TYPE_INTEGER_FIRST || other_kind > TYPE_FLOAT_LAST) return NULL; // 2. First check the float case. - if (other_kind >= TYPE_F16 && other_kind <= TYPE_FXX) + if (other_kind >= TYPE_FLOAT_FIRST && other_kind <= TYPE_FLOAT_LAST) { switch (other_kind) { - case TYPE_FXX: - return kind <= TYPE_IXX ? type_double : num_type; case TYPE_F16: case TYPE_F32: case TYPE_F64: @@ -1499,9 +1565,6 @@ Type *type_find_max_num_type(Type *num_type, Type *other_num) // Handle integer <=> integer conversions. assert(type_kind_is_any_integer(other_kind) && type_is_integer(num_type)); - // 3. If the other type is IXX, return the current type. - if (other_kind == TYPE_IXX) return num_type; - // 4. Check the bit sizes. unsigned other_bit_size = other_num->builtin.bitsize; unsigned bit_size = num_type->builtin.bitsize; @@ -1574,8 +1637,20 @@ static inline Type *type_find_max_ptr_type(Type *type, Type *other) Type *type_find_max_type(Type *type, Type *other) { - assert(type->canonical == type); - assert(other->canonical == other); + if (type == type_anyfail) + { + return type_get_opt_fail(other, true); + } + if (other == type_anyfail) + { + return type_get_failable(type); + } + + type = type->canonical; + other = other->canonical; + + assert(type->type_kind != TYPE_FAILABLE && other->type_kind != TYPE_FAILABLE); + if (type == other) return type; // Sort types @@ -1590,6 +1665,7 @@ Type *type_find_max_type(Type *type, Type *other) { case TYPE_INFERRED_ARRAY: case TYPE_POISONED: + case TYPE_FAILABLE: UNREACHABLE case TYPE_VOID: case TYPE_BOOL: @@ -1598,17 +1674,12 @@ Type *type_find_max_type(Type *type, Type *other) case TYPE_VIRTUAL_ANY: case TYPE_BITSTRUCT: return NULL; - case TYPE_IXX: + case ALL_INTS: if (other->type_kind == TYPE_DISTINCT && type_underlying_is_numeric(other)) return other; - FALLTHROUGH; - case ALL_SIGNED_INTS: - case ALL_UNSIGNED_INTS: if (other->type_kind == TYPE_ENUM) return type_find_max_type(type, other->decl->enums.type_info->type->canonical); return type_find_max_num_type(type, other); - case TYPE_FXX: + case ALL_FLOATS: if (other->type_kind == TYPE_DISTINCT && type_is_float(other->decl->distinct_decl.base_type)) return other; - FALLTHROUGH; - case ALL_REAL_FLOATS: return type_find_max_num_type(type, other); case TYPE_POINTER: return type_find_max_ptr_type(type, other); @@ -1664,7 +1735,7 @@ Type *type_find_common_ancestor(Type *left, Type *right) if (left->type_kind == TYPE_POINTER) { Type *common = type_find_common_ancestor(left->pointer, right->pointer); - return common ? type_generate_ptr(common, true) : NULL; + return common ? type_get_ptr(common) : NULL; } if (left->type_kind != TYPE_STRUCT) return NULL; diff --git a/src/compiler_tests/tests.c b/src/compiler_tests/tests.c index 3864b8398..2faca7db0 100644 --- a/src/compiler_tests/tests.c +++ b/src/compiler_tests/tests.c @@ -10,7 +10,6 @@ #include "benchmark.h" - void test_file(void) { File file; @@ -37,12 +36,160 @@ void test_file(void) TEST_ASSERT(source_file_find_position_in_file(&file, 21).line == 3, "Expected third line"); TEST_ASSERT(source_file_find_position_in_file(&file, 25).line == 3, "Expected third line"); TEST_ASSERT(source_file_find_position_in_file(&file, 31).line == 4, "Expected fourth line"); +} +#define i128(x_, y_) ((Int128){x_, y_}) +void test128() +{ + printf("Begin i128 testing.\n"); + Int128 addres = i128_add(i128(0x123, 0x123), i128(0x222, 0x333)); + TEST_ASSERTF(addres.high == 0x345 && addres.low == 0x456, "i128 add failed with small numbers was %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + addres = i128_add(i128(0x123, UINT64_MAX), i128(0x222, 0x1)); + TEST_ASSERT(addres.high == 0x346 && addres.low == 0, "i128 add failed with simple overflow"); + addres = i128_add(i128(0x123, UINT64_MAX), i128(0x222, UINT64_MAX)); + TEST_ASSERT(addres.high == 0x346 && addres.low == UINT64_MAX - 1, "i128 add failed with simple overflow2"); + addres = i128_add(i128(UINT64_MAX, UINT64_MAX), i128(0x0, 0x1)); + TEST_ASSERT(addres.high == 0 && addres.low == 0, "i128 add failed with wrap"); + addres = i128_add(i128(UINT64_MAX, UINT64_MAX), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX - 1, "i128 add failed overflow with wrap"); + printf("-- i128 Add - Ok.\n"); + addres = i128_sub(i128(0x345, 0x457), i128(0x222, 0x333)); + TEST_ASSERTF(addres.high == 0x123 && addres.low == 0x124, "i128 sub failed with small numbers was %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + addres = i128_sub(i128(0x346, 0), i128(0x222, 0x1)); + TEST_ASSERTF(addres.high == 0x123 && addres.low == UINT64_MAX, "i128 sub failed with simple overflow %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + addres = i128_sub(i128(0x346, UINT64_MAX - 1), i128(0x222, UINT64_MAX)); + TEST_ASSERT(addres.high == 0x123 && addres.low == UINT64_MAX, "i128 sub failed with simple overflow2"); + addres = i128_sub(i128(0, 0), i128(0x0, 0x1)); + TEST_ASSERTF(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "i128 sub failed with wrap %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + addres = i128_sub(i128(UINT64_MAX, UINT64_MAX - 1), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "i128 sub failed overflow with wrap"); + printf("-- i128 Sub - Ok.\n"); + addres = i128_and(i128(0x0, 0x0), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(addres.high == 0 && addres.low == 0, "And failed"); + addres = i128_and(i128(0x123, 0x123456789abcdef1), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(addres.high == 0x123 && addres.low == 0x123456789abcdef1, "And failed"); + addres = i128_and(i128(0xabcdef2233, 0x123456789A), i128(0x0F0F0F0F0F0F, 0xF0F0F0F0F0F0)); + TEST_ASSERT(addres.high == 0x0b0d0f0203 && addres.low == 0x1030507090, "And failed"); + printf("-- i128 And - Ok.\n"); + addres = i128_or(i128(0x0, 0x0), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "Or failed"); + addres = i128_or(i128(0x123, 0x123456789abcdef1), i128(0x123203, 0x0)); + TEST_ASSERT(addres.high == 0x123323 && addres.low == 0x123456789abcdef1, "Or failed"); + addres = i128_or(i128(0xabcdef2233, 0x123456789A), i128(0x0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0)); + TEST_ASSERTF(addres.high == 0x0FAFCFEF2F3F && addres.low == 0xF0F0F2F4F6F8FA, "Or failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + printf("-- i128 Or - Ok.\n"); + addres = i128_xor(i128(0x0, 0x0), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "Xor failed"); + addres = i128_xor(i128(0x123, 0x123456789abcdef1), i128(0x123223, 0x0)); + TEST_ASSERT(addres.high == 0x123300 && addres.low == 0x123456789abcdef1, "Xor failed"); + addres = i128_xor(i128(0xabcdef2233, 0x123456789A), i128(0x0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0)); + TEST_ASSERTF(addres.high == 0x0FA4C2E02d3c && addres.low == 0xF0F0e2c4a6886A, "Xor failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + printf("-- i128 Xor - Ok.\n"); + addres = i128_neg(i128(0x0, 0x0)); + TEST_ASSERT(addres.high == 0 && addres.low == 0, "Neg failed"); + addres = i128_neg(i128(0x123, 0x123456789abcdef1)); + TEST_ASSERT(addres.high == ~((uint64_t)0x123) && addres.low == ~(uint64_t)0x123456789abcdef0, "Neg failed"); + addres = i128_neg(i128(0xabcdef2233, 0x123456789A)); + TEST_ASSERTF(addres.high == ~(uint64_t)0xabcdef2233 && addres.low == ~(uint64_t)0x1234567899, "Neg failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + printf("-- i128 Neg - Ok.\n"); + + addres = i128_from_str("1123"); + TEST_ASSERT(addres.high == 0 && addres.low == 1123, "Init failed"); + addres = i128_from_str("10000000000000000000012344434232"); + TEST_ASSERT(addres.high == 0x7e37be2022 && addres.low == 0xc0914b295fc91e38, "Init failed"); + + addres = i128_mult(i128(0x111, 0x222), i128(0, 2)); + TEST_ASSERTF(addres.high == 0x222 && addres.low == 0x444, "Mult failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + addres = i128_mult(i128(0x111, 0x222), i128(2, 0)); + TEST_ASSERTF(addres.high == 0x444 && addres.low == 0, "Mult failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + addres = i128_mult(i128_from_str("523871293871232000123"), i128_from_str("283712312938293299")); + + TEST_ASSERTF(i128_ucomp(i128_from_str("148628736466183585621117368965778075777"), addres) == CMP_EQ, "Mult failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + printf("-- i128 Mult ok.\n"); + + TEST_ASSERTF(i128_ucomp(i128_from_str("123"), i128_from_str("123")) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_ucomp(i128_from_str("123"), i128_from_str("124")) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_ucomp(i128_from_str("123"), i128_from_str("121")) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_ucomp(i128(0x222, 0x111), i128(0x111, 0x222)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_ucomp(i128(0x111, 0x222), i128(0x222, 0x111)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_ucomp(i128(0x222, 0x111), i128(0x222, 0x111)) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_ucomp(i128(UINT64_MAX, 0x111), i128(0x111, 0x222)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_ucomp(i128(0x111, 0x222), i128(UINT64_MAX, 0x111)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + printf("-- i128 Ucomp ok.\n"); + + TEST_ASSERTF(i128_scomp(i128_from_str("123"), i128_from_str("123")) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_scomp(i128_from_str("123"), i128_from_str("124")) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_scomp(i128_from_str("123"), i128_from_str("121")) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_scomp(i128(0x222, 0x111), i128(0x111, 0x222)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_scomp(i128(0x111, 0x222), i128(0x222, 0x111)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_scomp(i128(0x222, 0x111), i128(0x222, 0x111)) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_scomp(i128(UINT64_MAX, 0x111), i128(0x111, 0x222)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_scomp(i128(0x111, 0x222), i128(UINT64_MAX, 0x111)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + printf("-- i128 Scomp ok.\n"); + + + addres = i128_shl(i128(0x234, 0x123456), i128(0, 0x4)); + TEST_ASSERT(addres.high == 0x2340 && addres.low == 0x1234560, "shl failed"); + addres = i128_shl(i128(0x234, 0x1234561), i128(0, 128)); + TEST_ASSERT(addres.high == 0 && addres.low == 0, "shl failed"); + addres = i128_shl(i128(0x234, 0x1234561), i128(1, 1)); + TEST_ASSERT(addres.high == 0 && addres.low == 0, "shl failed"); + addres = i128_shl(i128(0x234, 0x1234561), i128(0, 64)); + TEST_ASSERT(addres.high == 0x1234561 && addres.low == 0, "shl failed"); + printf("-- i128 Shl ok.\n"); + + addres = i128_lshr(i128(0x234, 0x123456), i128(0, 0x4)); + TEST_ASSERTF(addres.high == 0x23 && addres.low == 0x4000000000012345, "lshr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + addres = i128_lshr(i128(0x234, 0x1234561), i128(0, 128)); + TEST_ASSERT(addres.high == 0 && addres.low == 0, "lshr failed"); + addres = i128_lshr(i128(0x234, 0x1234561), i128(1, 1)); + TEST_ASSERT(addres.high == 0 && addres.low == 0, "lshr failed"); + addres = i128_lshr(i128(0x234, 0x1234561), i128(0, 64)); + TEST_ASSERTF(addres.high == 0 && addres.low == 0x234, "lshr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + printf("-- i128 Lshr ok.\n"); + + addres = i128_ashr(i128(0x234, 0x123456), i128(0, 0x4)); + TEST_ASSERTF(addres.high == 0x23 && addres.low == 0x4000000000012345, "ashr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + addres = i128_ashr(i128(0xF000000000000234, 0x123456), i128(0, 0x4)); + TEST_ASSERTF(addres.high == 0xFF00000000000023 && addres.low == 0x4000000000012345, "ashr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + addres = i128_ashr(i128(0x234, 0x1234561), i128(0, 128)); + TEST_ASSERT(addres.high == 0 && addres.low == 0, "ashr failed"); + addres = i128_ashr(i128(0xF000000000000234, 0x1234561), i128(0, 128)); + TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "ashr failed"); + addres = i128_ashr(i128(0x234, 0x1234561), i128(1, 1)); + TEST_ASSERT(addres.high == 0 && addres.low == 0, "ashr failed"); + addres = i128_ashr(i128(0xF000000000000234, 0x1234561), i128(1, 1)); + TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "ashr failed"); + addres = i128_ashr(i128(0x234, 0x1234561), i128(0, 64)); + TEST_ASSERTF(addres.high == 0 && addres.low == 0x234, "ashr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + addres = i128_ashr(i128(0xF000000000000234, 0x1234561), i128(0, 64)); + TEST_ASSERTF(addres.high == UINT64_MAX && addres.low == 0xF000000000000234, "ashr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + printf("-- i128 Ashr ok.\n"); + + TEST_ASSERT(i128_ucomp(i128_udiv(i128_from_str("123"), i128_from_str("123")), i128_from_str("1")) == CMP_EQ, "Div failed"); + TEST_ASSERT(i128_ucomp(i128_udiv(i128_from_str("123"), i128_from_str("124")), i128_from_str("0")) == CMP_EQ, "Div failed"); + TEST_ASSERT(i128_ucomp(i128_udiv(i128_from_str("245"), i128_from_str("123")), i128_from_str("1")) == CMP_EQ, "Div failed"); + addres = i128_udiv(i128(0x12345, UINT64_MAX), i128(1, 0)); + TEST_ASSERT(addres.low == 0x12345 && addres.high == 0, "Div failed"); + addres = i128_sdiv(i128(0x12345, UINT64_MAX), i128(1, 0)); + TEST_ASSERT(addres.low == 0x12345 && addres.high == 0, "Div failed"); + addres = i128_udiv(i128(UINT64_MAX, 0), i128(1, 0)); + TEST_ASSERT(addres.low == UINT64_MAX && addres.high == 0, "Div failed"); + addres = i128_sdiv(i128(UINT64_MAX - 1, UINT64_MAX - 1), i128(1, 0)); + TEST_ASSERTF(addres.low == UINT64_MAX && addres.high == UINT64_MAX, "Div failed %s", i128_to_string(addres, 10, true)); + addres = i128_sdiv(i128(2, 0), i128(UINT64_MAX - 1, UINT64_MAX - 1)); + printf("-- i128 Div okfefe %x.\n", (unsigned)-2); + TEST_ASSERTF(addres.low == UINT64_MAX && addres.high == UINT64_MAX, "Div failed: %s %llx, %llx", i128_to_string(addres, 10, true), (unsigned long long)addres.high, (unsigned long long)addres.low); + printf("-- i128 Div ok.\n"); + + + } void compiler_tests(void) { symtab_init(0x100000); test_file(); + test128(); run_arena_allocator_tests(); exit(0); diff --git a/src/utils/errors.h b/src/utils/errors.h index 595f89e39..4c8ddfe3c 100644 --- a/src/utils/errors.h +++ b/src/utils/errors.h @@ -43,7 +43,8 @@ #define TODO FATAL_ERROR("TODO reached"); -#define TEST_ASSERT(_condition, _string, ...) while (!(_condition)) { FATAL_ERROR(_string, ##__VA_ARGS__); } +#define TEST_ASSERT(condition_, string_) while (!(condition_)) { FATAL_ERROR(string_); } +#define TEST_ASSERTF(condition_, string_, ...) while (!(condition_)) { char* str_; asprintf(&str_, string_, __VA_ARGS__); FATAL_ERROR(str_); } #define EXPECT(_string, _value, _expected) \ do { long long __tempval1 = _value; long long __tempval2 = _expected; \ diff --git a/src/utils/lib.h b/src/utils/lib.h index 44ff6b8e8..6b14b2ed2 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -250,6 +250,17 @@ static inline bool is_hex(char c) } } +static inline int hex_nibble(char c) +{ + static int conv[256] = { + ['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4, + ['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9, + ['A'] = 10, ['B'] = 11, ['C'] = 12, ['D'] = 13, ['E'] = 14, ['F'] = 15, + ['a'] = 10, ['b'] = 11, ['c'] = 12, ['d'] = 13, ['e'] = 14, ['f'] = 15, + }; + return conv[(unsigned char)c]; +} + static inline bool is_whitespace(char c) { switch (c) diff --git a/src/version.h b/src/version.h index 18dea7d39..4ebc7f619 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "A238" \ No newline at end of file +#define COMPILER_VERSION "A239" \ No newline at end of file diff --git a/test/src/tester.py b/test/src/tester.py index 45cba6be9..a8b73d403 100644 --- a/test/src/tester.py +++ b/test/src/tester.py @@ -294,6 +294,7 @@ def main(): else: print("Error: Invalid path to tests: " + filepath) usage() - print("Found %d tests: %d / %d passed (%d skipped)." % (conf.numtests, conf.numsuccess, conf.numtests - conf.numskipped, conf.numskipped)) + + print("Found %d tests: %.1f%% (%d / %d) passed (%d skipped)." % (conf.numtests, 100 * conf.numsuccess / max(1, conf.numtests - conf.numskipped), conf.numsuccess, conf.numtests - conf.numskipped, conf.numskipped)) main() diff --git a/test/test_suite/attributes/repeat_of_attributes.c3 b/test/test_suite/attributes/repeat_of_attributes.c3 new file mode 100644 index 000000000..2e5f323d0 --- /dev/null +++ b/test/test_suite/attributes/repeat_of_attributes.c3 @@ -0,0 +1,6 @@ +func void test() +{ + for (int a @align(16) @align(16) = 0 ; a < 10; a++) // #error: Repeat of attribute 'align' + { + } +} diff --git a/test/test_suite/compile_time/cttype_reassign.c3t b/test/test_suite/compile_time/cttype_reassign.c3t new file mode 100644 index 000000000..7c53b05e0 --- /dev/null +++ b/test/test_suite/compile_time/cttype_reassign.c3t @@ -0,0 +1,15 @@ +// #target: x64-darwin +module reassign; + +func void test() +{ + var $Foo = double; + $Foo = int; + $Foo hello; +} + +// #expect: reassign.ll + + %hello = alloca i32, align 4 + store i32 0, i32* %hello, align 4 + ret void \ No newline at end of file diff --git a/test/test_suite/compile_time/not_yet_initialized.c3 b/test/test_suite/compile_time/not_yet_initialized.c3 new file mode 100644 index 000000000..08659e684 --- /dev/null +++ b/test/test_suite/compile_time/not_yet_initialized.c3 @@ -0,0 +1,17 @@ +macro foo($Foo) +{ + $Foo a; + return a; +} + +func void test1() +{ + var $Bar; + @foo($Bar); // #error: '$Bar' is not defined yet +} + +func void test2() +{ + var $Bar; + $Bar z; // #error: '$Bar' is not defined yet +} diff --git a/test/test_suite/compile_time/typeof_from_literal.c3 b/test/test_suite/compile_time/typeof_from_literal.c3 index 52d74c819..6043e7ff7 100644 --- a/test/test_suite/compile_time/typeof_from_literal.c3 +++ b/test/test_suite/compile_time/typeof_from_literal.c3 @@ -2,13 +2,13 @@ module foo; func void a() { - $typeof(9146744073709551615) ef; + $typeof(9146744073709551615i64) ef; int fffx = ef; // #error: 'long' to 'int' } func void b() { - $typeof(9223372036854775809) ef; + $typeof(9223372036854775809u64) ef; int fffx = ef; // #error: 'ulong' to 'int' } diff --git a/test/test_suite/enumerations/simple_inference.c3t b/test/test_suite/enumerations/simple_inference.c3t new file mode 100644 index 000000000..bb0bbf0c9 --- /dev/null +++ b/test/test_suite/enumerations/simple_inference.c3t @@ -0,0 +1,23 @@ +// #target: x64-darwin + +enum HelloEnum +{ + HELLO, + WORLD, + ELLOWORLD, +} +func void test() +{ + HelloEnum h = WORLD; + h = ELLOWORLD; +} + +/* #expect: simple_inference.ll + +define void @simple_inference.test() #0 { +entry: + %h = alloca i32, align 4 + store i32 1, i32* %h, align 4 + store i32 2, i32* %h, align 4 + ret void +} \ No newline at end of file diff --git a/test/test_suite/errors/bitshift_failable.c3 b/test/test_suite/errors/bitshift_failable.c3 new file mode 100644 index 000000000..8019d2a60 --- /dev/null +++ b/test/test_suite/errors/bitshift_failable.c3 @@ -0,0 +1,5 @@ +func void test() +{ + int! x = 0; + int! z = x << 100; // #error: shift exceeds bitsize of 'int' +} \ No newline at end of file diff --git a/test/test_suite/errors/failable_taddr_and_access.c3t b/test/test_suite/errors/failable_taddr_and_access.c3t new file mode 100644 index 000000000..6f00199d8 --- /dev/null +++ b/test/test_suite/errors/failable_taddr_and_access.c3t @@ -0,0 +1,124 @@ +// #target: x64-darwin + +module test; +struct Foo +{ + int x, y; +} + +errtype MyErr +{ + FOO +} + +extern func int printf(char *c, ...); + +func void main() +{ + int! z = 2; + Foo*! w = &&Foo{ z, 0 }; + printf("%d\n", w.x); + z = MyErr.FOO!; + w = &&Foo{ z, 0 }; + printf("Not visible: %d\n", w.x); +} + +/* #expect: test.ll + +%Foo = type { i32, i32 } + +@Foo = linkonce_odr constant i8 1 +@"test.MyErr$elements" = linkonce_odr constant [1 x i8*] zeroinitializer +@MyErr = linkonce_odr constant i8 1 +@.str = private constant [4 x i8] c"%d\0A\00", align 1 +@.str.1 = private constant [17 x i8] c"Not visible: %d\0A\00", align 1 + +; Function Attrs: nounwind +declare i32 @printf(i8*, ...) #0 + +; Function Attrs: nounwind +define void @main() #0 { +entry: + %z = alloca i32, align 4 + %z.f = alloca i64, align 8 + %w = alloca %Foo*, align 8 + %w.f = alloca i64, align 8 + %literal = alloca %Foo, align 4 + %literal5 = alloca %Foo, align 4 + store i64 0, i64* %z.f, align 8 + store i32 2, i32* %z, align 4 + store i64 0, i64* %z.f, align 8 + %0 = getelementptr inbounds %Foo, %Foo* %literal, i32 0, i32 0 + %1 = load i64, i64* %z.f, align 8 + %not_err = icmp eq i64 %1, 0 + br i1 %not_err, label %after_check, label %error + +error: ; preds = %entry + store i64 %1, i64* %w.f, align 8 + br label %after_assign + +after_check: ; preds = %entry + %2 = load i32, i32* %z, align 4 + store i32 %2, i32* %0, align 4 + %3 = getelementptr inbounds %Foo, %Foo* %literal, i32 0, i32 1 + store i32 0, i32* %3, align 4 + store %Foo* %literal, %Foo** %w, align 8 + store i64 0, i64* %w.f, align 8 + br label %after_assign + +after_assign: ; preds = %after_check, %error + %4 = load i64, i64* %w.f, align 8 + %not_err1 = icmp eq i64 %4, 0 + br i1 %not_err1, label %after_check2, label %voiderr + +after_check2: ; preds = %after_assign + %5 = load %Foo*, %Foo** %w, align 8 + %6 = getelementptr inbounds %Foo, %Foo* %5, i32 0, i32 0 + %7 = load i32, i32* %6, align 8 + %8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %7) + br label %voiderr + +voiderr: ; preds = %after_check2, %after_assign + store i64 ptrtoint ([1 x i8*]* @"test.MyErr$elements" to i64), i64* %z.f, align 8 + br label %after_assign3 + +after_assign3: ; preds = %voiderr + br label %voiderr4 + +voiderr4: ; preds = %after_assign3 + %9 = getelementptr inbounds %Foo, %Foo* %literal5, i32 0, i32 0 + %10 = load i64, i64* %z.f, align 8 + %not_err6 = icmp eq i64 %10, 0 + br i1 %not_err6, label %after_check8, label %error7 + +error7: ; preds = %voiderr4 + store i64 %10, i64* %w.f, align 8 + br label %after_assign9 + +after_check8: ; preds = %voiderr4 + %11 = load i32, i32* %z, align 4 + store i32 %11, i32* %9, align 4 + %12 = getelementptr inbounds %Foo, %Foo* %literal5, i32 0, i32 1 + store i32 0, i32* %12, align 4 + store %Foo* %literal5, %Foo** %w, align 8 + store i64 0, i64* %w.f, align 8 + br label %after_assign9 + +after_assign9: ; preds = %after_check8, %error7 + br label %voiderr10 + +voiderr10: ; preds = %after_assign9 + %13 = load i64, i64* %w.f, align 8 + %not_err11 = icmp eq i64 %13, 0 + br i1 %not_err11, label %after_check12, label %voiderr13 + +after_check12: ; preds = %voiderr10 + %14 = load %Foo*, %Foo** %w, align 8 + %15 = getelementptr inbounds %Foo, %Foo* %14, i32 0, i32 0 + %16 = load i32, i32* %15, align 8 + %17 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str.1, i32 0, i32 0), i32 %16) + br label %voiderr13 + +voiderr13: ; preds = %after_check12, %voiderr10 + ret void +} \ No newline at end of file diff --git a/test/test_suite/errors/failable_untyped_list.c3 b/test/test_suite/errors/failable_untyped_list.c3 new file mode 100644 index 000000000..b8d061007 --- /dev/null +++ b/test/test_suite/errors/failable_untyped_list.c3 @@ -0,0 +1,24 @@ +module test; +struct Foo +{ + int x, y; +} + +errtype MyErr +{ + FOO +} + +extern func int printf(char *c, ...); + +func void main() +{ + int! z = 2; + Foo*! w = &&{ z, 0 }; // #error: casting 'int[2]*' to 'Foo*' is not permitted +} + +func void test() +{ + int! z = 2; + Foo*! w = &&Foo!{ z, 0 }; // #error: please remove the '!' +} \ No newline at end of file diff --git a/test/test_suite/errors/illegal_use_of_failable.c3 b/test/test_suite/errors/illegal_use_of_failable.c3 index 586692caa..202389bf3 100644 --- a/test/test_suite/errors/illegal_use_of_failable.c3 +++ b/test/test_suite/errors/illegal_use_of_failable.c3 @@ -1,12 +1,12 @@ func void syntaxErrors() { int! i = 0; - while (i + 1) {} // #error: 'int!' cannot be converted into 'bool' - if (i + 1) {} // #error: 'int!' cannot be converted into 'bool' - for (int x = i;;) {} // #error: 'int!' cannot be converted into 'int'. - for (int x = 0; x < i + 1;) {} // #error: 'bool!' cannot be converted into 'bool' - for (int x = 0; x < 10; x += i + 1) {} // #error: 'int!' cannot be converted into 'int' - switch (i + 1) // #error: 'int!' cannot be converted into 'int' + while (i + 1) {} // #error: 'int!' cannot be converted to 'bool' + if (i + 1) {} // #error: 'int!' cannot be converted to 'bool' + for (int x = i;;) {} // #error: 'int!' cannot be converted to 'int'. + for (int x = 0; x < i + 1;) {} // #error: 'bool!' cannot be converted to 'bool' + for (int x = 0; x < 10; x += i + 1) {} // #error: 'int!' cannot be converted to 'int' + switch (i + 1) // #error: 'int!' cannot be converted to 'int' { default: i + 1; diff --git a/test/test_suite/errors/try_catch_if.c3t b/test/test_suite/errors/try_catch_if.c3t index 3d18e1251..3b6bbf65a 100644 --- a/test/test_suite/errors/try_catch_if.c3t +++ b/test/test_suite/errors/try_catch_if.c3t @@ -50,7 +50,6 @@ entry: %a.f = alloca i64, align 8 %err = alloca i64, align 8 %retparam = alloca i32, align 4 - store i64 0, i64* %a.f, align 8 store i32 123, i32* %a, align 4 store i64 0, i64* %a.f, align 8 br label %testblock diff --git a/test/test_suite/errors/try_with_chained_unwrap_errors.c3 b/test/test_suite/errors/try_with_chained_unwrap_errors.c3 index 56481ff9d..9e08c6718 100644 --- a/test/test_suite/errors/try_with_chained_unwrap_errors.c3 +++ b/test/test_suite/errors/try_with_chained_unwrap_errors.c3 @@ -88,6 +88,6 @@ func void test10() } else { - int g = a; // #error: 'int!' cannot be converted into 'int' + int g = a; // #error: 'int!' cannot be converted to 'int' } } \ No newline at end of file diff --git a/test/test_suite/expressions/addr_compiles.c3t b/test/test_suite/expressions/addr_compiles.c3t new file mode 100644 index 000000000..67ed4c528 --- /dev/null +++ b/test/test_suite/expressions/addr_compiles.c3t @@ -0,0 +1,145 @@ +// #target: x64-darwin + +module test; + +extern func void printf(char*, ...); + +func void main() +{ + test(); + test2(); + test3(); +} + +func void test() +{ + int f = 3; + int* x = &(((f))); + int* h = &&(f++); + printf("x = %d (4), h = %d (3)\n", *x, *h); +} + + +const int XX = 314; +func void test2() +{ + int* w = &XX; + printf("w = %d (314)\n", *w); +} + +struct Foo +{ + int x; + int y; +} +func void test3() +{ + Foo h = { 345, 555 }; + int* zx = &h.x; + int* zy = &h.y; + int[3] arr = { 333, 444, 999 }; + printf("zx = %d (345) zy = %d (555)\n", *zx, *zy); + arr[0]--; + arr[1]--; + arr[2]--; + int* d = &arr[2]; + int[]* e = &&arr[1..2]; + printf("d = %d (998) e = %d (443)\n", *d, (*e)[0]); +} + +/* #expect: test.ll + +define void @main() #0 { +entry: + call void @test.test() + call void @test.test2() + call void @test.test3() + ret void +} + +; Function Attrs: nounwind +define void @test.test() #0 { +entry: + %f = alloca i32, align 4 + %x = alloca i32*, align 8 + %h = alloca i32*, align 8 + %taddr = alloca i32, align 4 + store i32 3, i32* %f, align 4 + store i32* %f, i32** %x, align 8 + %0 = load i32, i32* %f, align 4 + %add = add i32 %0, 1 + store i32 %add, i32* %f, align 4 + store i32 %0, i32* %taddr, align 4 + store i32* %taddr, i32** %h, align 8 + %1 = load i32*, i32** %x, align 8 + %2 = load i32, i32* %1, align 8 + %3 = load i32*, i32** %h, align 8 + %4 = load i32, i32* %3, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([24 x i8], [24 x i8]* @.str, i32 0, i32 0), i32 %2, i32 %4) + ret void +} + +; Function Attrs: nounwind +define void @test.test2() #0 { +entry: + %w = alloca i32*, align 8 + store i32* @test.XX, i32** %w, align 8 + %0 = load i32*, i32** %w, align 8 + %1 = load i32, i32* %0, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.1, i32 0, i32 0), i32 %1) + ret void +} + +; Function Attrs: nounwind +define void @test.test3() #0 { +entry: + %h = alloca %Foo, align 4 + %zx = alloca i32*, align 8 + %zy = alloca i32*, align 8 + %arr = alloca [3 x i32], align 4 + %d = alloca i32*, align 8 + %e = alloca %"int[]"*, align 8 + %taddr = alloca %"int[]", align 8 + %0 = bitcast %Foo* %h to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast (%Foo* @.__const to i8*), i32 8, i1 false) + %1 = getelementptr inbounds %Foo, %Foo* %h, i32 0, i32 0 + store i32* %1, i32** %zx, align 8 + %2 = getelementptr inbounds %Foo, %Foo* %h, i32 0, i32 1 + store i32* %2, i32** %zy, align 8 + %3 = bitcast [3 x i32]* %arr to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %3, i8* align 4 bitcast ([3 x i32]* @.__const.2 to i8*), i32 12, i1 false) + %4 = load i32*, i32** %zx, align 8 + %5 = load i32, i32* %4, align 8 + %6 = load i32*, i32** %zy, align 8 + %7 = load i32, i32* %6, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([29 x i8], [29 x i8]* @.str.3, i32 0, i32 0), i32 %5, i32 %7) + %arridx = getelementptr inbounds [3 x i32], [3 x i32]* %arr, i64 0, i64 0 + %8 = load i32, i32* %arridx, align 4 + %sub = sub i32 %8, 1 + store i32 %sub, i32* %arridx, align 4 + %arridx1 = getelementptr inbounds [3 x i32], [3 x i32]* %arr, i64 0, i64 1 + %9 = load i32, i32* %arridx1, align 4 + %sub2 = sub i32 %9, 1 + store i32 %sub2, i32* %arridx1, align 4 + %arridx3 = getelementptr inbounds [3 x i32], [3 x i32]* %arr, i64 0, i64 2 + %10 = load i32, i32* %arridx3, align 4 + %sub4 = sub i32 %10, 1 + store i32 %sub4, i32* %arridx3, align 4 + %arridx5 = getelementptr inbounds [3 x i32], [3 x i32]* %arr, i64 0, i64 2 + store i32* %arridx5, i32** %d, align 8 + %11 = bitcast [3 x i32]* %arr to i32* + %offset = getelementptr inbounds i32, i32* %11, i64 1 + %12 = insertvalue %"int[]" undef, i32* %offset, 0 + %13 = insertvalue %"int[]" %12, i64 2, 1 + store %"int[]" %13, %"int[]"* %taddr, align 8 + store %"int[]"* %taddr, %"int[]"** %e, align 8 + %14 = load i32*, i32** %d, align 8 + %15 = load i32, i32* %14, align 8 + %16 = load %"int[]"*, %"int[]"** %e, align 8 + %subarrayptr = getelementptr inbounds %"int[]", %"int[]"* %16, i32 0, i32 0 + %saptr = load i32*, i32** %subarrayptr, align 8 + %sarridx = getelementptr inbounds i32, i32* %saptr, i64 0 + %17 = load i32, i32* %sarridx, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* @.str.4, i32 0, i32 0), i32 %15, i32 %17) + ret void +} \ No newline at end of file diff --git a/test/test_suite/expressions/addr_of_fails.c3 b/test/test_suite/expressions/addr_of_fails.c3 new file mode 100644 index 000000000..855c78208 --- /dev/null +++ b/test/test_suite/expressions/addr_of_fails.c3 @@ -0,0 +1,78 @@ +func void test() +{ + int f; + int* x = &(((f))); + int* h = &&(f++); + int* z = &(f++); // #error: To take the address of a temporary value, use '&&' instead of '&' +} + + +func void test2() +{ + int f; + var $foo; + int* x = &$foo; // #error: It's not possible to take the address of a compile time value +} + +int he; +macro int hello() +{ + return he; +} + +func void test3() +{ + int* x = &@hello(); // #error: To take the address of a temporary value, use '&&' instead of '&' +} + +func void test3b() +{ + int* x = &hello; // #error: It is not possible to take the address of a macro. +} + +const X = 2; +const int XX = 3; +func void test4() +{ + int* w = &XX; +} + +func void test5() +{ + int* z = &X; // #error: The constant is not typed, either type it or use && to take the reference to a temporary. +} + +struct Foo +{ + int x; + int y; +} + +define heh = he; + +func void test6() +{ + int* hee = &heh; + Foo h; + int* z = &h.x; + int[3] arr; + int* d = &arr[2]; + int[]* e = &arr[1..2]; // #error: To take the address of a temporary value, use '&&' instead of '&' +} + +define Baz = Foo; +define Bar = distinct int; +errtype Err { FOO } +union Un { int x; } +enum MyEnum { BAR } + +func void test7() +{ + &Baz; // #error: It is not possible to take the address of a type. + &Bar; // #error: It is not possible to take the address of a type. + &Err; // #error: It is not possible to take the address of a type. + &Un; // #error: It is not possible to take the address of a type. + &Err.FOO; // #error: To take the address of a temporary value, use '&&' instead of '&' + &MyEnum; // #error: It is not possible to take the address of a type. + &MyEnum.BAR; // #error: To take the address of a temporary value, use '&&' instead of '&' +} diff --git a/test/test_suite/expressions/casts/cast_to_nonscalar.c3 b/test/test_suite/expressions/casts/cast_to_nonscalar.c3 index 619409fca..87bb7209e 100644 --- a/test/test_suite/expressions/casts/cast_to_nonscalar.c3 +++ b/test/test_suite/expressions/casts/cast_to_nonscalar.c3 @@ -5,5 +5,5 @@ struct Struct func void test1() { - int a = (Struct)(200); // #error: Cannot cast 'compint' to 'Struct' + int a = (Struct)(200); // #error: Cannot cast 'int' to 'Struct' } diff --git a/test/test_suite/expressions/negate_int.c3 b/test/test_suite/expressions/negate_int.c3 new file mode 100644 index 000000000..8139edda5 --- /dev/null +++ b/test/test_suite/expressions/negate_int.c3 @@ -0,0 +1,41 @@ +func void test1() +{ + short! a = 1; + try(-a); + short b = -a; // #error: 'int!' cannot be converted to 'short'. +} + +func void test2() +{ + int! a = 1; + try(-a); + int b = -a; // #error: 'int!' cannot be converted to 'int' +} + +func void test3() +{ + long! a = 1; + try(-a); + long b = -a; // #error: 'long!' cannot be converted to 'long' +} + +func void test4() +{ + short! a = 1; + try(~a); + short b = ~a; // #error: 'short!' cannot be converted to 'short' +} + +func void test5() +{ + int! a = 1; + try(~a); + int b = ~a; // #error: 'int!' cannot be converted to 'int' +} + +func void test6() +{ + long! a = 1; + try(~a); + long b = ~a; // #error: 'long!' cannot be converted to 'long' +} diff --git a/test/test_suite/expressions/pointer_arith.c3 b/test/test_suite/expressions/pointer_arith.c3 index 1c13fa504..e6406ba39 100644 --- a/test/test_suite/expressions/pointer_arith.c3 +++ b/test/test_suite/expressions/pointer_arith.c3 @@ -14,5 +14,5 @@ func void test1(ichar* cp) func void test2(ichar* cp) { cp + 1; - cp * 1.0; // #error: Cannot multiply 'ichar*' by 'compfloat' + cp * 1.0; // #error: 'ichar*' by 'double' } \ No newline at end of file diff --git a/test/test_suite/expressions/rvalues.c3 b/test/test_suite/expressions/rvalues.c3 new file mode 100644 index 000000000..05e4c96e5 --- /dev/null +++ b/test/test_suite/expressions/rvalues.c3 @@ -0,0 +1,16 @@ +macro void hello($bar) +{ + $bar; +} +const FOO = 1 + 2; +func void test() +{ + var $Foo = int; + var $Bar = $Foo; + $Bar x; + @hello(1); + var $foo = 1; + $foo; + FOO; +} + diff --git a/test/test_suite/failable_catch.c3t b/test/test_suite/failable_catch.c3t new file mode 100644 index 000000000..b7c843b48 --- /dev/null +++ b/test/test_suite/failable_catch.c3t @@ -0,0 +1,189 @@ +// #target: x64-darwin + +errtype MyErr +{ + TEST +} + +macro foo(int x) +{ + if (x) return x; + return MyErr.TEST!; +} + +extern func void printf(char*, ...); + +func void main() +{ + int! a = @foo(1); + + int! b = @foo((a + 3) ?? 2); + int! c = @foo(0); + printf("a = %d\n", a); + printf("b = %d\n", b); + printf("c = %d\n", c); + if (catch(c)) printf("c had error\n"); + c = 3; + printf("c = %d\n", c); +} + +// #expect: failable_catch.ll + +define void @main() #0 { +entry: + %a = alloca i32, align 4 + %a.f = alloca i64, align 8 + %x = alloca i32, align 4 + %blockret = alloca i32, align 4 + %b = alloca i32, align 4 + %b.f = alloca i64, align 8 + %x1 = alloca i32, align 4 + %blockret2 = alloca i32, align 4 + %c = alloca i32, align 4 + %c.f = alloca i64, align 8 + %x8 = alloca i32, align 4 + %blockret9 = alloca i32, align 4 + %error_var = alloca i64, align 8 + store i32 1, i32* %x, align 4 + %0 = load i32, i32* %x, align 4 + %intbool = icmp ne i32 %0, 0 + br i1 %intbool, label %if.then, label %if.exit + +if.then: ; preds = %entry + %1 = load i32, i32* %x, align 4 + store i32 %1, i32* %blockret, align 4 + br label %expr_block.exit + +if.exit: ; preds = %entry + store i64 ptrtoint ([1 x i8*]* @"failable_catch.MyErr$elements" to i64), i64* %a.f, align 8 + br label %after_assign + +expr_block.exit: ; preds = %if.then + %2 = load i32, i32* %blockret, align 4 + store i32 %2, i32* %a, align 4 + store i64 0, i64* %a.f, align 8 + br label %after_assign + +after_assign: ; preds = %expr_block.exit, %if.exit + %3 = load i64, i64* %a.f, align 8 + %not_err = icmp eq i64 %3, 0 + br i1 %not_err, label %after_check, label %else_block + +after_check: ; preds = %after_assign + %4 = load i32, i32* %a, align 4 + %add = add i32 %4, 3 + br label %phi_block + +else_block: ; preds = %after_assign + br label %phi_block + +phi_block: ; preds = %else_block, %after_check + %val = phi i32 [ %add, %after_check ], [ 2, %else_block ] + store i32 %val, i32* %x1, align 4 + %5 = load i32, i32* %x1, align 4 + %intbool3 = icmp ne i32 %5, 0 + br i1 %intbool3, label %if.then4, label %if.exit5 + +if.then4: ; preds = %phi_block + %6 = load i32, i32* %x1, align 4 + store i32 %6, i32* %blockret2, align 4 + br label %expr_block.exit6 + +if.exit5: ; preds = %phi_block + store i64 ptrtoint ([1 x i8*]* @"failable_catch.MyErr$elements" to i64), i64* %b.f, align 8 + br label %after_assign7 + +expr_block.exit6: ; preds = %if.then4 + %7 = load i32, i32* %blockret2, align 4 + store i32 %7, i32* %b, align 4 + store i64 0, i64* %b.f, align 8 + br label %after_assign7 + +after_assign7: ; preds = %expr_block.exit6, %if.exit5 + store i32 0, i32* %x8, align 4 + %8 = load i32, i32* %x8, align 4 + %intbool10 = icmp ne i32 %8, 0 + br i1 %intbool10, label %if.then11, label %if.exit12 + +if.then11: ; preds = %after_assign7 + %9 = load i32, i32* %x8, align 4 + store i32 %9, i32* %blockret9, align 4 + br label %expr_block.exit13 + +if.exit12: ; preds = %after_assign7 + store i64 ptrtoint ([1 x i8*]* @"failable_catch.MyErr$elements" to i64), i64* %c.f, align 8 + br label %after_assign14 + +expr_block.exit13: ; preds = %if.then11 + %10 = load i32, i32* %blockret9, align 4 + store i32 %10, i32* %c, align 4 + store i64 0, i64* %c.f, align 8 + br label %after_assign14 + +after_assign14: ; preds = %expr_block.exit13, %if.exit12 + %11 = load i64, i64* %a.f, align 8 + %not_err15 = icmp eq i64 %11, 0 + br i1 %not_err15, label %after_check16, label %voiderr + +after_check16: ; preds = %after_assign14 + %12 = load i32, i32* %a, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 %12) + br label %voiderr + +voiderr: ; preds = %after_check16, %after_assign14 + %13 = load i64, i64* %b.f, align 8 + %not_err17 = icmp eq i64 %13, 0 + br i1 %not_err17, label %after_check18, label %voiderr19 + +after_check18: ; preds = %voiderr + %14 = load i32, i32* %b, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.1, i32 0, i32 0), i32 %14) + br label %voiderr19 + +voiderr19: ; preds = %after_check18, %voiderr + %15 = load i64, i64* %c.f, align 8 + %not_err20 = icmp eq i64 %15, 0 + br i1 %not_err20, label %after_check21, label %voiderr22 + +after_check21: ; preds = %voiderr19 + %16 = load i32, i32* %c, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i32 0, i32 0), i32 %16) + br label %voiderr22 + +voiderr22: ; preds = %after_check21, %voiderr19 + store i64 0, i64* %error_var, align 8 + %17 = load i64, i64* %c.f, align 8 + %not_err23 = icmp eq i64 %17, 0 + br i1 %not_err23, label %after_check24, label %error + +error: ; preds = %voiderr22 + store i64 %17, i64* %error_var, align 8 + br label %noerr_block + +after_check24: ; preds = %voiderr22 + br label %noerr_block + +noerr_block: ; preds = %after_check24, %error + %18 = load i64, i64* %error_var, align 8 + %neq = icmp ne i64 %18, 0 + br i1 %neq, label %if.then25, label %if.exit26 + +if.then25: ; preds = %noerr_block + call void (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.3, i32 0, i32 0)) + br label %if.exit26 + +if.exit26: ; preds = %if.then25, %noerr_block + store i32 3, i32* %c, align 4 + store i64 0, i64* %c.f, align 8 + %19 = load i64, i64* %c.f, align 8 + %not_err27 = icmp eq i64 %19, 0 + br i1 %not_err27, label %after_check28, label %voiderr29 + +after_check28: ; preds = %if.exit26 + %20 = load i32, i32* %c, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.4, i32 0, i32 0), i32 %20) + br label %voiderr29 + +voiderr29: ; preds = %after_check28, %if.exit26 + ret void +} \ No newline at end of file diff --git a/test/test_suite/floats/explicit_float_truncation_needed.c3 b/test/test_suite/floats/explicit_float_truncation_needed.c3 index 6bd4792fe..374385ed3 100644 --- a/test/test_suite/floats/explicit_float_truncation_needed.c3 +++ b/test/test_suite/floats/explicit_float_truncation_needed.c3 @@ -1,4 +1,4 @@ func void test() { - float x = 0x7FFFFFFFFFFF.0p+400; // #error: The value '3.63419e+134' is out of range for 'float', so you need an explicit cast to truncate the value + float x = 0x7FFFFFFFFFFF.0p+400; // #error: The value '3.63419e+134' is out of range for 'float' } \ No newline at end of file diff --git a/test/test_suite/functions/test_regression.c3t b/test/test_suite/functions/test_regression.c3t index ec59fdc2c..eceab3902 100644 --- a/test/test_suite/functions/test_regression.c3t +++ b/test/test_suite/functions/test_regression.c3t @@ -140,7 +140,7 @@ func void main() } list.free(); - printf("Min %d Max %d Elements: %d\n", MyEnum.min, MyEnum.max, MyEnum.elements); + printf("Min %d Max %d Elements: %d\n", MyEnum.min, MyEnum.max, (int)(MyEnum.elements)); int max = MyEnum.max; int min = MyEnum.min; @@ -245,7 +245,7 @@ func Type getValue(Blob blob) return blob.a; } -// #expect: test.ll +/* #expect: test.ll %Blob = type { i32 } %Blob.0 = type { double } @@ -364,7 +364,7 @@ entry: store i32 0, i32* %sum, align 4 %len = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 1 %2 = load i64, i64* %len, align 8 - %eq = icmp eq i64 %2, 0 + %eq = icmp eq i64 0, %2 br i1 %eq, label %if.then, label %if.exit if.then: ; preds = %entry diff --git a/test/test_suite/functions/test_regression_mingw.c3t b/test/test_suite/functions/test_regression_mingw.c3t index 1a007b80f..46abfa5d2 100644 --- a/test/test_suite/functions/test_regression_mingw.c3t +++ b/test/test_suite/functions/test_regression_mingw.c3t @@ -140,7 +140,7 @@ func void main() } list.free(); - printf("Min %d Max %d Elements: %d\n", MyEnum.min, MyEnum.max, MyEnum.elements); + printf("Min %d Max %d Elements: %d\n", MyEnum.min, MyEnum.max, (int)(MyEnum.elements)); int max = MyEnum.max; int min = MyEnum.min; @@ -358,7 +358,7 @@ entry: store i32 0, i32* %sum, align 4 %len = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 1 %3 = load i64, i64* %len, align 8 - %eq = icmp eq i64 %3, 0 + %eq = icmp eq i64 0, %3 br i1 %eq, label %if.then, label %if.exit if.then: diff --git a/test/test_suite/globals/global_init.c3 b/test/test_suite/globals/global_init.c3 index b4144e45f..b258a909f 100644 --- a/test/test_suite/globals/global_init.c3 +++ b/test/test_suite/globals/global_init.c3 @@ -5,7 +5,7 @@ char[] str3 = "hello"; int[2] a1 = { 1, 2 }; -int[2] a2 = 30; // #error: 'compint' into 'int[2]' +int[2] a2 = 30; // #error: 'int' into 'int[2]' // TODO int[2] a3 = a1; ichar[*] a; // #error: Inferred array types can only be used in declarations with initializers diff --git a/test/test_suite/initializer_lists/fasta.c3t b/test/test_suite/initializer_lists/fasta.c3t index 99c3a0321..ec0bc00bf 100644 --- a/test/test_suite/initializer_lists/fasta.c3t +++ b/test/test_suite/initializer_lists/fasta.c3t @@ -1,9 +1,10 @@ +// #target: x64-darwin module fasta; -const IM = 139968; -const IA = 3877; -const IC = 29573; -const SEED = 42; +const IM = 139968u; +const IA = 3877u; +const IC = 29573u; +const SEED = 42u; uint seed = SEED; @@ -108,10 +109,10 @@ func void main(int argc, char **argv) %"char[]" = type { i8*, i64 } %"double[]" = type { double*, i64 } -@fasta.IM = constant i32 139968, align 1 -@fasta.IA = constant i32 3877, align 1 -@fasta.IC = constant i32 29573, align 1 -@fasta.SEED = constant i32 42, align 1 +@fasta.IM = constant i32 139968, align 4 +@fasta.IA = constant i32 3877, align 4 +@fasta.IC = constant i32 29573, align 4 +@fasta.SEED = constant i32 42, align 4 @fasta.seed = global i32 42, align 4 @.str = private constant [288 x i8] c"GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA\00", align 1 @fasta.alu = protected global %"char[]" { i8* getelementptr inbounds ([288 x i8], [288 x i8]* @.str, i32 0, i32 0), i64 287 }, align 8 @@ -123,7 +124,7 @@ func void main(int argc, char **argv) @fasta.homosapiens = global %"char[]" { i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.12, i32 0, i32 0), i64 4 }, align 8 @.taddr.13 = private hidden global [4 x double] [double 0x3FD3639D20BAEB5B, double 0x3FC957AE3DCD561B, double 0x3FC9493AEAB6C2BF, double 0x3FD34BEE4B030838], align 8 @fasta.homosapiens_p = global %"double[]" { double* getelementptr inbounds ([4 x double], [4 x double]* @.taddr.13, i32 0, i32 0), i64 4 }, align 8 -@fasta.LINELEN = constant i32 60, align 1 +@fasta.LINELEN = constant i32 60, align 4 @.str.14 = private constant [23 x i8] c">ONE Homo sapiens alu\0A\00", align 1 @.str.15 = private constant [26 x i8] c">TWO IUB ambiguity codes\0A\00", align 1 @.str.16 = private constant [31 x i8] c">THREE Homo sapiens frequency\0A\00", align 1 @@ -229,7 +230,7 @@ entry: %symb = alloca %"char[]", align 8 %probability = alloca %"double[]", align 8 %n = alloca i32, align 4 - %len = alloca i32, align 4 + %len5 = alloca i32, align 4 %i = alloca i32, align 4 %v = alloca double, align 8 %j = alloca i32, align 4 @@ -244,97 +245,103 @@ entry: %hi3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair1, i32 0, i32 1 store i64 %3, i64* %hi3, align 8 store i32 %4, i32* %n, align 4 + %len = getelementptr inbounds %"char[]", %"char[]"* %symb, i32 0, i32 1 + %5 = load i64, i64* %len, align 8 %len4 = getelementptr inbounds %"double[]", %"double[]"* %probability, i32 0, i32 1 - %5 = load i64, i64* %len4, align 8 - %uisitrunc = trunc i64 %5 to i32 - store i32 %uisitrunc, i32* %len, align 4 + %6 = load i64, i64* %len4, align 8 + %eq = icmp eq i64 %5, %6 + call void @llvm.assume(i1 %eq) + %len6 = getelementptr inbounds %"double[]", %"double[]"* %probability, i32 0, i32 1 + %7 = load i64, i64* %len6, align 8 + %uisitrunc = trunc i64 %7 to i32 + store i32 %uisitrunc, i32* %len5, align 4 store i32 0, i32* %i, align 4 br label %for.cond -for.cond: ; preds = %for.inc15, %entry - %6 = load i32, i32* %i, align 4 - %7 = load i32, i32* %n, align 4 - %lt = icmp slt i32 %6, %7 - br i1 %lt, label %for.body, label %for.exit17 +for.cond: ; preds = %for.inc18, %entry + %8 = load i32, i32* %i, align 4 + %9 = load i32, i32* %n, align 4 + %lt = icmp slt i32 %8, %9 + br i1 %lt, label %for.body, label %for.exit20 for.body: ; preds = %for.cond - %8 = call float @fasta.fasta_rand(float 1.000000e+00) - %fpfpext = fpext float %8 to double + %10 = call float @fasta.fasta_rand(float 1.000000e+00) + %fpfpext = fpext float %10 to double store double %fpfpext, double* %v, align 8 store i32 0, i32* %j, align 4 - br label %for.cond5 + br label %for.cond7 -for.cond5: ; preds = %for.inc, %for.body - %9 = load i32, i32* %j, align 4 - %10 = load i32, i32* %len, align 4 - %sub = sub i32 %10, 1 - %lt6 = icmp slt i32 %9, %sub - br i1 %lt6, label %for.body7, label %for.exit +for.cond7: ; preds = %for.inc, %for.body + %11 = load i32, i32* %j, align 4 + %12 = load i32, i32* %len5, align 4 + %sub = sub i32 %12, 1 + %lt8 = icmp slt i32 %11, %sub + br i1 %lt8, label %for.body9, label %for.exit -for.body7: ; preds = %for.cond5 - %11 = load double, double* %v, align 8 +for.body9: ; preds = %for.cond7 + %13 = load double, double* %v, align 8 %subarrayptr = getelementptr inbounds %"double[]", %"double[]"* %probability, i32 0, i32 0 %saptr = load double*, double** %subarrayptr, align 8 - %12 = load i32, i32* %j, align 4 - %sisiext = sext i32 %12 to i64 + %14 = load i32, i32* %j, align 4 + %sisiext = sext i32 %14 to i64 %sarridx = getelementptr inbounds double, double* %saptr, i64 %sisiext - %13 = load double, double* %sarridx, align 8 - %fsub = fsub double %11, %13 + %15 = load double, double* %sarridx, align 8 + %fsub = fsub double %13, %15 store double %fsub, double* %v, align 8 - %14 = load double, double* %v, align 8 - %lt8 = fcmp olt double %14, 0.000000e+00 - br i1 %lt8, label %if.then, label %if.exit + %16 = load double, double* %v, align 8 + %lt10 = fcmp olt double %16, 0.000000e+00 + br i1 %lt10, label %if.then, label %if.exit -if.then: ; preds = %for.body7 +if.then: ; preds = %for.body9 br label %for.exit -if.exit: ; preds = %for.body7 +if.exit: ; preds = %for.body9 br label %for.inc for.inc: ; preds = %if.exit - %15 = load i32, i32* %j, align 4 - %add = add i32 %15, 1 + %17 = load i32, i32* %j, align 4 + %add = add i32 %17, 1 store i32 %add, i32* %j, align 4 - br label %for.cond5 + br label %for.cond7 -for.exit: ; preds = %if.then, %for.cond5 - %subarrayptr9 = getelementptr inbounds %"char[]", %"char[]"* %symb, i32 0, i32 0 - %saptr10 = load i8*, i8** %subarrayptr9, align 8 - %16 = load i32, i32* %j, align 4 - %sisiext11 = sext i32 %16 to i64 - %sarridx12 = getelementptr inbounds i8, i8* %saptr10, i64 %sisiext11 - %17 = load i8, i8* %sarridx12, align 1 - %uisiext = zext i8 %17 to i32 +for.exit: ; preds = %if.then, %for.cond7 + %subarrayptr11 = getelementptr inbounds %"char[]", %"char[]"* %symb, i32 0, i32 0 + %saptr12 = load i8*, i8** %subarrayptr11, align 8 + %18 = load i32, i32* %j, align 4 + %sisiext13 = sext i32 %18 to i64 + %sarridx14 = getelementptr inbounds i8, i8* %saptr12, i64 %sisiext13 + %19 = load i8, i8* %sarridx14, align 1 + %uisiext = zext i8 %19 to i32 call void @putchar(i32 %uisiext) - %18 = load i32, i32* %i, align 4 - %smod = srem i32 %18, 60 - %eq = icmp eq i32 %smod, 59 - br i1 %eq, label %if.then13, label %if.exit14 + %20 = load i32, i32* %i, align 4 + %smod = srem i32 %20, 60 + %eq15 = icmp eq i32 %smod, 59 + br i1 %eq15, label %if.then16, label %if.exit17 -if.then13: ; preds = %for.exit +if.then16: ; preds = %for.exit call void @putchar(i32 10) - br label %if.exit14 + br label %if.exit17 -if.exit14: ; preds = %if.then13, %for.exit - br label %for.inc15 +if.exit17: ; preds = %if.then16, %for.exit + br label %for.inc18 -for.inc15: ; preds = %if.exit14 - %19 = load i32, i32* %i, align 4 - %add16 = add i32 %19, 1 - store i32 %add16, i32* %i, align 4 +for.inc18: ; preds = %if.exit17 + %21 = load i32, i32* %i, align 4 + %add19 = add i32 %21, 1 + store i32 %add19, i32* %i, align 4 br label %for.cond -for.exit17: ; preds = %for.cond - %20 = load i32, i32* %i, align 4 - %smod18 = srem i32 %20, 60 - %neq = icmp ne i32 %smod18, 0 - br i1 %neq, label %if.then19, label %if.exit20 +for.exit20: ; preds = %for.cond + %22 = load i32, i32* %i, align 4 + %smod21 = srem i32 %22, 60 + %neq = icmp ne i32 %smod21, 0 + br i1 %neq, label %if.then22, label %if.exit23 -if.then19: ; preds = %for.exit17 +if.then22: ; preds = %for.exit20 call void @putchar(i32 10) - br label %if.exit20 + br label %if.exit23 -if.exit20: ; preds = %if.then19, %for.exit17 +if.exit23: ; preds = %if.then22, %for.exit20 ret void } @@ -383,4 +390,4 @@ if.exit: ; preds = %if.then, %entry %mul10 = mul i32 %11, 5 call void @fasta.random_fasta(i8* %lo6, i64 %hi7, i8* %lo8, i64 %hi9, i32 %mul10) ret void -} +} \ No newline at end of file diff --git a/test/test_suite/initializer_lists/general_tests.c3t b/test/test_suite/initializer_lists/general_tests.c3t index 12a10b387..f3d443546 100644 --- a/test/test_suite/initializer_lists/general_tests.c3t +++ b/test/test_suite/initializer_lists/general_tests.c3t @@ -31,7 +31,7 @@ func int test() } -// #expect: general_tests.ll +/* #expect: general_tests.ll %Baz = type { double } %Bar = type { i32, i32 } @@ -52,15 +52,14 @@ entry: %a = alloca [0 x i32], align 4 %foo2 = alloca i32, align 4 %str = alloca i8*, align 8 - %literal = alloca [1 x i8*], align 8 %x = alloca i8, align 1 - %literal1 = alloca [3 x i32], align 4 + %literal = alloca [3 x i32], align 4 %b = alloca %Bar, align 4 %z = alloca %Baz, align 8 %sub = alloca %"int[]", align 8 - %literal2 = alloca [0 x i32], align 4 + %literal1 = alloca [0 x i32], align 4 %foo = alloca %"Bar[]", align 8 - %literal3 = alloca [0 x %Bar], align 4 + %literal2 = alloca [0 x %Bar], align 4 %baz = alloca [3 x %Baz], align 16 %0 = bitcast %Baz* %ffe to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %0, i8* align 8 bitcast ({ i32, [4 x i8] }* @.__const to i8*), i32 8, i1 false) @@ -69,40 +68,35 @@ entry: %2 = bitcast [0 x i32]* %a to i8* call void @llvm.memset.p0i8.i64(i8* align 4 %2, i8 0, i64 0, i1 false) store i32 33, i32* %foo2, align 4 - %3 = getelementptr inbounds i8*, [1 x i8*]* %literal, i32 0 - store i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i32 0, i32 0), i8** %3, align 8 - %arridx = getelementptr inbounds [1 x i8*], [1 x i8*]* %literal, i64 0, i64 0 - %4 = load i8*, i8** %arridx, align 8 - store i8* %4, i8** %str, align 8 - %5 = getelementptr inbounds i32, [3 x i32]* %literal1, i32 0 - store i32 1, i32* %5, align 4 - %6 = getelementptr inbounds i32, [3 x i32]* %literal1, i32 1 - store i32 2, i32* %6, align 4 - %7 = getelementptr inbounds i32, [3 x i32]* %literal1, i32 2 - store i32 3, i32* %7, align 4 - %8 = bitcast [3 x i32]* %literal1 to i32* - %9 = insertvalue %"int[]" undef, i32* %8, 0 - %10 = insertvalue %"int[]" %9, i64 3, 1 - %11 = extractvalue %"int[]" %10, 1 - %neq = icmp ne i64 %11, 0 - %not = xor i1 %neq, true - %12 = zext i1 %not to i8 - store i8 %12, i8* %x, align 1 - %13 = bitcast %Bar* %b to i8* - call void @llvm.memset.p0i8.i64(i8* align 4 %13, i8 0, i64 8, i1 false) - %14 = bitcast %Baz* %z to i8* - call void @llvm.memset.p0i8.i64(i8* align 8 %14, i8 0, i64 8, i1 false) - store [0 x i32] zeroinitializer, [0 x i32]* %literal2, align 4 - %15 = bitcast [0 x i32]* %literal2 to i32* - %16 = insertvalue %"int[]" undef, i32* %15, 0 - %17 = insertvalue %"int[]" %16, i64 0, 1 - store %"int[]" %17, %"int[]"* %sub, align 8 - store [0 x %Bar] zeroinitializer, [0 x %Bar]* %literal3, align 4 - %18 = bitcast [0 x %Bar]* %literal3 to %Bar* - %19 = insertvalue %"Bar[]" undef, %Bar* %18, 0 - %20 = insertvalue %"Bar[]" %19, i64 0, 1 - store %"Bar[]" %20, %"Bar[]"* %foo, align 8 - %21 = bitcast [3 x %Baz]* %baz to i8* - call void @llvm.memset.p0i8.i64(i8* align 16 %21, i8 0, i64 24, i1 false) + store i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i32 0, i32 0), i8** %str, align 8 + %3 = getelementptr inbounds i32, [3 x i32]* %literal, i32 0 + store i32 1, i32* %3, align 4 + %4 = getelementptr inbounds i32, [3 x i32]* %literal, i32 1 + store i32 2, i32* %4, align 4 + %5 = getelementptr inbounds i32, [3 x i32]* %literal, i32 2 + store i32 3, i32* %5, align 4 + %6 = bitcast [3 x i32]* %literal to i32* + %7 = insertvalue %"int[]" undef, i32* %6, 0 + %8 = insertvalue %"int[]" %7, i64 3, 1 + %len = extractvalue %"int[]" %8, 1 + %not = icmp eq i64 %len, 0 + %9 = zext i1 %not to i8 + store i8 %9, i8* %x, align 1 + %10 = bitcast %Bar* %b to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %10, i8 0, i64 8, i1 false) + %11 = bitcast %Baz* %z to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %11, i8 0, i64 8, i1 false) + store [0 x i32] zeroinitializer, [0 x i32]* %literal1, align 4 + %12 = bitcast [0 x i32]* %literal1 to i32* + %13 = insertvalue %"int[]" undef, i32* %12, 0 + %14 = insertvalue %"int[]" %13, i64 0, 1 + store %"int[]" %14, %"int[]"* %sub, align 8 + store [0 x %Bar] zeroinitializer, [0 x %Bar]* %literal2, align 4 + %15 = bitcast [0 x %Bar]* %literal2 to %Bar* + %16 = insertvalue %"Bar[]" undef, %Bar* %15, 0 + %17 = insertvalue %"Bar[]" %16, i64 0, 1 + store %"Bar[]" %17, %"Bar[]"* %foo, align 8 + %18 = bitcast [3 x %Baz]* %baz to i8* + call void @llvm.memset.p0i8.i64(i8* align 16 %18, i8 0, i64 24, i1 false) ret i32 1 -} +} \ No newline at end of file diff --git a/test/test_suite/initializer_lists/indexing_into_complist.c3 b/test/test_suite/initializer_lists/indexing_into_complist.c3 new file mode 100644 index 000000000..7254be9c3 --- /dev/null +++ b/test/test_suite/initializer_lists/indexing_into_complist.c3 @@ -0,0 +1,28 @@ +func void test() +{ + char* c = { 1, 3, "hello"}[2]; + int x; + int z = { 1, 3 }[x]; // #error: To subscript a compile time list a compile time integer index is needed +} + +func void test2() +{ + int z = { 1, 3 }[-1]; // #error: The index may not be negative +} + +func void test3() +{ + int z = { 1, 3 }[0xFFFF_FFFF_FFFF_FFFFu64]; // #error: The index is out of range. +} + +func void test4() +{ + int z = { 1, 3 }[2]; // #error: An index of '2' is out of range, a value between 0 and 1 was expected. + int z2 = { 1 }[2]; // #error: An index of '2' is out of range, a value of 0 was expected. +} + +func void test5() +{ + int z = { 1, 3 }[^4]; // #error: An index of '4' from the end is out of range, a value between 1 and 2 was expected + int z2 = { 1 }[^4]; // #error: An index of '4' from the end is out of range, a value of 1 was expected. +} diff --git a/test/test_suite/initializer_lists/subarrays.c3t b/test/test_suite/initializer_lists/subarrays.c3t index 6f7acfd37..0302ef0da 100644 --- a/test/test_suite/initializer_lists/subarrays.c3t +++ b/test/test_suite/initializer_lists/subarrays.c3t @@ -81,9 +81,9 @@ entry: %b = alloca %Bar, align 4 %z = alloca %Baz, align 8 %sub = alloca %"int[]", align 8 - %literal9 = alloca [0 x i32], align 4 + %literal10 = alloca [0 x i32], align 4 %foo = alloca %"Bar[]", align 8 - %literal10 = alloca [0 x %Bar], align 4 + %literal11 = alloca [0 x %Bar], align 4 %baz = alloca [3 x %Baz], align 16 %saptr = load %Bar*, %Bar** getelementptr inbounds (%"Bar[]", %"Bar[]"* @subarrays.arrbar, i32 0, i32 0), align 8 %sarridx = getelementptr inbounds %Bar, %Bar* %saptr, i64 1 @@ -145,36 +145,35 @@ entry: %30 = bitcast [3 x i32]* %literal7 to i32* %31 = insertvalue %"int[]" undef, i32* %30, 0 %32 = insertvalue %"int[]" %31, i64 3, 1 - %33 = extractvalue %"int[]" %32, 1 - %neq = icmp ne i64 %33, 0 - %not = xor i1 %neq, true - %34 = zext i1 %not to i8 - store i8 %34, i8* %xy, align 1 - %35 = load i8, i8* %xy, align 1 - %36 = trunc i8 %35 to i1 - %not8 = xor i1 %36, true - br i1 %not8, label %if.then, label %if.exit + %len8 = extractvalue %"int[]" %32, 1 + %not = icmp eq i64 %len8, 0 + %33 = zext i1 %not to i8 + store i8 %33, i8* %xy, align 1 + %34 = load i8, i8* %xy, align 1 + %35 = trunc i8 %34 to i1 + %not9 = xor i1 %35, true + br i1 %not9, label %if.then, label %if.exit if.then: ; preds = %entry - %37 = call i32 @"std::io.println"(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.9, i32 0, i32 0)) #3 + %36 = call i32 @"std::io.println"(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.9, i32 0, i32 0)) #3 br label %if.exit if.exit: ; preds = %if.then, %entry - %38 = bitcast %Bar* %b to i8* - call void @llvm.memset.p0i8.i64(i8* align 4 %38, i8 0, i64 8, i1 false) - %39 = bitcast %Baz* %z to i8* - call void @llvm.memset.p0i8.i64(i8* align 8 %39, i8 0, i64 8, i1 false) - store [0 x i32] zeroinitializer, [0 x i32]* %literal9, align 4 - %40 = bitcast [0 x i32]* %literal9 to i32* - %41 = insertvalue %"int[]" undef, i32* %40, 0 - %42 = insertvalue %"int[]" %41, i64 0, 1 - store %"int[]" %42, %"int[]"* %sub, align 8 - store [0 x %Bar] zeroinitializer, [0 x %Bar]* %literal10, align 4 - %43 = bitcast [0 x %Bar]* %literal10 to %Bar* - %44 = insertvalue %"Bar[]" undef, %Bar* %43, 0 - %45 = insertvalue %"Bar[]" %44, i64 0, 1 - store %"Bar[]" %45, %"Bar[]"* %foo, align 8 - %46 = bitcast [3 x %Baz]* %baz to i8* - call void @llvm.memset.p0i8.i64(i8* align 16 %46, i8 0, i64 24, i1 false) + %37 = bitcast %Bar* %b to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %37, i8 0, i64 8, i1 false) + %38 = bitcast %Baz* %z to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %38, i8 0, i64 8, i1 false) + store [0 x i32] zeroinitializer, [0 x i32]* %literal10, align 4 + %39 = bitcast [0 x i32]* %literal10 to i32* + %40 = insertvalue %"int[]" undef, i32* %39, 0 + %41 = insertvalue %"int[]" %40, i64 0, 1 + store %"int[]" %41, %"int[]"* %sub, align 8 + store [0 x %Bar] zeroinitializer, [0 x %Bar]* %literal11, align 4 + %42 = bitcast [0 x %Bar]* %literal11 to %Bar* + %43 = insertvalue %"Bar[]" undef, %Bar* %42, 0 + %44 = insertvalue %"Bar[]" %43, i64 0, 1 + store %"Bar[]" %44, %"Bar[]"* %foo, align 8 + %45 = bitcast [3 x %Baz]* %baz to i8* + call void @llvm.memset.p0i8.i64(i8* align 16 %45, i8 0, i64 24, i1 false) ret i32 1 } \ No newline at end of file diff --git a/test/test_suite/macros/macro_with_body.c3t b/test/test_suite/macros/macro_with_body.c3t index 47d85a506..c89d16ecc 100644 --- a/test/test_suite/macros/macro_with_body.c3t +++ b/test/test_suite/macros/macro_with_body.c3t @@ -43,7 +43,7 @@ func void main() } -// #expect: withbody.ll +/* #expect: withbody.ll define i32 @withbody.Foo__mutate(%Foo* %0) entry: diff --git a/test/test_suite/macros/type_params.c3t b/test/test_suite/macros/type_params.c3t new file mode 100644 index 000000000..4d108c124 --- /dev/null +++ b/test/test_suite/macros/type_params.c3t @@ -0,0 +1,66 @@ +// #target: x64-darwin +macro foo($Foo) +{ + $Foo a; + return a; +} + +define Bar = short; +func void test() +{ + int x = @foo(int); + var $Foo = double; + double d = @foo($Foo); + double d2 = @foo($typeof(d)); + short z = @foo(Bar); +} + + +/* #expect: type_params.ll + + %x = alloca i32, align 4 + %blockret = alloca i32, align 4 + %a = alloca i32, align 4 + %d = alloca double, align 8 + %blockret1 = alloca double, align 8 + %a2 = alloca double, align 8 + %d2 = alloca double, align 8 + %blockret4 = alloca double, align 8 + %a5 = alloca double, align 8 + %z = alloca i16, align 2 + %blockret7 = alloca i16, align 2 + %a8 = alloca i16, align 2 + store i32 0, i32* %a, align 4 + %0 = load i32, i32* %a, align 4 + store i32 %0, i32* %blockret, align 4 + br label %expr_block.exit + +expr_block.exit: ; preds = %entry + %1 = load i32, i32* %blockret, align 4 + store i32 %1, i32* %x, align 4 + store double 0.000000e+00, double* %a2, align 8 + %2 = load double, double* %a2, align 8 + store double %2, double* %blockret1, align 8 + br label %expr_block.exit3 + +expr_block.exit3: ; preds = %expr_block.exit + %3 = load double, double* %blockret1, align 8 + store double %3, double* %d, align 8 + store double 0.000000e+00, double* %a5, align 8 + %4 = load double, double* %a5, align 8 + store double %4, double* %blockret4, align 8 + br label %expr_block.exit6 + +expr_block.exit6: ; preds = %expr_block.exit3 + %5 = load double, double* %blockret4, align 8 + store double %5, double* %d2, align 8 + store i16 0, i16* %a8, align 2 + %6 = load i16, i16* %a8, align 2 + store i16 %6, i16* %blockret7, align 2 + br label %expr_block.exit9 + +expr_block.exit9: ; preds = %expr_block.exit6 + %7 = load i16, i16* %blockret7, align 2 + store i16 %7, i16* %z, align 2 + ret void +} diff --git a/test/test_suite/statements/comparison_widening.c3t b/test/test_suite/statements/comparison_widening.c3t index a83ab4a25..6a8ec9e4d 100644 --- a/test/test_suite/statements/comparison_widening.c3t +++ b/test/test_suite/statements/comparison_widening.c3t @@ -4,7 +4,7 @@ func void test1() { char a = 1; int b = 2; - char c = b > a ? 1 : 0; + char c = b > a ? 1u8 : 0u8; } // #expect: comparison_widening.ll diff --git a/test/test_suite/statements/foreach_with_error.c3 b/test/test_suite/statements/foreach_with_error.c3 new file mode 100644 index 000000000..6a68e8700 --- /dev/null +++ b/test/test_suite/statements/foreach_with_error.c3 @@ -0,0 +1,12 @@ +module test; + +func void test() +{ + int[3]! x; + int g; + foreach (z : x) // #error: It's not possible to enumerate an expression of type 'int[3]!' + { + g += z; + x[0] = 1; + } +} diff --git a/test/test_suite/statements/foreach_with_error.c3t b/test/test_suite/statements/foreach_with_error.c3t deleted file mode 100644 index d9872c55d..000000000 --- a/test/test_suite/statements/foreach_with_error.c3t +++ /dev/null @@ -1,66 +0,0 @@ -module test; - -func void test() -{ - int[3]! x; - int g; - foreach (z : x) - { - g += z; - x[0] = 1; - } -} - -// #expect: test.ll - -entry: - %x = alloca [3 x i32], align 4 - %x.f = alloca i64, align 8 - %g = alloca i32, align 4 - %idx = alloca i64, align 8 - %z = alloca i32, align 4 - store i64 0, i64* %x.f, align 8 - %0 = bitcast [3 x i32]* %x to i8* - call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 12, i1 false) - store i32 0, i32* %g, align 4 - %1 = load i64, i64* %x.f, align 8 - %not_err = icmp eq i64 %1, 0 - br i1 %not_err, label %after_check, label %foreach.exit - -after_check: - store i64 0, i64* %idx, align 8 - br label %foreach.cond - -foreach.cond: - %2 = load i64, i64* %idx, align 8 - %lt = icmp ult i64 %2, 3 - br i1 %lt, label %foreach.body, label %foreach.exit - -foreach.body: - %3 = getelementptr inbounds i32, [3 x i32]* %x, i64 %2 - %4 = load i32, i32* %3, align 4 - store i32 %4, i32* %z, align 4 - %5 = load i32, i32* %g, align 4 - %6 = load i32, i32* %z, align 4 - %add = add i32 %5, %6 - store i32 %add, i32* %g, align 4 - %7 = load i64, i64* %x.f, align 8 - %not_err1 = icmp eq i64 %7, 0 - br i1 %not_err1, label %after_check2, label %voiderr - -after_check2: - %arridx = getelementptr inbounds [3 x i32], [3 x i32]* %x, i64 0, i64 0 - store i32 1, i32* %arridx, align 4 - br label %voiderr - -voiderr: - br label %foreach.inc - -foreach.inc: - %8 = load i64, i64* %idx, align 8 - %9 = add i64 %8, 1 - store i64 %9, i64* %idx, align 8 - br label %foreach.cond - -foreach.exit: - ret void diff --git a/test/test_suite/statements/if_while_do_error.c3 b/test/test_suite/statements/if_while_do_error.c3 index 5e34c8d40..ee5f9437c 100644 --- a/test/test_suite/statements/if_while_do_error.c3 +++ b/test/test_suite/statements/if_while_do_error.c3 @@ -3,7 +3,7 @@ module test; func void test1() { bool! x = 0; - if (x) // #error: 'bool!' cannot be converted into 'bool' + if (x) // #error: 'bool!' cannot be converted to 'bool' { x = 100; } @@ -12,7 +12,7 @@ func void test1() func void test2() { bool! x = 0; - while (x) // #error: 'bool!' cannot be converted into 'bool' + while (x) // #error: 'bool!' cannot be converted to 'bool' { x = false; } @@ -30,5 +30,5 @@ func void test3() { x = !x; } - while (x); // #error: 'bool!' cannot be converted into 'bool' + while (x); // #error: 'bool!' cannot be converted to 'bool' } \ No newline at end of file diff --git a/test/test_suite/statements/various_switching.c3t b/test/test_suite/statements/various_switching.c3t index 7154affb7..d565ce487 100644 --- a/test/test_suite/statements/various_switching.c3t +++ b/test/test_suite/statements/various_switching.c3t @@ -148,7 +148,7 @@ switch.case10: ; preds = %next_if8 br label %switch.case13 next_if11: ; preds = %next_if8 - %eq12 = icmp eq i64 16, %4 + %eq12 = icmp eq i64 15, %4 br i1 %eq12, label %switch.case13, label %next_if14 switch.case13: ; preds = %next_if11, %switch.case10 diff --git a/test/test_suite/strings/multiline_strings.c3t b/test/test_suite/strings/multiline_strings.c3t index 2072248c1..525224853 100644 --- a/test/test_suite/strings/multiline_strings.c3t +++ b/test/test_suite/strings/multiline_strings.c3t @@ -1,3 +1,4 @@ +// #target: x64-darwin char *message0 = """ hello diff --git a/test/test_suite/subarrays/sub_array_init.c3 b/test/test_suite/subarrays/sub_array_init.c3 index 187338cd7..35e012b31 100644 --- a/test/test_suite/subarrays/sub_array_init.c3 +++ b/test/test_suite/subarrays/sub_array_init.c3 @@ -2,6 +2,6 @@ func void test2() { int[2] a = { 1, 2 }; - int[2] b = 30; // #error: 'compint' into 'int[2]' + int[2] b = 30; // #error: 'int' into 'int[2]' int[2] c = a; } diff --git a/test/test_suite/types/enum_overflow.c3 b/test/test_suite/types/enum_overflow.c3 index 4631356d9..ce13cbe10 100644 --- a/test/test_suite/types/enum_overflow.c3 +++ b/test/test_suite/types/enum_overflow.c3 @@ -1,4 +1,4 @@ enum EnumTestOverflow { - VALUE = 0x80000000, // #error: The value '2147483648' is out of range for 'int' + VALUE = 0x80000000i64, // #error: Cannot narrow 'long' to 'int' } \ No newline at end of file diff --git a/test/test_suite/union/empty_unions.c3 b/test/test_suite/union/empty_unions.c3 index d0b403b88..9ac3af435 100644 --- a/test/test_suite/union/empty_unions.c3 +++ b/test/test_suite/union/empty_unions.c3 @@ -24,7 +24,7 @@ func Xe foo(Xe a) { a.c = 123; a.a = 39249; - a.b = 12301230123123; + a.b = 12301230123123i64; a.z = 1; return a; } \ No newline at end of file diff --git a/wrapper/src/wrapper.cpp b/wrapper/src/wrapper.cpp index 9c48d6e2c..f7cbcaebf 100644 --- a/wrapper/src/wrapper.cpp +++ b/wrapper/src/wrapper.cpp @@ -106,4 +106,6 @@ bool llvm_link_mingw(const char **args, int arg_count, const char** error_string return llvm_link(MINGW, args, arg_count, error_string); } + + }