From 2b97d7d59c739493fd38c7f5e6e0351831611dac Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 18 Jun 2025 13:17:21 +0200 Subject: [PATCH] `x++` and `x--` works on pointer vectors #2222. --- releasenotes.md | 1 + src/compiler/llvm_codegen_expr.c | 15 +++-- src/compiler/sema_expr.c | 4 +- .../test_suite/expressions/vector_inc_dec.c3t | 65 +++++++++++++++++++ 4 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 test/test_suite/expressions/vector_inc_dec.c3t diff --git a/releasenotes.md b/releasenotes.md index 43ae2896f..5230fc178 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -49,6 +49,7 @@ - Method ambiguity when importing parent module publicly in private submodule. #2208 - Linker errors when shadowing @local with public function #2198 - Bug when offsetting pointers of large structs using ++ and --. +- `x++` and `x--` works on pointer vectors #2222. ### Stdlib changes - Deprecate `String.is_zstr` and `String.quick_zstr` #2188. diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index f9f4c679c..c955b1beb 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2048,15 +2048,21 @@ static inline LLVMValueRef llvm_emit_inc_dec_value(GenContext *c, SourceSpan spa } case TYPE_VECTOR: { - Type *element = type->array.base; + Type *element = type_lowering(type->array.base); LLVMValueRef diff_value; - bool is_integer = type_is_integer(element); + bool is_integer = type_kind_is_any_integer(element->type_kind); + bool is_ptr; if (is_integer) { diff_value = LLVMConstInt(llvm_get_type(c, element), 1, false); } + else if ((is_ptr = element->type_kind == TYPE_POINTER)) + { + diff_value = llvm_const_int(c, type_isz, diff); + } else { + ASSERT_AT(span, type_is_float(element)); diff_value = LLVMConstReal(llvm_get_type(c, element), diff); } ArraySize width = type->array.len; @@ -2071,10 +2077,11 @@ static inline LLVMValueRef llvm_emit_inc_dec_value(GenContext *c, SourceSpan spa ? llvm_emit_add_int(c, original->type, original->value, val, span) : llvm_emit_sub_int(c, original->type, original->value, val, span); } - else + if (is_ptr) { - return LLVMBuildFAdd(c->builder, original->value, val, "fincdec"); + return llvm_emit_ptradd_raw(c, original->value, val, type_size(element->pointer)); } + return LLVMBuildFAdd(c->builder, original->value, val, "fincdec"); } default: UNREACHABLE diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index dee0e6d08..a5f6ee3a4 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -8482,9 +8482,9 @@ static inline bool sema_expr_analyse_incdec(SemaContext *context, Expr *expr) Type *type = type_flatten(inner->type); // 5. We can only inc/dec numbers or pointers. - if (!type_underlying_may_add_sub(type)) + if (!type_underlying_may_add_sub(type) && type->type_kind != TYPE_VECTOR) { - RETURN_SEMA_ERROR(inner, "The expression must be a number or a pointer."); + RETURN_SEMA_ERROR(inner, "The expression must be a vector, enum, number or a pointer."); } if (inner->expr_kind == EXPR_SUBSCRIPT_ASSIGN) diff --git a/test/test_suite/expressions/vector_inc_dec.c3t b/test/test_suite/expressions/vector_inc_dec.c3t new file mode 100644 index 000000000..614d9137b --- /dev/null +++ b/test/test_suite/expressions/vector_inc_dec.c3t @@ -0,0 +1,65 @@ +// #target: macos-x64 +module test; +import std::io; +const FOO_SIZE = 500; + +alias Foo = char[FOO_SIZE]; + +fn int main(String[] args) +{ + const NUM = 10; + + Foo* x = calloc(Foo.sizeof * NUM); + Foo*[<1>] z = { x }; + Foo*[<1>] g = { x }; + for (int i = 0; i < 10; i++) + { + z++; + g--; + } + return 0; +} + +/* #expect: test.ll + +define i32 @test.main(ptr %0, i64 %1) #0 { +entry: + %args = alloca %"char[][]", align 8 + %x = alloca ptr, align 8 + %z = alloca <1 x ptr>, align 8 + %g = alloca <1 x ptr>, align 8 + %i = alloca i32, align 4 + store ptr %0, ptr %args, align 8 + %ptradd = getelementptr inbounds i8, ptr %args, i64 8 + store i64 %1, ptr %ptradd, align 8 + %2 = call ptr @std.core.mem.calloc(i64 5000) #3 + store ptr %2, ptr %x, align 8 + %3 = load ptr, ptr %x, align 8 + %4 = insertelement <1 x ptr> undef, ptr %3, i64 0 + store <1 x ptr> %4, ptr %z, align 8 + %5 = load ptr, ptr %x, align 8 + %6 = insertelement <1 x ptr> undef, ptr %5, i64 0 + store <1 x ptr> %6, ptr %g, align 8 + store i32 0, ptr %i, align 4 + br label %loop.cond + +loop.cond: ; preds = %loop.body, %entry + %7 = load i32, ptr %i, align 4 + %lt = icmp slt i32 %7, 10 + br i1 %lt, label %loop.body, label %loop.exit + +loop.body: ; preds = %loop.cond + %8 = load <1 x ptr>, ptr %z, align 8 + %ptroffset_any = getelementptr [500 x i8], <1 x ptr> %8, <1 x i64> + store <1 x ptr> %ptroffset_any, ptr %z, align 8 + %9 = load <1 x ptr>, ptr %g, align 8 + %ptroffset_any1 = getelementptr [500 x i8], <1 x ptr> %9, <1 x i64> + store <1 x ptr> %ptroffset_any1, ptr %g, align 8 + %10 = load i32, ptr %i, align 4 + %add = add i32 %10, 1 + store i32 %add, ptr %i, align 4 + br label %loop.cond + +loop.exit: ; preds = %loop.cond + ret i32 0 +}