- 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.
This commit is contained in:
Christoffer Lerno
2026-01-19 15:01:08 +01:00
parent 0fea6c6056
commit 4512c6446d
11 changed files with 60 additions and 17 deletions

View File

@@ -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.

View File

@@ -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;

View File

@@ -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");
}

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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);

View File

@@ -0,0 +1,8 @@
import std;
macro void test(int[*][6] a)
{
}
fn void main()
{
test({ { 1, 2 } }); // #error: Too few elements in initializer
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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> <i8 -34, i8 -83>, align 2