From a38a627a1d37998a56ef1f34006d3345f0307f90 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 25 Oct 2025 20:33:47 +0200 Subject: [PATCH] Allow `(Foo)0` bitstruct casts even if type sizes do not match. --- releasenotes.md | 1 + resources/testproject/src/{ => hello}/bar.c3 | 0 .../src/{ => hello}/hello_world.c3 | 0 src/compiler/sema_casts.c | 30 +++++++++++++++++-- .../bitstruct/bistruct_cast_wrong_size.c3 | 22 ++++++++------ .../bitstruct/bitstruct_cast_literal.c3t | 27 +++++++++++++++++ 6 files changed, 69 insertions(+), 11 deletions(-) rename resources/testproject/src/{ => hello}/bar.c3 (100%) rename resources/testproject/src/{ => hello}/hello_world.c3 (100%) create mode 100644 test/test_suite/bitstruct/bitstruct_cast_literal.c3t diff --git a/releasenotes.md b/releasenotes.md index 14a371858..21bb22b5c 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -15,6 +15,7 @@ - ABI change for vectors: store and pass them as arrays #2542. - Add @simd and @align attributes to typedef #2543. - Rename `@extern` to `@cname`, deprecating the old name #2493. +- Allow `(Foo)0` bitstruct casts even if type sizes do not match. ### Fixes - Bug in `io::write_using_write_byte`. diff --git a/resources/testproject/src/bar.c3 b/resources/testproject/src/hello/bar.c3 similarity index 100% rename from resources/testproject/src/bar.c3 rename to resources/testproject/src/hello/bar.c3 diff --git a/resources/testproject/src/hello_world.c3 b/resources/testproject/src/hello/hello_world.c3 similarity index 100% rename from resources/testproject/src/hello_world.c3 rename to resources/testproject/src/hello/hello_world.c3 diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 549128c8d..d2a3a4174 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -1523,7 +1523,24 @@ static bool rule_int_to_bits(CastContext *cc, bool is_explicit, bool is_silent) { Type *base_type = cc->to->decl->strukt.container_type->type; Type *from_type = cc->from; - bool success = type_is_integer(base_type) && type_size(from_type) == type_size(base_type); + Expr *expr = cc->expr; + bool success = false; + do + { + if (!type_is_integer(base_type)) break; + if (type_size(base_type) == type_size(from_type)) + { + success = true; + break; + } + if (!sema_cast_const(expr) || !expr_is_const_int(expr)) break; + if (!int_fits(cc->expr->const_expr.ixx, base_type->canonical->type_kind)) + { + if (is_silent) return false; + RETURN_CAST_ERROR(expr, "The value '%s' does not fit in the container type of the bitstruct %s, which is %s.", int_to_str(cc->expr->const_expr.ixx, 10, false), type_quoted_error_string(cc->to), type_quoted_error_string(base_type)); + } + success = true; + } while (0); if (!is_explicit || !success) return sema_cast_error(cc, success, is_silent); return true; } @@ -1857,7 +1874,16 @@ static void cast_expand_to_vec(Expr *expr, Type *type) } static void cast_bitstruct_to_int_arr(Expr *expr, Type *type) { expr_rewrite_recast(expr, type); } -static void cast_int_arr_to_bitstruct(Expr *expr, Type *type) { expr_rewrite_recast(expr, type); } +static void cast_int_arr_to_bitstruct(Expr *expr, Type *type) +{ + if (expr_is_const_int(expr)) + { + Type *widening_type = type_flatten(type)->decl->strukt.container_type->type->canonical; + expr->const_expr.ixx.type = widening_type->type_kind; + expr->type = widening_type; + } + expr_rewrite_recast(expr, type); +} static void cast_bitstruct_to_bool(Expr *expr, Type *type) { diff --git a/test/test_suite/bitstruct/bistruct_cast_wrong_size.c3 b/test/test_suite/bitstruct/bistruct_cast_wrong_size.c3 index 27b1e34c7..48d415eed 100644 --- a/test/test_suite/bitstruct/bistruct_cast_wrong_size.c3 +++ b/test/test_suite/bitstruct/bistruct_cast_wrong_size.c3 @@ -1,14 +1,18 @@ module bug_repro; -bitstruct Edge_Flags : char { - bool left; - bool top; - bool right; - bool bottom; +bitstruct Edge_Flags : char +{ + bool left; + bool top; + bool right; + bool bottom; } -fn int main() { - Edge_Flags edges; - Edge_Flags a = edges & (Edge_Flags)(1 << 0); // #error: 'int' - return 0; +fn int main() +{ + Edge_Flags edges; + Edge_Flags a = edges & (Edge_Flags)(1 << 8); // #error: The value '256' does not fit in the container type of the bitstruct + int x = 0; + Edge_Flags ab = edges & (Edge_Flags)x; // #error: 'int' + return 0; } \ No newline at end of file diff --git a/test/test_suite/bitstruct/bitstruct_cast_literal.c3t b/test/test_suite/bitstruct/bitstruct_cast_literal.c3t new file mode 100644 index 000000000..8cd75b015 --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_cast_literal.c3t @@ -0,0 +1,27 @@ +// #target: macos-x64 +module test; +bitstruct Foo : char +{ + bool baz; +} + +fn int main() +{ + char ch = 0; + Foo foo1 = (Foo)ch; + Foo foo2 = (Foo)0; + return 0; +} + +/* #expect: test.ll + +entry: + %ch = alloca i8, align 1 + %foo1 = alloca i8, align 1 + %foo2 = alloca i8, align 1 + store i8 0, ptr %ch, align 1 + %0 = load i8, ptr %ch, align 1 + store i8 %0, ptr %foo1, align 1 + store i8 0, ptr %foo2, align 1 + ret i32 0 +}