From 4512c6446d48c3122413150a53ae2e6a63d16d53 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 19 Jan 2026 15:01:08 +0100 Subject: [PATCH] - Empty struct after `@if` processing was not detected, causing a crash instead of an error. - Comparing an uint and int[<4>] was incorrectly assumed to be uint compared to int, causing a crash instead of an error. - When an `int[*][6]` was given too few values, the compiler would assert instead of giving an error. --- releasenotes.md | 3 +++ src/compiler/compiler_internal.h | 6 ++++-- src/compiler/llvm_codegen_builtins.c | 6 +++--- src/compiler/llvm_codegen_expr.c | 10 +++++----- src/compiler/sema_decls.c | 10 +++++++++- src/compiler/sema_expr.c | 2 +- src/compiler/sema_initializers.c | 4 ++++ .../test_suite/arrays/array_of_inferred_array.c3 | 8 ++++++++ test/test_suite/struct/empty_struct_if.c3 | 16 ++++++++++++++++ test/test_suite/vector/vec_compare_int.c3 | 7 +++++++ test/test_suite/vector/vector_from_hex.c3t | 5 ----- 11 files changed, 60 insertions(+), 17 deletions(-) create mode 100644 test/test_suite/arrays/array_of_inferred_array.c3 create mode 100644 test/test_suite/struct/empty_struct_if.c3 create mode 100644 test/test_suite/vector/vec_compare_int.c3 diff --git a/releasenotes.md b/releasenotes.md index 6b9e10fc0..25480875d 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -81,6 +81,9 @@ - Mixing struct splat, non-named params and named params would crash rather than to print an error. #2771 - Creating a char vector from bytes would crash. #2771 - Using $$wstr16 with an illegal argument would crash instead of printing an error. #2771 +- Empty struct after `@if` processing was not detected, causing a crash instead of an error. #2771 +- Comparing an uint and int[<4>] was incorrectly assumed to be uint compared to int, causing a crash instead of an error. #2771 +- When an `int[*][6]` was given too few values, the compiler would assert instead of giving an error. #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/compiler_internal.h b/src/compiler/compiler_internal.h index 682b1e444..ccf7abb83 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2739,7 +2739,7 @@ INLINE bool type_is_arraylike(Type *type); INLINE bool type_is_any_arraylike(Type *type); INLINE bool type_is_promotable_float(Type *type); INLINE bool type_is_promotable_int_bool(Type *type); -INLINE bool type_is_signed(Type *type); +INLINE bool type_is_signed_any(Type *type); INLINE bool type_ok(Type *type); INLINE bool type_is_unsigned(Type *type); INLINE bool type_is_union_or_strukt(Type *type); @@ -2820,6 +2820,7 @@ INLINE Type *type_from_inferred(Type *flattened, Type *element_type, unsigned co case TYPE_INFERRED_VECTOR: return type_get_vector(element_type, TYPE_VECTOR, count); case TYPE_ARRAY: + ASSERT(flattened->array.len == count); FALLTHROUGH; case TYPE_INFERRED_ARRAY: @@ -3519,11 +3520,12 @@ INLINE bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kin INLINE bool type_kind_is_any_integer(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_U128; } INLINE bool type_kind_is_enum_or_fault(TypeKind kind) { return kind == TYPE_ENUM || kind == TYPE_ANYFAULT; } INLINE bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U128; } +INLINE bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I128; } INLINE bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; } INLINE bool type_info_ok(TypeInfo *type_info) { return !type_info || type_info->kind != TYPE_INFO_POISON; } bool type_is_scalar(Type *type); -INLINE bool type_is_signed(Type *type) +INLINE bool type_is_signed_any(Type *type) { TypeKind kind = type->type_kind; if (kind >= TYPE_I8 && kind < TYPE_U8) return true; diff --git a/src/compiler/llvm_codegen_builtins.c b/src/compiler/llvm_codegen_builtins.c index 3c8327618..527c87669 100644 --- a/src/compiler/llvm_codegen_builtins.c +++ b/src/compiler/llvm_codegen_builtins.c @@ -707,7 +707,7 @@ static void llvm_emit_overflow_builtin(GenContext *c, BEValue *be_value, Expr *e // Note that we can make additional improvements here! llvm_value_set_address(c, &ref, ref.value, ref.type->pointer, type_abi_alignment(ref.type->pointer)); LLVMTypeRef call_type[1] = { LLVMTypeOf(arg_slots[0]) }; - unsigned intrinsic = type_is_signed(type_lowering(args[0]->type)) ? intrinsic_signed : intrinsic_unsigned; + unsigned intrinsic = type_is_signed_any(type_lowering(args[0]->type)) ? intrinsic_signed : intrinsic_unsigned; LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 1, arg_slots, 2); LLVMValueRef failed = llvm_emit_extract_value(c, result, 1); LLVMValueRef value = llvm_emit_extract_value(c, result, 0); @@ -739,7 +739,7 @@ static void llvm_emit_wrap_builtin(GenContext *c, BEValue *result_value, Expr *e res = LLVMBuildMul(c->builder, arg_slots[0], arg_slots[1], "emul"); break; case BUILTIN_EXACT_DIV: - if (type_is_signed(base_type)) + if (type_is_signed_any(base_type)) { res = LLVMBuildSDiv(c->builder, arg_slots[0], arg_slots[1], "esdiv"); } @@ -749,7 +749,7 @@ static void llvm_emit_wrap_builtin(GenContext *c, BEValue *result_value, Expr *e } break; case BUILTIN_EXACT_MOD: - if (type_is_signed(base_type)) + if (type_is_signed_any(base_type)) { res = LLVMBuildSRem(c->builder, arg_slots[0], arg_slots[1], "eumod"); } diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 8898b41d3..cf2390f1f 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2094,7 +2094,7 @@ static inline LLVMValueRef llvm_emit_inc_dec_value(GenContext *c, SourceSpan spa LLVMValueRef diff_value = LLVMConstInt(llvm_type, 1, false); if (!allow_wrap) { - if (type_is_signed(type)) + if (type_is_signed_any(type)) { return diff > 0 ? LLVMBuildNSWAdd(c->builder, original->value, diff_value, "addnsw") @@ -3119,8 +3119,8 @@ void llvm_emit_int_comp_raw(GenContext *c, BEValue *result, Type *lhs_type, Type Type *vector_type = type_vector_type(lhs_type); if (vector_type) { - lhs_signed = type_is_signed(vector_type); - rhs_signed = type_is_signed(type_vector_type(rhs_type)); + lhs_signed = type_is_signed_any(vector_type); + rhs_signed = type_is_signed_any(type_vector_type(rhs_type)); } else { @@ -7167,7 +7167,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_FLOAT_TO_INT: llvm_emit_expr(c, value, expr->inner_expr); llvm_value_rvalue(c, value); - if (type_is_signed(type_lowering(expr->type))) + if (type_is_signed_any(type_lowering(expr->type))) { llvm_value_set(value, LLVMBuildFPToSI(c->builder, value->value, llvm_get_type(c, expr->type), "fpsi"), expr->type); return; @@ -7177,7 +7177,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_INT_TO_FLOAT: llvm_emit_expr(c, value, expr->inner_expr); llvm_value_rvalue(c, value); - if (type_is_signed(value->type)) + if (type_is_signed_any(value->type)) { llvm_value_set(value, LLVMBuildSIToFP(c->builder, value->value, llvm_get_type(c, expr->type), "sifp"), expr->type); } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 13a9401d0..81e2dcf42 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -387,7 +387,10 @@ static bool sema_analyse_union_members(SemaContext *context, Decl *decl) // Offset is always 0 member->offset = 0; } - + if (!member_count) + { + RETURN_SEMA_ERROR(decl, "No union members exist after processing attributes, this is not allowed. Please make sure it has at least one member."); + } ASSERT(decl_ok(decl)); // 1. If packed, then the alignment is zero, unless previously given @@ -644,6 +647,11 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl) if (offset < sz || offset > MAX_STRUCT_SIZE) RETURN_SEMA_ERROR(member, "Struct member '%s' would cause the struct to become too large (exceeding 2 GB).", member->name); } + if (!member_count) + { + RETURN_SEMA_ERROR(decl, "No members exist for this struct after processing attributes, creating an invalid empty struct. Please make sure every struct/inner struct has at least one member."); + } + // Set the alignment: // 1. If packed, use the alignment given, otherwise set to 1. diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 674574b4f..a14680bc3 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -7688,7 +7688,7 @@ 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)); } - if (type_is_signed(flat_max)) + if (type_is_signed_any(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; diff --git a/src/compiler/sema_initializers.c b/src/compiler/sema_initializers.c index 5b8cbcb6e..5b28b84b2 100644 --- a/src/compiler/sema_initializers.c +++ b/src/compiler/sema_initializers.c @@ -348,6 +348,10 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex // Generate a nice error message for zero. RETURN_SEMA_ERROR(elements[0], "Too many elements in initializer, it must be empty."); } + if (expected_members > 0 && count > 0 && count != expected_members) + { + RETURN_SEMA_ERROR(elements[0], "Too %s elements in initializer, expected %u.", count > expected_members ? "many" : "few", expected_members); + } bool optional = false; bool is_vector = type_flat_is_vector(assigned); diff --git a/test/test_suite/arrays/array_of_inferred_array.c3 b/test/test_suite/arrays/array_of_inferred_array.c3 new file mode 100644 index 000000000..2b9137250 --- /dev/null +++ b/test/test_suite/arrays/array_of_inferred_array.c3 @@ -0,0 +1,8 @@ +import std; +macro void test(int[*][6] a) +{ +} +fn void main() +{ + test({ { 1, 2 } }); // #error: Too few elements in initializer +} \ No newline at end of file diff --git a/test/test_suite/struct/empty_struct_if.c3 b/test/test_suite/struct/empty_struct_if.c3 new file mode 100644 index 000000000..ab7504b3c --- /dev/null +++ b/test/test_suite/struct/empty_struct_if.c3 @@ -0,0 +1,16 @@ +import std; +const bool FOO = false; +struct Foo +{ + struct + { + struct // #error: members exist for this struct after processing + { + char d_pad0 @if(FOO); + } + } +} +fn void main() +{ + Foo f; +} \ No newline at end of file diff --git a/test/test_suite/vector/vec_compare_int.c3 b/test/test_suite/vector/vec_compare_int.c3 new file mode 100644 index 000000000..0eee1ab41 --- /dev/null +++ b/test/test_suite/vector/vec_compare_int.c3 @@ -0,0 +1,7 @@ +import std; +fn void main() +{ + Win32_UINT v1 = 1; + int[<4>] v2 = 2; + bool[<4>] vb = (v1 == v2); // #error: 'Win32_UINT' (uint) to 'int[<4>]' is not permitted, but you may do an explicit cast by placing +} diff --git a/test/test_suite/vector/vector_from_hex.c3t b/test/test_suite/vector/vector_from_hex.c3t index 1353f0698..449443565 100644 --- a/test/test_suite/vector/vector_from_hex.c3t +++ b/test/test_suite/vector/vector_from_hex.c3t @@ -10,11 +10,6 @@ fn void main() /* #expect: test.ll -; ModuleID = 'test' -source_filename = "test" -target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" -target triple = "x86_64-apple-macosx10.13.0" - @test.fooy = local_unnamed_addr global [2 x i8] c"\DE\AD", align 2 @test.fooy2 = local_unnamed_addr global <2 x i8> , align 2