From cb2d0e798ec633389b1620519a0b5104771d8d01 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 29 Sep 2025 01:59:38 +0200 Subject: [PATCH] Prevent `foo.bar = {}` when `bar` is a flexible array member. --- releasenotes.md | 1 + src/compiler/sema_expr.c | 10 +++++++++ src/compiler/sema_internal.h | 2 ++ test/test_suite/struct/flex_more_errors.c3 | 25 ++++++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 test/test_suite/struct/flex_more_errors.c3 diff --git a/releasenotes.md b/releasenotes.md index 39a4f1759..a0d47581c 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -41,6 +41,7 @@ - Issue not correctly aborting compilation on recursive generics. - Crash during codegen when taking the typeid of an empty enum with associated values. - Assert when the binary doesn't get created and --run-once is used. #2502 +- Prevent `foo.bar = {}` when `bar` is a flexible array member. ### Stdlib changes - Added generic `InterfaceList` to store a list of values that implement a specific interface diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 7b5bae818..07bc95915 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -6862,6 +6862,10 @@ static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *lef default: break; } + if (left->type && left->type->type_kind == TYPE_FLEXIBLE_ARRAY) + { + RETURN_SEMA_ERROR(left, "You can't assign to a flexible array member, but you may index into it and mutate it that way."); + } // 2. Check assignability if (!sema_expr_check_assign(context, left, failed_ref)) return false; @@ -7174,7 +7178,13 @@ static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr * break; } + if (left->type->type_kind == TYPE_FLEXIBLE_ARRAY) + { + RETURN_SEMA_ERROR(left, "You can't assign to a flexible array member, but you may index into it and mutate it that way."); + } + // 2. Verify that the left side is assignable. + if (!sema_expr_check_assign(context, left, NULL)) return false; Type *left_type_canonical = left->type->canonical; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 3c90a6bf9..80f1cf9f6 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -275,6 +275,8 @@ static inline StorageType sema_resolve_storage_type(SemaContext *context, Type * if (!sema_analyse_decl(context, type->decl)) return false; type = type->decl->distinct->type; goto RETRY; + case TYPE_FLEXIBLE_ARRAY: + return STORAGE_UNKNOWN; default: return STORAGE_NORMAL; } diff --git a/test/test_suite/struct/flex_more_errors.c3 b/test/test_suite/struct/flex_more_errors.c3 new file mode 100644 index 000000000..485ce9c01 --- /dev/null +++ b/test/test_suite/struct/flex_more_errors.c3 @@ -0,0 +1,25 @@ +module main; + +struct OwnedString +{ + usz len; + char[*] data; +} + +fn void test1() +{ + OwnedString owned; + owned.data = {}; // #error: You can't assign to a flexible array member +} + +fn void test2() +{ + $typefrom(OwnedString.data.typeid) x = 1; // #error: 'char[*]' has unknown size, and +} + +fn int main() +{ + OwnedString owned; + owned.data[0] = 1; + return 0; +} \ No newline at end of file