From 1b2f5989e1f8a12b789e0f2e782836fd0894fbf8 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 23 Jun 2025 14:12:34 +0200 Subject: [PATCH] Assert casting bitstruct to short/char #2237 --- releasenotes.md | 1 + src/compiler/compiler_internal.h | 6 ++++ src/compiler/parse_expr.c | 1 + src/compiler/sema_casts.c | 36 ++++++++++--------- .../cast/implicit_widen_const_bitstruct.c3 | 9 +++++ 5 files changed, 36 insertions(+), 17 deletions(-) create mode 100644 test/test_suite/cast/implicit_widen_const_bitstruct.c3 diff --git a/releasenotes.md b/releasenotes.md index 70040444e..3d4c247a2 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -61,6 +61,7 @@ - Overload resolution fixes to inline typedef #2226. - `math::overflow_*` wrappers incorrectly don't allow distinct integers #2221. - Compiler segfault when using distinct type in attribute imported from other module #2234. +- Assert casting bitstruct to short/char #2237 ### Stdlib changes - Deprecate `String.is_zstr` and `String.quick_zstr` #2188. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 2cb59ee24..27329f310 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -4094,6 +4094,12 @@ INLINE bool expr_is_const_enum(Expr *expr) return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_ENUM; } +INLINE bool expr_is_const_number(Expr *expr) +{ + ASSERT(expr->resolve_status == RESOLVE_DONE); + return expr->expr_kind == EXPR_CONST && (expr->const_expr.const_kind == CONST_INTEGER || expr->const_expr.const_kind == CONST_FLOAT); +} + INLINE bool expr_is_const_fault(Expr *expr) { ASSERT(expr->resolve_status == RESOLVE_DONE); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 1155b3ffc..bc9a0b569 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -824,6 +824,7 @@ static Expr *parse_grouping_expr(ParseContext *c, Expr *left) .span = span, .cast_expr.type_info = type_infoid(info), .cast_expr.expr = inner}; + RANGE_EXTEND_PREV(expr); } break; } diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 14140b37a..4ed297b84 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -495,22 +495,24 @@ RETRY: UNREACHABLE case EXPR_CONST: // For constants, just check that they will fit. - if (type_is_integer(type)) + switch (expr->const_expr.const_kind) { - ASSERT(expr->const_expr.const_kind == CONST_INTEGER || expr->const_expr.const_kind == CONST_ENUM); - if (expr_const_will_overflow(&expr->const_expr, type_flatten(type)->type_kind)) - { - return expr; - } - return NULL; + case CONST_ENUM: + case CONST_INTEGER: + if (expr_const_will_overflow(&expr->const_expr, type_flatten(type)->type_kind)) + { + return expr; + } + return NULL; + case CONST_FLOAT: + if (!expr_const_float_fits_type(&expr->const_expr, type_flatten(type)->type_kind)) + { + return expr; + } + return NULL; + default: + goto CHECK_SIZE; } - ASSERT(type_is_float(type)); - ASSERT(expr->const_expr.const_kind == CONST_FLOAT); - if (!expr_const_float_fits_type(&expr->const_expr, type_flatten(type)->type_kind)) - { - return expr; - } - return NULL; case EXPR_POST_UNARY: expr = expr->unary_expr.expr; goto RETRY; @@ -1294,7 +1296,7 @@ static bool rule_widen_narrow(CastContext *cc, bool is_explicit, bool is_silent) } // If const, check in range. - if (sema_cast_const(expr) && expr_const_will_overflow(&expr->const_expr, cc->to->type_kind)) + if (sema_cast_const(expr) && expr_is_const_number(expr) && expr_const_will_overflow(&expr->const_expr, cc->to->type_kind)) { if (!is_silent) { @@ -1319,7 +1321,7 @@ static bool rule_widen_narrow(CastContext *cc, bool is_explicit, bool is_silent) // If it's an integer that's the problem, zoom in on that one. if (type_is_integer(type_flatten(problem->type))) expr = problem; // Otherwise require a cast. - if (expr_is_const(expr)) + if (expr_is_const_number(expr)) { RETURN_CAST_ERROR(expr, "The value of the expression (%s) is out of range and cannot implicitly be converted to %s, but you may use a cast.", expr_const_to_error_string(&expr->const_expr), @@ -1704,7 +1706,7 @@ static void cast_int_to_int(Expr *expr, Type *type) } // Insert runtime casts on non-const. - if (!expr_is_const(expr)) + if (!expr_is_const_int(expr)) { expr_rewrite_ext_trunc(expr, type, type_is_signed(type_flatten_to_int(expr->type))); return; diff --git a/test/test_suite/cast/implicit_widen_const_bitstruct.c3 b/test/test_suite/cast/implicit_widen_const_bitstruct.c3 new file mode 100644 index 000000000..a5c1543b8 --- /dev/null +++ b/test/test_suite/cast/implicit_widen_const_bitstruct.c3 @@ -0,0 +1,9 @@ +bitstruct Foo : ushort +{ + bool bar; +} + +fn void main() +{ + ushort foo = (ushort)((Foo) {.bar}) & 1; +} \ No newline at end of file