From a126a25d66573df91fe265839fa417e8c4c98099 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 21 Jan 2026 12:56:25 +0100 Subject: [PATCH] Casting const bytes to vector with different element size was broken #2787 --- releasenotes.md | 21 +++--- src/compiler/parse_global.c | 5 ++ src/compiler/sema_casts.c | 76 ++++++++++++++++---- test/test_suite/slices/slice_to_vec_conv.c3t | 15 ++++ test/test_suite/vector/vec_from_bytes.c3t | 11 +++ test/test_suite/vector/vector_from_hex.c3t | 3 +- 6 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 test/test_suite/slices/slice_to_vec_conv.c3t create mode 100644 test/test_suite/vector/vec_from_bytes.c3t diff --git a/releasenotes.md b/releasenotes.md index be5745e4d..71f08767d 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -20,16 +20,6 @@ - Module-based generics using {} is deprecated. - Create optional with `~` instead of `?`. `return io::EOF?;` becomes `return io::EOF~`. - Deprecated use of `?` to create optional. -- Vectors not converted to arrays when passed as raw vaargs. #2776 -- Second value in switch range not checked properly, causing an error on non-const values. #2777 -- Broken cast from fault to array pointer #2778. -- $typeof untyped list crashes when trying to create typeid from it. #2779 -- Recursive constant definition not properly detected, leading to assert #2780 -- Failed to reject void compile time variables, leading to crash. #2781 -- Inferring the size of a slice with an inner inferred array using {} isn't detected as error #2783 -- Bug in sysv abi when passing union in with floats #2784 -- When a global const has invalid attributes, handling is incorrect, leading to a crash #2785. -- `int? ?` was not correctly handled. #2786 ### Fixes - Regression with npot vector in struct triggering an assert #2219. @@ -98,6 +88,17 @@ - When an `int[*][6]` was given too few values, the compiler would assert instead of giving an error. #2771 - Inferring length from a slice was accidentally not an error. - Eager evaluation of macro arguments would break inferred arrays on some platforms. #2771. +- Vectors not converted to arrays when passed as raw vaargs. #2776 +- Second value in switch range not checked properly, causing an error on non-const values. #2777 +- Broken cast from fault to array pointer #2778. +- $typeof untyped list crashes when trying to create typeid from it. #2779 +- Recursive constant definition not properly detected, leading to assert #2780 +- Failed to reject void compile time variables, leading to crash. #2781 +- Inferring the size of a slice with an inner inferred array using {} isn't detected as error #2783 +- Bug in sysv abi when passing union in with floats #2784 +- When a global const has invalid attributes, handling is incorrect, leading to a crash #2785. +- `int? ?` was not correctly handled. #2786 +- Casting const bytes to vector with different element size was broken #2787 ### 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 3de9a0ad8..56af7bb8c 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -689,6 +689,11 @@ static inline TypeInfo *parse_vector_type_index(ParseContext *c, TypeInfo *type) PRINT_ERROR_HERE("Only '@simd' is a valid attribute, found '%s'.", symstr(c)); return poisoned_type_info; } + if (vector->kind == TYPE_INFO_INFERRED_VECTOR) + { + PRINT_ERROR_HERE("The '@simd' attribute cannot be used on an inferred vector type."); + return poisoned_type_info; + } advance(c); vector->is_simd = true; } diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 8c2e22a4d..8287dc6a4 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -25,9 +25,10 @@ typedef struct // NOLINT static bool sema_error_const_int_out_of_range(CastContext *cc, Expr *expr, Expr *problem, Type *to_type); static Expr *recursive_may_narrow(Expr *expr, Type *type); static void expr_recursively_rewrite_untyped_list(Expr *expr, Type *to_type); +static void expr_rewrite_bytes_to_const_initializer(Expr *expr, Type *target_type); static void vector_const_initializer_convert_to_type(ConstInitializer *initializer, Type *to_type); static bool cast_is_allowed(CastContext *cc, bool is_explicit, bool is_silent); - +static void cast_arr_to_vec(Expr *expr, Type *to_type); static bool cast_if_valid(SemaContext *context, Expr *expr, Type *to_type, bool is_explicit, bool is_silent, bool is_binary_conversion); INLINE ConvGroup type_to_group(Type *type); @@ -2034,7 +2035,7 @@ static void cast_vec_to_arr(Expr *expr, Type *to_type) */ static void cast_vec_to_vec(Expr *expr, Type *to_type) { - if (!sema_cast_const(expr)) + if (!sema_cast_const(expr) || !expr_is_const_initializer(expr)) { // Extract indexed types. Type *from_type = type_flatten(expr->type); @@ -2376,7 +2377,7 @@ static void cast_vecarr_to_slice(Expr *expr, Type *to_type) } UNREACHABLE_VOID } -static void cast_slice_to_vecarr(Expr *expr, Type *to_type) +static void cast_slice_to_arr(Expr *expr, Type *to_type) { if (!sema_cast_const(expr)) { @@ -2402,6 +2403,54 @@ static void cast_slice_to_vecarr(Expr *expr, Type *to_type) expr->type = to_type; } +static void expr_rewrite_bytes_to_const_initializer(Expr *expr, Type *target_type) +{ + Type *flat_vec = type_flatten(target_type); + Type *base = type_flatten(flat_vec->array.base); + ConstInitializer **inits = MALLOC(sizeof(ConstInitializer*) * expr->const_expr.bytes.len); + for (int i = 0; i < expr->const_expr.bytes.len; i++) + { + Expr *int_expr = expr_new_const_int(expr->span, base, (unsigned char)expr->const_expr.bytes.ptr[i]); + ConstInitializer *init = const_init_new_value(int_expr); + inits[i] = init; + } + ASSERT(inits); + Type *type = type_get_vector(base, flat_vec->type_kind, expr->const_expr.bytes.len); + ConstInitializer *slice = const_init_new_array_full(type, inits); + expr_rewrite_const_initializer(expr, type, slice); +} + +static void cast_slice_to_vec(Expr *expr, Type *to_type) +{ + if (!sema_cast_const(expr)) + { + switch (expr->expr_kind) + { + case EXPR_SLICE: + { + expr->inner_expr = expr_copy(expr); + expr->expr_kind = EXPR_SLICE_TO_VEC_ARRAY; + expr->type = to_type; + expr->resolve_status = RESOLVE_DONE; + return; + } + default: + UNREACHABLE_VOID; + } + } + if (expr_is_const_bytes(expr) || expr_is_const_string(expr)) + { + expr_rewrite_bytes_to_const_initializer(expr, to_type); + } + + if (expr_is_const_slice(expr)) + { + expr->const_expr.const_kind = CONST_INITIALIZER; + } + ASSERT(expr_is_const(expr)); + expr->type = to_type; +} + static void cast_slice_to_infer(Expr *expr, Type *to_type) { ArraySize len = sema_len_from_const(expr); @@ -2435,18 +2484,16 @@ static void cast_arr_to_vec(Expr *expr, Type *to_type) Type *to_temp = index_vec == index_arr ? to_type : type_get_vector(index_arr, to_type->canonical->type_kind, type_flatten(expr->type)->array.len); if (sema_cast_const(expr)) { - // For the array -> vector this is always a simple rewrite of type. if (expr->const_expr.const_kind == CONST_BYTES || expr->const_expr.const_kind == CONST_STRING) { - expr->type = to_temp; - } - else - { - ASSERT(expr->const_expr.const_kind == CONST_INITIALIZER); - ConstInitializer *list = expr->const_expr.initializer; - list->type = type_flatten(to_temp); - expr->type = to_temp; + expr_rewrite_bytes_to_const_initializer(expr, to_type); + expr->type = to_type; + return; } + ASSERT(expr->const_expr.const_kind == CONST_INITIALIZER); + ConstInitializer *list = expr->const_expr.initializer; + list->type = type_flatten(to_temp); + expr->type = to_temp; } else { @@ -2511,7 +2558,8 @@ static void cast_typeid_to_bool(Expr *expr, Type *to_type) { expr_rewrite_int_to #define TI2IN &cast_typeid_to_int #define TI2PT &cast_typeid_to_ptr #define AF2BO &cast_anyfault_to_bool -#define SL2VA &cast_slice_to_vecarr +#define SL2VC &cast_slice_to_vec +#define SL2AR &cast_slice_to_arr #define VA2SL &cast_vecarr_to_slice #define XX2VO &cast_all_to_void #define SL2FE &cast_slice_to_infer @@ -2596,7 +2644,7 @@ CastFunction cast_function[CONV_LAST + 1][CONV_LAST + 1] = { {XX2VO, 0, IN2BO, IN2IN, IN2FP, IN2PT, 0, EX2VC, IA2BS, 0, 0, 0, 0, 0, 0, IN2EN, 0, IN2PT, 0, 0, IN2PT, IN2PT, 0, 0 }, // INT {XX2VO, 0, FP2BO, FP2IN, FP2FP, 0, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // FLOAT {XX2VO, 0, PT2BO, PT2IN, 0, PT2PT, 0, EX2VC, 0, 0, 0, 0, 0, PT2AY, PT2AY, 0, 0, 0, 0, 0, PT2PT, PT2PT, PT2FE, 0 }, // PTR - {XX2VO, 0, SL2BO, 0, 0, SL2PT, SL2SL, SL2VA, 0, 0, SL2VA, 0, 0, 0, 0, 0, 0, 0, 0, 0, SL2PT, SL2PT, SL2FE, 0 }, // SLICE + {XX2VO, 0, SL2BO, 0, 0, SL2PT, SL2SL, SL2VC, 0, 0, SL2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, SL2PT, SL2PT, SL2FE, 0 }, // SLICE {XX2VO, 0, 0, 0, 0, 0, VA2SL, VC2VC, 0, 0, VC2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VA2FE, 0 }, // VECTOR {XX2VO, 0, BS2BO, BS2IA, 0, 0, 0, 0, 0, 0, BS2IA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BITSTRUCT { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // DISTINCT diff --git a/test/test_suite/slices/slice_to_vec_conv.c3t b/test/test_suite/slices/slice_to_vec_conv.c3t new file mode 100644 index 000000000..b8a33f921 --- /dev/null +++ b/test/test_suite/slices/slice_to_vec_conv.c3t @@ -0,0 +1,15 @@ +// #target: linux-x64 +module test; +long[<*>] a = x'dead'; +long[<*>] a2 = "hello world blah1"; +fn int main() +{ + a[0] = a2[0]; + return 0; +} + +/* #expect: test.ll + +@test.a = local_unnamed_addr global <2 x i64> , align 16 +@test.a2 = local_unnamed_addr global <17 x i64> , align 256 + diff --git a/test/test_suite/vector/vec_from_bytes.c3t b/test/test_suite/vector/vec_from_bytes.c3t new file mode 100644 index 000000000..abf3f706c --- /dev/null +++ b/test/test_suite/vector/vec_from_bytes.c3t @@ -0,0 +1,11 @@ +// #target: linux-x64 +module test; +long[<*>] a = x'dead'; +long[<*>] a2 = "hello world blah"; + +/* #expect: test.ll + +@test.a = local_unnamed_addr global <2 x i64> , align 16 +@test.a2 = local_unnamed_addr global <16 x i64> , align 128 + + diff --git a/test/test_suite/vector/vector_from_hex.c3t b/test/test_suite/vector/vector_from_hex.c3t index 449443565..ddc95fbe7 100644 --- a/test/test_suite/vector/vector_from_hex.c3t +++ b/test/test_suite/vector/vector_from_hex.c3t @@ -9,8 +9,7 @@ fn void main() } /* #expect: test.ll - -@test.fooy = local_unnamed_addr global [2 x i8] c"\DE\AD", align 2 +@test.fooy = local_unnamed_addr global <2 x i8> , align 2 @test.fooy2 = local_unnamed_addr global <2 x i8> , align 2 define void @test.main() #0 {