From dd8449576fce9c0339281c63e20d54fc76d6a080 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 18 Jan 2026 22:47:17 +0100 Subject: [PATCH] - Passing a non-conststring to module attributes like @cname would trigger an assert rather than printing an error. - Passing different types to arg 1 and 2 for $$matrix_transpose would trigger an assert. - Zero init of optional compile time variable would crash the compiler. - Using multiple declaration for generics in generic module would fail. - Defining an extern const without a type would crash rather than print an error. - Typedef followed by brace would trigger an assert. - Union with too big member would trigger an assert. --- releasenotes.md | 7 ++++++ src/compiler/parse_global.c | 22 +++++++++++++++---- src/compiler/sema_builtins.c | 1 + src/compiler/sema_decls.c | 9 +++++++- .../attributes/string_module_attributes.c3 | 5 +++++ .../builtins/matrix_builtin_cast.c3 | 6 +++++ .../compile_time_zero_val_optional_init.c3t | 4 ++++ .../generic/generic_multiple_decl.c3t | 11 ++++++++++ .../globals/extern_const_no_type.c3 | 1 + .../types/typedef_followed_by_brace.c3 | 2 ++ test/test_suite/union/union_too_big.c3 | 4 ++++ 11 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 test/test_suite/attributes/string_module_attributes.c3 create mode 100644 test/test_suite/builtins/matrix_builtin_cast.c3 create mode 100644 test/test_suite/compile_time/compile_time_zero_val_optional_init.c3t create mode 100644 test/test_suite/generic/generic_multiple_decl.c3t create mode 100644 test/test_suite/globals/extern_const_no_type.c3 create mode 100644 test/test_suite/types/typedef_followed_by_brace.c3 create mode 100644 test/test_suite/union/union_too_big.c3 diff --git a/releasenotes.md b/releasenotes.md index 11d26ed91..49d595969 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -66,6 +66,13 @@ - Too little memory reserved when printing backtrace on Darwin #2698. - In some cases, a type would not get implicitly converted to a typeid #2764. - Assert on defining a const fault enum with enumerator and fault of the same name. #2732 +- Passing a non-conststring to module attributes like @cname would trigger an assert rather than printing an error. #2771 +- Passing different types to arg 1 and 2 for $$matrix_transpose would trigger an assert. #2771 +- Zero init of optional compile time variable would crash the compiler. #2771 +- Using multiple declaration for generics in generic module would fail. #2771 +- Defining an extern const without a type would crash rather than print an error. #2771 +- Typedef followed by brace would trigger an assert. #2771 +- Union with too big member would trigger an assert. #2771 ### Stdlib changes - Add `ThreadPool` join function to wait for all threads to finish in the pool without destroying the threads. diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 521b106d8..7113d2d9f 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -370,7 +370,7 @@ bool parse_module(ParseContext *c, AstId contracts) vec_size(attr->exprs)); } Expr *expr = attr->exprs[0]; - if (!expr_is_const_string(expr)) RETURN_PRINT_ERROR_AT(false, expr, "Expected a constant string."); + if (expr->resolve_status != RESOLVE_DONE || !expr_is_const_string(expr)) RETURN_PRINT_ERROR_AT(false, expr, "Expected a constant string."); if (c->unit->module->extname) { RETURN_PRINT_ERROR_AT(false, attr, @@ -388,7 +388,7 @@ bool parse_module(ParseContext *c, AstId contracts) vec_size(attr->exprs)); } Expr *expr = attr->exprs[0]; - if (!expr_is_const_string(expr)) RETURN_PRINT_ERROR_AT(false, expr, "Expected a constant string."); + if (expr->resolve_status != RESOLVE_DONE || !expr_is_const_string(expr)) RETURN_PRINT_ERROR_AT(false, expr, "Expected a constant string."); if (c->unit->module->extname) { RETURN_PRINT_ERROR_AT(false, attr, @@ -1533,6 +1533,7 @@ static inline Decl *parse_global_declaration(ParseContext *c) CONSUME_EOS_OR_RET(poisoned_decl); Attr **attributes = decl->attributes; // Copy the attributes to the other variables. + if (attributes) { FOREACH(Decl *, d, decls) @@ -1541,12 +1542,27 @@ static inline Decl *parse_global_declaration(ParseContext *c) d->attributes = copy_attributes_single(attributes); } } + int generics_id = decl->is_template ? decl->generic_id : -1; + if (generics_id > -1) + { + FOREACH(Decl *, d, decls) + { + if (d == decl) continue; + d->generic_id = generics_id; + d->is_template = true; + } + } // If we have multiple decls, then we return that as a bundled decl_globals if (decls) { decl = decl_calloc(); decl->decl_kind = DECL_GROUP; decl->decls = decls; + if (generics_id > -1) + { + decl->generic_id = generics_id; + decl->is_template = true; + } return decl; } return decl; @@ -2065,8 +2081,6 @@ static inline Decl *parse_typedef_declaration(ParseContext *c) // 2. Now parse the type which we know is here. ASSIGN_TYPE_OR_RET(decl->distinct, parse_optional_type(c), poisoned_decl); - ASSERT(!tok_is(c, TOKEN_LBRACE)); - while (tok_is(c, TOKEN_AT_IDENT)) { const char *name = symstr(c); diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index 4abcfa9bb..ae4388dac 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -835,6 +835,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) if (!sema_check_builtin_args(context, args, (BuiltinArg[]) {BA_VEC, BA_INTEGER, BA_INTEGER}, 3)) return false; if (!sema_check_builtin_args_const(context, &args[1], 2)) return false; ArraySize vec_len = type_flatten(args[0]->type)->array.len; + if (!cast_implicit(context, args[2], args[1]->type, false)) return false; Int sum = int_mul(args[1]->const_expr.ixx, args[2]->const_expr.ixx); if (!int_icomp(sum, vec_len, BINARYOP_EQ)) { diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 3503e44c0..27688704c 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -362,6 +362,8 @@ static bool sema_analyse_union_members(SemaContext *context, Decl *decl) if (!sema_check_struct_holes(context, decl, member)) return false; ByteSize member_size = type_size(member->type); + if (member_size > MAX_STRUCT_SIZE) RETURN_SEMA_ERROR(member, "Union member '%s' would cause the union to become too large (exceeding 2 GB).", member->name); + ASSERT(member_size <= MAX_TYPE_SIZE); // Update max alignment if (member->alignment > member_alignment) member_alignment = member->alignment; @@ -4726,7 +4728,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl, bool *check_fail goto FAIL; } decl->var.init_expr = init = expr_new(EXPR_POISONED, decl->span); - expr_rewrite_to_const_zero(init, decl->type); + expr_rewrite_to_const_zero(init, type_no_optional(decl->type)); } // Analyse the expression. @@ -4813,6 +4815,11 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local, bool *c TypeInfo *type_info = vartype(decl); // We expect a constant to actually be parsed correctly so that it has a value, so // this should always be true. + if (!type_info && decl->is_extern) + { + SEMA_ERROR(decl, "A type is needed for the extern %s '%s'.", decl_to_name(decl), decl->name); + return decl_poison(decl); + } ASSERT(type_info || decl->var.init_expr); bool erase_decl = false; diff --git a/test/test_suite/attributes/string_module_attributes.c3 b/test/test_suite/attributes/string_module_attributes.c3 new file mode 100644 index 000000000..7f7397ba2 --- /dev/null +++ b/test/test_suite/attributes/string_module_attributes.c3 @@ -0,0 +1,5 @@ +module linux @cname(NETBSD ??? "" : ""); // #error: Expected a constant string +fn int main() +{ + return 0; +} \ No newline at end of file diff --git a/test/test_suite/builtins/matrix_builtin_cast.c3 b/test/test_suite/builtins/matrix_builtin_cast.c3 new file mode 100644 index 000000000..edb7c434f --- /dev/null +++ b/test/test_suite/builtins/matrix_builtin_cast.c3 @@ -0,0 +1,6 @@ +fn int main() +{ + int[<6>] c = 2; + c = $$matrix_transpose(c, 0x10, 3); // #error: Expected row * col to equal 6 + return 0; +} \ No newline at end of file diff --git a/test/test_suite/compile_time/compile_time_zero_val_optional_init.c3t b/test/test_suite/compile_time/compile_time_zero_val_optional_init.c3t new file mode 100644 index 000000000..5d6850984 --- /dev/null +++ b/test/test_suite/compile_time/compile_time_zero_val_optional_init.c3t @@ -0,0 +1,4 @@ +fn void main() +{ + char? a, $b; +} \ No newline at end of file diff --git a/test/test_suite/generic/generic_multiple_decl.c3t b/test/test_suite/generic/generic_multiple_decl.c3t new file mode 100644 index 000000000..255686dcb --- /dev/null +++ b/test/test_suite/generic/generic_multiple_decl.c3t @@ -0,0 +1,11 @@ +module elastic_array ; +int a, b, c; +module bar; +import elastic_array; +fn int main() +{ + int g = elastic_array::a{int, 2}; + int g2 = elastic_array::b{int, 2}; + elastic_array::b{int, 2} = 3; + return 0; +} \ No newline at end of file diff --git a/test/test_suite/globals/extern_const_no_type.c3 b/test/test_suite/globals/extern_const_no_type.c3 new file mode 100644 index 000000000..377741285 --- /dev/null +++ b/test/test_suite/globals/extern_const_no_type.c3 @@ -0,0 +1 @@ +extern const FOO; // #error: A type is needed for the extern constant 'FOO' \ No newline at end of file diff --git a/test/test_suite/types/typedef_followed_by_brace.c3 b/test/test_suite/types/typedef_followed_by_brace.c3 new file mode 100644 index 000000000..11c950a7b --- /dev/null +++ b/test/test_suite/types/typedef_followed_by_brace.c3 @@ -0,0 +1,2 @@ +typedef Bar = BacktraceList? // #error: Expected ';' +{ \ No newline at end of file diff --git a/test/test_suite/union/union_too_big.c3 b/test/test_suite/union/union_too_big.c3 new file mode 100644 index 000000000..1ca03261c --- /dev/null +++ b/test/test_suite/union/union_too_big.c3 @@ -0,0 +1,4 @@ +union Xu +{ + int[int.max] b; // #error: Union member 'b' would cause the union to become too large +} \ No newline at end of file