diff --git a/releasenotes.md b/releasenotes.md index fe86ef237..be0981473 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -13,6 +13,7 @@ - Improve error message when doing a rethrow in a function that doesn't return an optional. - Add `--list-asm` to view all supported `asm` instructions. - Formatting option "%h" now supports pointers. +- Improve error on unsigned implicit conversion to signed. ### Fixes - mkdir/rmdir would not work properly with substring paths on non-windows platforms. diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 850e2cc39..c2e1b829f 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -7134,6 +7134,22 @@ static bool sema_replace_with_overload(SemaContext *context, Expr *expr, Expr *l return true; } +static inline bool sema_check_untyped_promotion(SemaContext *context, Expr *expr, bool is_left, CanonicalType *max_flat, Type *max) +{ + Type *flat = type_flatten(expr->type); + if (!type_is_unsigned(flat) || type_size(max_flat) != type_size(flat)) return true; + if (sema_cast_const(expr) && expr_is_const_int(expr) && expr_const_will_overflow(&expr->const_expr, max_flat->type_kind)) + { + RETURN_SEMA_ERROR(expr, + "This expression (%s) will be implicitly converted to a signed type due to the %s-hand side being signed, but the value does not fit %s. " + "To fix this, either cast the value explicitly, or make the %s-hand side an unsigned type.", + expr_const_to_error_string(&expr->const_expr), is_left ? "right" : "left", type_quoted_error_string(max), + is_left ? "right" : "left"); + } + + return true; + +} static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, Expr *right, Type *left_type, Type *right_type, Expr *parent, const char *error_message, bool allow_bool_vec, OperatorOverload *operator_overload_ref, @@ -7155,6 +7171,12 @@ static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, E } RETURN_SEMA_ERROR(parent, error_message, type_quoted_error_string(left->type), type_quoted_error_string(right->type)); } + Type *flat_max = type_flatten(max); + if (type_is_signed(flat_max)) + { + if (!sema_check_untyped_promotion(context, left, true, flat_max, max)) return false; + if (!sema_check_untyped_promotion(context, right, false, flat_max, max)) return false; + } return cast_implicit_binary(context, left, max, failed_ref) && cast_implicit_binary(context, right, max, failed_ref); } diff --git a/test/test_suite/cast/out_of_range_unlong.c3 b/test/test_suite/cast/out_of_range_unlong.c3 new file mode 100644 index 000000000..b27a15aae --- /dev/null +++ b/test/test_suite/cast/out_of_range_unlong.c3 @@ -0,0 +1,6 @@ +fn int main() +{ + ulong x = ulong.max - 1; // #error: This expression (18446744073709551615) will be implicitly converted to a signed type due to the right-hand side being signed + ulong x1 = -1 + ulong.max; // #error: converted to a signed type due to the left-hand side being signed, but the value does + return 0; +} \ No newline at end of file