From 72cc8e430a4244d2fe79c359461d3f0cbe1ad7ed Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 4 Jun 2025 15:20:49 +0200 Subject: [PATCH] `-0xFF` will now be a signed integer. - `-2147483648`, MIN literals work correctly. --- lib/std/math/math_nolibc/__fmod.c3 | 8 +- releasenotes.md | 2 + src/compiler/parse_expr.c | 90 +++++++++++++++++------ test/test_suite/literals/literal_check.c3 | 23 ++++++ 4 files changed, 98 insertions(+), 25 deletions(-) create mode 100644 test/test_suite/literals/literal_check.c3 diff --git a/lib/std/math/math_nolibc/__fmod.c3 b/lib/std/math/math_nolibc/__fmod.c3 index 432bd0cf9..7e23e4c16 100644 --- a/lib/std/math/math_nolibc/__fmod.c3 +++ b/lib/std/math/math_nolibc/__fmod.c3 @@ -35,7 +35,7 @@ fn double fmod(double x, double y) @extern("fmod") @weak @nostrip } else { - uxi &= -1UL >> 12; + uxi &= (ulong)-1 >> 12; uxi |= 1UL << 52; } if (!ey) @@ -45,7 +45,7 @@ fn double fmod(double x, double y) @extern("fmod") @weak @nostrip } else { - uy.i &= -1UL >> 12; + uy.i &= (ulong)-1 >> 12; uy.i |= 1UL << 52; } @@ -105,7 +105,7 @@ fn float fmodf(float x, float y) @extern("fmodf") @weak @nostrip } else { - uxi &= -1U >> 9; + uxi &= (uint)-1 >> 9; uxi |= 1U << 23; } if (!ey) @@ -115,7 +115,7 @@ fn float fmodf(float x, float y) @extern("fmodf") @weak @nostrip } else { - uy.i &= -1U >> 9; + uy.i &= (ulong)-1 >> 9; uy.i |= 1U << 23; } diff --git a/releasenotes.md b/releasenotes.md index b63c2d09c..93dd4ece6 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -5,6 +5,7 @@ ### Changes / improvements - `$typefrom` now also accepts a constant string, and so works like `$evaltype`. - `$evaltype` is deprecated in favour of `$typefrom`. +- `-0xFF` will now be a signed integer. ### Fixes @@ -71,6 +72,7 @@ - Bug using `#foo` arguments with `$defined` #2173 - Incorrect ensure on String.split. - Removed the naive check for compile time modification, which fixes #1997 but regresses in detection. +- `-2147483648`, MIN literals work correctly. ### Stdlib changes - Added `String.quick_ztr` and `String.is_zstr` diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 852d599cd..ff76ea241 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -8,6 +8,7 @@ typedef Expr *(*ParseFn)(ParseContext *context, Expr *); static Expr *parse_subscript_expr(ParseContext *c, Expr *left); static Expr *parse_initializer_list(ParseContext *c, Expr *left); +static Expr *parse_integer_expr(ParseContext *c, bool negated); typedef struct { @@ -689,6 +690,14 @@ static Expr *parse_unary_expr(ParseContext *c, Expr *left) { ASSERT(!left && "Did not expect a left hand side!"); + if (tok_is(c, TOKEN_MINUS)) + { + if (peek(c) == TOKEN_INTEGER) + { + advance(c); + return parse_integer_expr(c, true); + } + } bool is_bangbang = tok_is(c, TOKEN_BANGBANG); Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY); if (c->tok == TOKEN_CT_AND) @@ -1394,10 +1403,10 @@ static int read_int_suffix(const char *string, size_t loc, size_t len, char c, b } } -Expr *parse_integer(ParseContext *c, Expr *left) +static Expr *parse_integer_expr(ParseContext *c, bool negated) { - ASSERT(!left && "Had left hand side"); Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST); + if (negated) expr_int->span = extend_span_with_token(c->prev_span, c->span); expr_int->resolve_status = RESOLVE_DONE; size_t len = c->data.lex_len; const char *string = symstr(c); @@ -1408,12 +1417,15 @@ Expr *parse_integer(ParseContext *c, Expr *left) int oct_characters = 0; int binary_characters = 0; bool wrapped = false; + bool set_unsigned = false; uint64_t max; bool bit_suffix = false; + unsigned radix = 10; switch (len > 2 ? (string[1] | 32) : '0') { case 'x': is_unsigned = true; + radix = 16; max = UINT64_MAX >> 4; for (size_t loc = 2; loc < len; loc++) { @@ -1423,6 +1435,7 @@ Expr *parse_integer(ParseContext *c, Expr *left) case 'u': type_bits = read_int_suffix(string, loc, len, ch, &bit_suffix); is_unsigned = true; + set_unsigned = true; goto EXIT; case 'l': case 'i': @@ -1442,6 +1455,7 @@ Expr *parse_integer(ParseContext *c, Expr *left) break; case 'o': is_unsigned = true; + radix = 8; max = UINT64_MAX >> 3; for (size_t loc = 2; loc < len; loc++) { @@ -1451,6 +1465,7 @@ Expr *parse_integer(ParseContext *c, Expr *left) case 'u': type_bits = read_int_suffix(string, loc, len, ch, &bit_suffix); is_unsigned = true; + set_unsigned = true; goto EXIT; case 'l': case 'i': @@ -1469,6 +1484,7 @@ Expr *parse_integer(ParseContext *c, Expr *left) } break; case 'b': + radix = 2; is_unsigned = true; max = UINT64_MAX >> 1; for (size_t loc = 2; loc < len; loc++) @@ -1479,6 +1495,7 @@ Expr *parse_integer(ParseContext *c, Expr *left) case 'u': type_bits = read_int_suffix(string, loc, len, ch, &bit_suffix); is_unsigned = true; + set_unsigned = true; goto EXIT; case 'l': case 'i': @@ -1505,6 +1522,7 @@ Expr *parse_integer(ParseContext *c, Expr *left) case 'u': type_bits = read_int_suffix(string, loc, len, ch, &bit_suffix); is_unsigned = true; + set_unsigned = true; goto EXIT; case 'l': case 'i': @@ -1526,9 +1544,25 @@ Expr *parse_integer(ParseContext *c, Expr *left) EXIT: if (wrapped) { - PRINT_ERROR_HERE("Integer size exceeded 128 bits, max 128 bits are supported."); + PRINT_ERROR_AT(expr_int, "Integer size exceeded 128 bits, max 128 bits are supported."); return poisoned_expr; } + if (negated) + { + if (set_unsigned) + { + PRINT_ERROR_AT(expr_int, "Negating an explicitly unsigned value is not allowed, but you can place the value inside of (), " + "like -(%s).", i128_to_string(i, radix, false, true)); + return poisoned_expr; + } + is_unsigned = false; + if (i128_comp(i, INT128_MIN, type_u128) == CMP_GT) + { + PRINT_ERROR_AT(expr_int, "The negated integer size would exeed an int128."); + return poisoned_expr; + } + if (negated) i = i128_neg(i); + } expr_int->const_expr.const_kind = CONST_INTEGER; expr_int->const_expr.is_character = false; expr_int->const_expr.is_hex = hex_characters > 0; @@ -1537,7 +1571,7 @@ EXIT: { if (type_bits < 0 || !is_power_of_two((uint64_t)type_bits) || type_bits > 128) { - PRINT_ERROR_HERE("Integer type suffix should be i8, i16, i32, i64 or i128."); + PRINT_ERROR_AT(expr_int, "Integer type suffix should be i8, i16, i32, i64 or i128."); return poisoned_expr; } const char *suffix; @@ -1586,7 +1620,7 @@ EXIT: type_bits = 4 * hex_characters; if (type_bits > 128) { - PRINT_ERROR_HERE("%d hex digits indicates a bit width over 128, which is not supported.", hex_characters); + PRINT_ERROR_AT(expr_int, "%d hex digits indicates a bit width over 128, which is not supported.", hex_characters); return poisoned_expr; } } @@ -1595,7 +1629,7 @@ EXIT: type_bits = 3 * oct_characters; if (type_bits > 128) { - PRINT_ERROR_HERE("%d octal digits indicates a bit width over 128, which is not supported.", oct_characters); + PRINT_ERROR_AT(expr_int, "%d octal digits indicates a bit width over 128, which is not supported.", oct_characters); return poisoned_expr; } } @@ -1604,11 +1638,11 @@ EXIT: type_bits = binary_characters; if (type_bits > 128) { - PRINT_ERROR_HERE("%d binary digits indicates a bit width over 128, which is not supported.", binary_characters); + PRINT_ERROR_AT(expr_int, "%d binary digits indicates a bit width over 128, which is not supported.", binary_characters); return poisoned_expr; } } - if (type_bits && type_bits < 8) type_bits = 8; + if (type_bits && type_bits < compiler.platform.width_c_int) type_bits = compiler.platform.width_c_int; if (type_bits && !is_power_of_two((uint64_t)type_bits)) type_bits = (int)next_highest_power_of_2((uint32_t)type_bits); } if (type_bits) expr_int->const_expr.is_hex = false; @@ -1619,14 +1653,13 @@ EXIT: } else { - BitSize min_bits = (BitSize)type_size(type_cint) * 8; - Int test = { .i = i }; + BitSize min_bits = compiler.platform.width_c_int; + Int test = { .i = i, .type = negated ? TYPE_I128 : TYPE_U128 }; for (int type_kind = 0; type_kind < 5; type_kind++) { TypeKind kind = (is_unsigned ? TYPE_U8 : TYPE_I8) + type_kind; int bitsize = type_kind_bitsize(kind); if (bitsize < min_bits) continue; - test.type = kind; if (int_fits(test, kind)) { type_base = is_unsigned ? type_int_unsigned_by_bitsize(bitsize) : type_int_signed_by_bitsize(bitsize); @@ -1635,30 +1668,45 @@ EXIT: } if (!type_base) type_base = is_unsigned ? type_cuint : type_cint; } - expr_int->const_expr.ixx = (Int) { i, type_base->type_kind }; - if (!int_fits(expr_int->const_expr.ixx, type_base->type_kind)) + Int result = { i, type_base->type_kind }; + if (!int_fits(result, type_base->type_kind)) { - unsigned radix = 10; - if (hex_characters) radix = 16; - if (oct_characters) radix = 8; - if (binary_characters) radix = 2; if (type_bits) { - PRINT_ERROR_HERE("'%s' does not fit in a '%c%d' literal.", - i128_to_string(i, radix, true, false), is_unsigned ? 'u' : 'i', type_bits); + PRINT_ERROR_AT(expr_int, "'%s' does not fit in a '%c%d' literal.", + i128_to_string(i, radix, true, false), is_unsigned ? 'u' : 'i', type_bits); } else { - PRINT_ERROR_HERE("'%s' does not fit in an %s literal.", - i128_to_string(i, radix, true, false), is_unsigned ? "unsigned int" : "int"); + if (is_unsigned) + { + PRINT_ERROR_AT(expr_int, "'%s' does not fit in an unsigned integer literal.", + i128_to_string(i, radix, false, false)); + } + else + { + if (negated) + { + PRINT_ERROR_AT(expr_int, "'%s' does not fit in a signed integer literal.", i128_to_string(i, radix, true, false)); + } + else + { + PRINT_ERROR_AT(expr_int, "'%s' does not fit in a signed integer literal, but you can try making it unsigned by appending the 'U' suffix.", i128_to_string(i, radix, false, false)); + } + } } return poisoned_expr; } + expr_int->const_expr.ixx = result; expr_int->type = type_base; advance(c); return expr_int; } +Expr *parse_integer(ParseContext *c, Expr *left) +{ + return parse_integer_expr(c, false); +} /** * Parse hex, skipping over invalid characters. * @param result_pointer ref to place to put the data diff --git a/test/test_suite/literals/literal_check.c3 b/test/test_suite/literals/literal_check.c3 new file mode 100644 index 000000000..ed3704d22 --- /dev/null +++ b/test/test_suite/literals/literal_check.c3 @@ -0,0 +1,23 @@ +fn void main() +{ + $assert(-0x80000000 < 0); + $assert(-0x80000000 != 0x80000000); + $assert(-0x8000000000000000 < 0); + $assert(-0x8000000000000000 != 0x8000000000000000); + $assert($sizeof(0x7fffffff) == 4); + $assert($sizeof(0x80000000) == 4); + $assert($sizeof(2147483648) == 8); + $assert(-0x1 < 0); + $assert(-01 < 0); + $assert(-0o1 < 0); + $assert(-0b1 < 0); + $assert(@typeid(-2147483648) == int.typeid); + $assert(@typeid(0x1) == uint.typeid); + $assert(@typeid(0x8000_0000_0000_0000) == ulong.typeid); + $assert(@typeid(-0x7FFF_0000_0000_0000) == long.typeid); + int128 x = (int128)-(0x8000_0000_0000_0000_0000_0000_0000_0000 - 1u); + assert(-170141183460469231731687303715884105727 == x); + $assert(@typeid(-170141183460469231731687303715884105728) == int128.typeid); + $assert(@typeid(170141183460469231731687303715884105727) == int128.typeid); + $assert(@typeid(170141183460469231731687303715884105728U) == uint128.typeid); +}