From 362d5680e49ae0cc2f4b242720632358b8e3ea07 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 28 Jan 2026 21:59:40 +0100 Subject: [PATCH] - Optional in initializer cause a crash #2864 - Negating a global address with offset was a counted as a global runtime constant #2865 - Converting static "make_slice" to array failed to be handled #2866 - Narrowing a not expression was incorrectly handled #2867 - Vector shift by optional scalar failed #2868 --- releasenotes.md | 5 ++ src/compiler/expr.c | 2 +- src/compiler/llvm_codegen_expr.c | 6 ++ src/compiler/sema_casts.c | 7 +- src/compiler/sema_expr.c | 1 + src/compiler/types.c | 6 +- .../slice_conversion_from_make_slice.c3t | 36 +++++++++ .../compile_time_introspection/kindof_fn.c3 | 8 ++ .../expressions/casts/narrowing_bool.c3t | 31 ++++++++ .../expressions/optional_in_initializer.c3t | 75 +++++++++++++++++++ .../vector/vector_shift_by_opt_scalar.c3t | 42 +++++++++++ 11 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 test/test_suite/arrays/slice_conversion_from_make_slice.c3t create mode 100644 test/test_suite/compile_time_introspection/kindof_fn.c3 create mode 100644 test/test_suite/expressions/casts/narrowing_bool.c3t create mode 100644 test/test_suite/expressions/optional_in_initializer.c3t create mode 100644 test/test_suite/vector/vector_shift_by_opt_scalar.c3t diff --git a/releasenotes.md b/releasenotes.md index ea22e4593..60750a109 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -141,6 +141,11 @@ - Crashes when using `io::EOF~!` in various unhandled places. #2848 - Crash when trying to create a const zero untyped list #2847 - Incorrect handling when reporting fn with optional compile time type #2862 +- Optional in initializer cause a crash #2864 +- Negating a global address with offset was a counted as a global runtime constant #2865 +- Converting static "make_slice" to array failed to be handled #2866 +- Narrowing a not expression was incorrectly handled #2867 +- Vector shift by optional scalar failed #2868 ### 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/expr.c b/src/compiler/expr.c index 25dd829a8..07e13b8a8 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -379,7 +379,7 @@ bool expr_is_runtime_const(Expr *expr) } return exprid_is_runtime_const(expr->builtin_access_expr.inner); case EXPR_INT_TO_BOOL: - return expr_is_runtime_const(expr->int_to_bool_expr.inner); + return expr_is_const(expr->int_to_bool_expr.inner); case EXPR_EXT_TRUNC: return expr_is_runtime_const(expr->ext_trunc_expr.inner); case EXPR_CONST: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 58565ac2e..cf2f68a7a 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -193,6 +193,12 @@ BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *ref_expr, Expr BEValue val = llvm_emit_alloca_b(c, type, ".assign_list"); llvm_emit_initialize_reference(c, &val, expr); if (ref_expr) llvm_emit_expr(c, ref, ref_expr); + ASSERT(!llvm_is_global_eval(c)); + if (!c->current_block) + { + llvm_value_set_empty(ref); + return *ref; + } llvm_store(c, ref, &val); } value = *ref; diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 8287dc6a4..878b84686 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -537,9 +537,8 @@ RETRY: { case UNARYOP_ERROR: case UNARYOP_ADDR: - case UNARYOP_NOT: case UNARYOP_TADDR: - UNREACHABLE + UNREACHABLE; case UNARYOP_DEREF: // Check sizes. goto CHECK_SIZE; @@ -550,6 +549,9 @@ RETRY: case UNARYOP_DEC: expr = expr->unary_expr.expr; goto RETRY; + case UNARYOP_NOT: + goto CHECK_SIZE; + } } default: @@ -2384,6 +2386,7 @@ static void cast_slice_to_arr(Expr *expr, Type *to_type) switch (expr->expr_kind) { case EXPR_SLICE: + case EXPR_MAKE_SLICE: { expr->inner_expr = expr_copy(expr); expr->expr_kind = EXPR_SLICE_TO_VEC_ARRAY; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 5645fe5eb..080c68ff7 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -6010,6 +6010,7 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr, ASSERT_SPAN(expr, type == type->canonical); ASSERT_SPAN(expr, sema_type_property_is_valid_for_type(type, property)); Type *flat = type_flatten(type); + if (type->type_kind == TYPE_FUNC_RAW) type = type_get_func_ptr(type); switch (property) { case TYPE_PROPERTY_INF: diff --git a/src/compiler/types.c b/src/compiler/types.c index 1ddc75bd9..4a37cd558 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1373,7 +1373,11 @@ Type *type_get_vector_bool(Type *original_type, TypeKind kind) Type *type_get_vector_from_vector(Type *base_type, Type *orginal_vector) { ASSERT(type_kind_is_real_vector(orginal_vector->type_kind)); - return type_get_vector(base_type, orginal_vector->type_kind, orginal_vector->array.len); + bool opt = type_is_optional(base_type); + if (opt) base_type = type_no_optional(base_type); + Type *res = type_get_vector(base_type, orginal_vector->type_kind, orginal_vector->array.len); + if (opt) res = type_get_optional(res); + return res; } Type *type_get_simd_from_vector(Type *orginal_vector) diff --git a/test/test_suite/arrays/slice_conversion_from_make_slice.c3t b/test/test_suite/arrays/slice_conversion_from_make_slice.c3t new file mode 100644 index 000000000..9ddf9618a --- /dev/null +++ b/test/test_suite/arrays/slice_conversion_from_make_slice.c3t @@ -0,0 +1,36 @@ +// #target: macos-x64 +module test; +import std; +fn int[3] test() +{ + int[3] x = io::EOF~!!; + return (int[])&x; +} +fn int main() +{ + test(); + return 0; +} + +/* #expect: test.ll + + +define { i64, i32 } @test.test() #0 { +entry: + %x = alloca [3 x i32], align 4 + %error_var = alloca i64, align 8 + %varargslots = alloca [1 x %any], align 16 + %indirectarg = alloca %"any[]", align 8 + store i64 ptrtoint (ptr @std.io.EOF to i64), ptr %error_var, align 8 + br label %panic_block + +panic_block: ; preds = %entry + %0 = insertvalue %any undef, ptr %error_var, 0 + %1 = insertvalue %any %0, i64 ptrtoint (ptr @"$ct.fault" to i64), 1 + store %any %1, ptr %varargslots, align 16 + %2 = insertvalue %"any[]" undef, ptr %varargslots, 0 + %"$$temp" = insertvalue %"any[]" %2, i64 1, 1 + store %"any[]" %"$$temp", ptr %indirectarg, align 8 + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 35, ptr @.func, i64 4, i32 5, ptr byval(%"any[]") align 8 %indirectarg) #2 + unreachable +} \ No newline at end of file diff --git a/test/test_suite/compile_time_introspection/kindof_fn.c3 b/test/test_suite/compile_time_introspection/kindof_fn.c3 new file mode 100644 index 000000000..343609bbc --- /dev/null +++ b/test/test_suite/compile_time_introspection/kindof_fn.c3 @@ -0,0 +1,8 @@ +<* @require main.kindof == SIGNED_INT *> +struct Foo +{} + +fn int main() +{ + Foo{double} d; // #error: Parameter(s) failed validation: @require "main.kindof == SIGNED_INT" violated +} \ No newline at end of file diff --git a/test/test_suite/expressions/casts/narrowing_bool.c3t b/test/test_suite/expressions/casts/narrowing_bool.c3t new file mode 100644 index 000000000..6c57eb67e --- /dev/null +++ b/test/test_suite/expressions/casts/narrowing_bool.c3t @@ -0,0 +1,31 @@ +// #target: macos-x64 +module test; +fn void main() +{ + ulong[1] haystack; + ulong[1] needle; + uint is_found = 0; + is_found += (ulong)~!(haystack[0] == needle[0]); +} +/* #expect: test.ll + +define void @test.main() #0 { +entry: + %haystack = alloca [1 x i64], align 8 + %needle = alloca [1 x i64], align 8 + %is_found = alloca i32, align 4 + store i64 0, ptr %haystack, align 8 + store i64 0, ptr %needle, align 8 + store i32 0, ptr %is_found, align 4 + %0 = load i32, ptr %is_found, align 4 + %1 = load i64, ptr %haystack, align 8 + %2 = load i64, ptr %needle, align 8 + %eq = icmp eq i64 %1, %2 + %not = xor i1 %eq, true + %bnot = xor i1 %not, true + %zext = zext i1 %bnot to i64 + %trunc = trunc i64 %zext to i32 + %add = add i32 %0, %trunc + store i32 %add, ptr %is_found, align 4 + ret void +} diff --git a/test/test_suite/expressions/optional_in_initializer.c3t b/test/test_suite/expressions/optional_in_initializer.c3t new file mode 100644 index 000000000..9e6b17d42 --- /dev/null +++ b/test/test_suite/expressions/optional_in_initializer.c3t @@ -0,0 +1,75 @@ +// #target: macos-x64 +module abc ; +import std::io, std::collections::list; +struct TextTemplate +{ + Allocator allocator; + String template; +} +fn void? TextTemplate.init(&self, String , String tag_start = "", String tag_end = "", Allocator using ) +{ + *self = { .allocator = using, .template = io::EOF~!, }; +} +module text_test; +import abc; +alias FooTmpl = TextTemplate{Foo}; +alias BarTmpl = TextTemplate{Bar}; +struct Foo { BarTmpl bar; } +struct Bar { String bar; } +fn void main() +{ + String foo_tmpl = ""; + FooTmpl ft; + ft.init(foo_tmpl, using: tmem)!!; +} + +/* #expect: abc.ll + +@std.io.EOF = linkonce constant %"char[]" { ptr @std.io.EOF.nameof, i64 7 }, align 8 +@std.io.EOF.nameof = internal constant [8 x i8] c"io::EOF\00", align 1 + +define weak i64 @"abc.TextTemplate$text_test.Foo$.init"(ptr %0, ptr %1, i64 %2, ptr %3, i64 %4, ptr byval(%"char[]") align 8 %5, ptr byval(%any) align 8 %6) #0 { +entry: + %.anon = alloca %"char[]", align 8 + %tag_start = alloca %"char[]", align 8 + %.assign_list = alloca %"TextTemplate{Foo}", align 8 + %error_var = alloca i64, align 8 + store ptr %1, ptr %.anon, align 8 + %ptradd = getelementptr inbounds i8, ptr %.anon, i64 8 + store i64 %2, ptr %ptradd, align 8 + store ptr %3, ptr %tag_start, align 8 + %ptradd1 = getelementptr inbounds i8, ptr %tag_start, i64 8 + store i64 %4, ptr %ptradd1, align 8 + call void @llvm.memset.p0.i64(ptr align 8 %.assign_list, i8 0, i64 32, i1 false) + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %.assign_list, ptr align 8 %6, i32 16, i1 false) + %ptradd2 = getelementptr inbounds i8, ptr %.assign_list, i64 16 + store i64 ptrtoint (ptr @std.io.EOF to i64), ptr %error_var, align 8 + br label %guard_block + +guard_block: ; preds = %entry + %7 = load i64, ptr %error_var, align 8 + ret i64 %7 +} + +define weak i64 @"abc.TextTemplate$text_test.Bar$.init"(ptr %0, ptr %1, i64 %2, ptr %3, i64 %4, ptr byval(%"char[]") align 8 %5, ptr byval(%any) align 8 %6) #0 { +entry: + %.anon = alloca %"char[]", align 8 + %tag_start = alloca %"char[]", align 8 + %.assign_list = alloca %"TextTemplate{Bar}", align 8 + %error_var = alloca i64, align 8 + store ptr %1, ptr %.anon, align 8 + %ptradd = getelementptr inbounds i8, ptr %.anon, i64 8 + store i64 %2, ptr %ptradd, align 8 + store ptr %3, ptr %tag_start, align 8 + %ptradd1 = getelementptr inbounds i8, ptr %tag_start, i64 8 + store i64 %4, ptr %ptradd1, align 8 + call void @llvm.memset.p0.i64(ptr align 8 %.assign_list, i8 0, i64 32, i1 false) + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %.assign_list, ptr align 8 %6, i32 16, i1 false) + %ptradd2 = getelementptr inbounds i8, ptr %.assign_list, i64 16 + store i64 ptrtoint (ptr @std.io.EOF to i64), ptr %error_var, align 8 + br label %guard_block + +guard_block: ; preds = %entry + %7 = load i64, ptr %error_var, align 8 + ret i64 %7 +} diff --git a/test/test_suite/vector/vector_shift_by_opt_scalar.c3t b/test/test_suite/vector/vector_shift_by_opt_scalar.c3t new file mode 100644 index 000000000..4a1d036e6 --- /dev/null +++ b/test/test_suite/vector/vector_shift_by_opt_scalar.c3t @@ -0,0 +1,42 @@ +// #target: macos-x64 +module test; +typedef Foo = inline int; +fn int main() +{ + Foo? u = 2; + int[<2>]? f4 = (int[<2>]) { 1, 2 } << u; + return 0; +} + +/* #expect: test.ll + +define i32 @main() #0 { +entry: + %u = alloca i32, align 4 + %u.f = alloca i64, align 8 + %f4 = alloca <2 x i32>, align 8 + %f4.f = alloca i64, align 8 + store i32 2, ptr %u, align 4 + store i64 0, ptr %u.f, align 8 + %optval = load i64, ptr %u.f, align 8 + %not_err = icmp eq i64 %optval, 0 + %0 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %0, label %after_check, label %assign_optional + +assign_optional: ; preds = %entry + store i64 %optval, ptr %f4.f, align 8 + br label %after_assign + +after_check: ; preds = %entry + %1 = load i32, ptr %u, align 4 + %2 = insertelement <2 x i32> undef, i32 %1, i64 0 + %3 = insertelement <2 x i32> %2, i32 %1, i64 1 + %shl = shl <2 x i32> , %3 + %4 = freeze <2 x i32> %shl + store <2 x i32> %4, ptr %f4, align 8 + store i64 0, ptr %f4.f, align 8 + br label %after_assign + +after_assign: ; preds = %after_check, %assign_optional + ret i32 0 +}