diff --git a/lib/std/atomic.c3 b/lib/std/atomic.c3 index 947359829..3a881adf4 100644 --- a/lib/std/atomic.c3 +++ b/lib/std/atomic.c3 @@ -272,7 +272,7 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) @require $defined(*ptr) : "Expected a pointer" @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." - @require $defined(*ptr * y) : "/ must be defined between the values." + @require $defined(*ptr / y) : "/ must be defined between the values." @require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." *> macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) diff --git a/lib/std/core/string_to_real.c3 b/lib/std/core/string_to_real.c3 index 73d27372b..d39e6a03a 100644 --- a/lib/std/core/string_to_real.c3 +++ b/lib/std/core/string_to_real.c3 @@ -158,7 +158,7 @@ macro double? decfloat(char[] chars, int $bits, int $emin, int sign) if (rp % 9) { long rpm9 = rp >= 0 ? rp % 9 : rp % 9 + 9; - int p10 = P10S[8 - rpm9]; + uint p10 = P10S[8 - rpm9]; uint carry = 0; for (k = a; k != z; k++) { diff --git a/lib/std/io/formatter_private.c3 b/lib/std/io/formatter_private.c3 index 164931b0e..1bf83d993 100644 --- a/lib/std/io/formatter_private.c3 +++ b/lib/std/io/formatter_private.c3 @@ -377,13 +377,13 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv j %= 9; int i; for (i = 10, j++; j < 9; i *= 10, j++); - x = *d % i; + x = *d % (uint)i; // Are there any significant digits past j? if (x || (d + 1) != z) { double round = 2 / math::DOUBLE_EPSILON; double small; - if (((*d / i) & 1) || (i == 1000000000 && d > a && (d[-1] & 1))) + if (((*d / (uint)i) & 1) || (i == 1000000000 && d > a && (d[-1] & 1))) { round += 2; } @@ -437,7 +437,7 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv // Count trailing zeros in last place if (z > a && z[-1]) { - for (int i = 10, j = 0; z[-1] % i == 0; i *= 10, j++); + for (int i = 10, j = 0; z[-1] % (uint)i == 0; i *= 10, j++); } else { diff --git a/releasenotes.md b/releasenotes.md index fff471612..3a8791e15 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -8,6 +8,7 @@ - Improve support for Android with Termux. - Integrated download of the MSVC SDK when compiling for Windows. - For `c3c init` with library templates, provide example exported functions. #2898 +- `unsigned % signed` and `unsigned / signed` is no longer allowed without explicit casts, except for const denominators. #2928 ### Stdlib changes - Summarize sort macros as generic function wrappers to reduce the amount of generated code. #2831 diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index bd9a352e7..4159ae97c 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1524,6 +1524,7 @@ static inline bool sema_binary_analyse_arithmetic_subexpr(SemaContext *context, if (flat_left->type_kind == TYPE_BITSTRUCT && left_type == right_type) return true; if (flat_left == type_bool && left_type == right_type) return true; } + // 2. Perform promotion to a common type. return sema_binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, error, bool_and_bitstruct_is_allowed, operator_overload_ref, failed_ref); @@ -8157,6 +8158,16 @@ static bool sema_expr_analyse_mult(SemaContext *context, Expr *expr, Expr *left, return true; } +static inline bool sema_convert_denominator_to_unsigned_if_needed(SemaContext *context, Expr *left, Expr *right) +{ + if (!type_is_unsigned(left->type->canonical) || !type_is_signed(right->type->canonical)) return true; + if (sema_cast_const(right) && expr_is_const_int(right) && !int_is_neg(right->const_expr.ixx)) + { + return cast_implicit(context, right, type_int_unsigned_by_bitsize(type_bit_size(right->type->canonical)), true); + } + return false; +} + /** * Analyse a / b * @return true if analysis completed ok. @@ -8165,6 +8176,11 @@ static bool sema_expr_analyse_div(SemaContext *context, Expr *expr, Expr *left, { // 1. Analyse sub expressions and promote to a common type OperatorOverload overload = OVERLOAD_DIVIDE; + if (!sema_convert_denominator_to_unsigned_if_needed(context, left, right)) + { + if (failed_ref) return *failed_ref = true, false; + RETURN_SEMA_ERROR(expr, "Cannot implicitly divide an unsigned integer by an non-const signed integer, please use explicit casts."); + } if (!sema_binary_analyse_arithmetic_subexpr(context, expr, "Cannot divide %s by %s.", false, &overload, failed_ref)) return false; if (!overload) return true; @@ -8221,6 +8237,12 @@ static bool sema_expr_analyse_mod(SemaContext *context, Expr *expr, Expr *left, { // 1. Analyse both sides and promote to a common type OperatorOverload overload = OVERLOAD_REMINDER; + if (!sema_convert_denominator_to_unsigned_if_needed(context, left, right)) + { + if (failed_ref) return *failed_ref = true, false; + RETURN_SEMA_ERROR(expr, "Cannot implicitly convert the values in a remainder operation with an unsigned nominator and non-const or negative signed denominator, please use explicit casts."); + } + if (!sema_binary_analyse_arithmetic_subexpr(context, expr, "Cannot calculate the remainder %s %% %s", false, &overload, failed_ref)) return false; if (!overload) return true; diff --git a/test/unit/stdlib/atomic.c3 b/test/unit/stdlib/atomic.c3 index b7ba45bb1..942e50db6 100644 --- a/test/unit/stdlib/atomic.c3 +++ b/test/unit/stdlib/atomic.c3 @@ -87,7 +87,7 @@ fn void div() @test { t.create(fn int(void* arg) { thread::sleep_ms(5); - atomic::fetch_div(&a, 8); + atomic::fetch_div(&a, 8U); return 0; }, null)!!; }