From 9b2fc0495963a13090a7353ff9f6e48990368e4e Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 24 Jan 2026 22:42:04 +0100 Subject: [PATCH] ompile time dereference of a constant slice was too generous #2821 --- releasenotes.md | 3 ++- src/compiler/sema_expr.c | 12 +++++++++++- .../expressions/deref_const_string.c3t | 11 +++-------- .../slices/deref_const_slice_cast.c3t | 17 +++++++++++++++++ 4 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 test/test_suite/slices/deref_const_slice_cast.c3t diff --git a/releasenotes.md b/releasenotes.md index 38bb6f391..0bd8a5537 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -120,7 +120,8 @@ - Generating typeid from function gives incorrect typeid #2816 - Recursive definitions not discovered when initializer is access on other const #2817 - Slice overrun detected late hit codegen assert #2822 - +- Compile time dereference of a constant slice was too generous #2821 + ### Stdlib changes - Add `ThreadPool` join function to wait for all threads to finish in the pool without destroying the threads. - Add `@in` compile-time macro to check for a value in a variable list of constants. #2662 diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 4444fc004..66483158b 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -8921,8 +8921,18 @@ static inline bool sema_expr_analyse_deref(SemaContext *context, Expr *expr, boo break; case CONST_BYTES: case CONST_STRING: - expr_rewrite_const_int(expr, type_get_indexed_type(inner->type), inner->const_expr.bytes.len ? inner->const_expr.bytes.ptr[0] : 0); + { + if (inner->type->type_kind != TYPE_POINTER) break; + Type *inner_type = type_get_indexed_type(inner->type); + if (!type_kind_is_any_integer(type_flatten(inner_type)->type_kind)) break; + if (!inner->const_expr.bytes.len) + { + RETURN_SEMA_ERROR(inner, "You cannot dereference an empty constant array or slice."); + } + expr_rewrite_const_int(expr, inner_type, inner->const_expr.bytes.ptr[0]); return true; + + } default: UNREACHABLE } diff --git a/test/test_suite/expressions/deref_const_string.c3t b/test/test_suite/expressions/deref_const_string.c3t index 84a889211..e20579af1 100644 --- a/test/test_suite/expressions/deref_const_string.c3t +++ b/test/test_suite/expressions/deref_const_string.c3t @@ -4,13 +4,11 @@ import std; const char *VOWS = "aeiou"; const char *CONS = "bcdfghjklmnpqrstvwxyz"; -const char *TEST = ""; fn int main (String[] args) { char a = *VOWS; char b = *CONS; - char c = *TEST; return 0; } @@ -18,12 +16,9 @@ fn int main (String[] args) @.str = private unnamed_addr constant [6 x i8] c"aeiou\00", align 1 @test.VOWS = local_unnamed_addr constant ptr @.str, align 8 -@.str.3 = private unnamed_addr constant [22 x i8] c"bcdfghjklmnpqrstvwxyz\00", align 1 -@test.CONS = local_unnamed_addr constant ptr @.str.3, align 8 -@.str.4 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 -@test.TEST = local_unnamed_addr constant ptr @.str.4, align 8 +@.str.2 = private unnamed_addr constant [22 x i8] c"bcdfghjklmnpqrstvwxyz\00", align 1 +@test.CONS = local_unnamed_addr constant ptr @.str.2, align 8 store i8 97, ptr %a, align 1 store i8 98, ptr %b, align 1 - store i8 0, ptr %c, align 1 - ret i32 0 + ret i32 0 \ No newline at end of file diff --git a/test/test_suite/slices/deref_const_slice_cast.c3t b/test/test_suite/slices/deref_const_slice_cast.c3t new file mode 100644 index 000000000..362879abe --- /dev/null +++ b/test/test_suite/slices/deref_const_slice_cast.c3t @@ -0,0 +1,17 @@ +// #target: macos-x64 +module test; +fn int main() +{ + char[*] b = *(char[5]*)""; + return 0; +} +/* #expect: test.ll + +@.str = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 + +define i32 @main() #0 { +entry: + %b = alloca [5 x i8], align 1 + call void @llvm.memcpy.p0.p0.i32(ptr align 1 %b, ptr align 1 @.str, i32 5, i1 false) + ret i32 0 +}