From aa425a088621156ebe1bf7a57f5822b71aa94a71 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 18 Jun 2025 22:27:30 +0200 Subject: [PATCH] Fixes to `x += { 1, 1 }` for enum and pointer vectors #2222. --- releasenotes.md | 1 + src/compiler/compiler_internal.h | 18 ++-- src/compiler/sema_expr.c | 84 +++++++++---------- .../enumerations/inc_assign_enum_vector.c3t | 63 ++++++++++++++ .../expressions/vector_ptr.assign_add_sub.c3t | 40 +++++---- 5 files changed, 140 insertions(+), 66 deletions(-) create mode 100644 test/test_suite/enumerations/inc_assign_enum_vector.c3t diff --git a/releasenotes.md b/releasenotes.md index 086993d0c..c8f2de309 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -51,6 +51,7 @@ - Bug when offsetting pointers of large structs using ++ and --. - `x++` and `x--` works on pointer vectors #2222. - `x += 1` and `x -= 1` works propertly on pointer vectors #2222. +- Fixes to `x += { 1, 1 }` for enum and pointer vectors #2222. ### Stdlib changes - Deprecate `String.is_zstr` and `String.quick_zstr` #2188. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 87467c024..418e2ce3d 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -3172,10 +3172,19 @@ INLINE bool type_is_number(Type *type) static inline Type *type_flat_for_arithmethics(Type *type) { - do + while (true) { type = type->canonical; - if (type->type_kind != TYPE_DISTINCT) break; + switch (type->type_kind) + { + case TYPE_OPTIONAL: + type = type->optional; + continue; + case TYPE_DISTINCT: + break; + default: + return type; + } Decl *decl = type->decl; Type *inner = decl->distinct->type; if (decl->is_substruct) @@ -3185,9 +3194,8 @@ static inline Type *type_flat_for_arithmethics(Type *type) } inner = type_flat_for_arithmethics(inner); if (type_is_number_or_bool(inner)) return inner; - break; - } while (1); - return type; + return type; + } } INLINE bool type_is_numeric(Type *type) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 5f161bc9f..d53284371 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -6642,6 +6642,40 @@ AFTER_ADDR:; expr_rewrite_two(expr, init, binary); return sema_analyse_expr(context, expr); } + +static bool sema_expr_analyse_op_assign_enum_ptr(SemaContext *context, Expr *rhs, Type *flat, Type *base, Type *flat_rhs, bool is_enum, BinaryOp op) +{ + if (flat == base) + { + if (!type_is_integer(flat_rhs)) + { + RETURN_SEMA_ERROR(rhs, + "The right side was '%s' but only integers are valid on the right side of %s when the left side is %s.", + type_to_error_string(rhs->type), + token_type_to_string(binaryop_to_token(op)), is_enum ? "an enum" : "a pointer"); + } + Type *to = is_enum ? flat->decl->enums.type_info->type : type_isz; + if (!cast_implicit(context, rhs, to, true)) return false; + } + else + { + Type *real_type = type_get_vector(is_enum ? base->decl->enums.type_info->type : type_isz, flat->array.len); + if (flat_rhs == type_untypedlist) + { + if (!cast_implicit(context, rhs, real_type, true)) return false; + flat_rhs = type_flat_for_arithmethics(rhs->type); + } + if (!type_is_integer(flat_rhs) && (flat_rhs->type_kind != TYPE_VECTOR || !type_is_integer(flat_rhs->array.base))) + { + RETURN_SEMA_ERROR(rhs, + "The right side was '%s' but only integers or integer vectors are valid on the right side of %s when the left side is %s.", + type_to_error_string(rhs->type), + token_type_to_string(binaryop_to_token(op)), is_enum ? "an enum vector" : "a pointer vector"); + } + if (!cast_implicit(context, rhs, real_type, true)) return false; + } + return true; +} /** * Analyse *= /= %= ^= |= &= += -= <<= >>= * @@ -6761,7 +6795,7 @@ SKIP_OVERLOAD_CHECK:; BITSTRUCT_OK: // 5. Analyse RHS - if (flat->type_kind == TYPE_ENUM || flat->type_kind == TYPE_POINTER) + if (base->type_kind == TYPE_ENUM || base->type_kind == TYPE_POINTER ) { if (!sema_analyse_expr(context, right)) return false; } @@ -6779,57 +6813,17 @@ BITSTRUCT_OK: expr->type = left->type; bool optional = IS_OPTIONAL(left) || IS_OPTIONAL(right); - Type *type_rhs_inline = type_flat_distinct_inline(type_no_optional(right->type->canonical)); + Type *type_rhs_inline = type_flat_for_arithmethics(right->type); - // 5. In the pointer case we have to treat this differently. + // 5. In the enum case we have to treat this differently. if (base->type_kind == TYPE_ENUM) { - // 7. Finally, check that the right side is indeed an integer. - if (flat == base) - { - if (!type_is_integer(type_rhs_inline)) - { - RETURN_SEMA_ERROR(right, - "The right side was '%s' but only integers are valid on the right side of %s when the left side is an enum.", - type_to_error_string(right->type), - token_type_to_string(binaryop_to_token(expr->binary_expr.operator))); - } - } - else - { - if (!type_is_integer(type_rhs_inline) && (type_rhs_inline->type_kind != TYPE_VECTOR || !type_is_integer(type_rhs_inline->array.base))) - { - RETURN_SEMA_ERROR(right, - "The right side was '%s' but only integers or integer vectors are valid on the right side of %s when the left side is an enum vector.", - type_to_error_string(right->type), - token_type_to_string(binaryop_to_token(expr->binary_expr.operator))); - } - } - if (!cast_implicit(context, right, flat->decl->enums.type_info->type, false)) return false; + if (!sema_expr_analyse_op_assign_enum_ptr(context, right, flat, base, type_rhs_inline, true, expr->binary_expr.operator)) return false; goto END; } if (base->type_kind == TYPE_POINTER) { - if (flat == base) - { - if (!type_is_integer(type_rhs_inline)) - { - RETURN_SEMA_ERROR(right, - "The right side was '%s' but only integers are valid on the right side of %s when the left side is a pointer.", - type_to_error_string(right->type), - token_type_to_string(binaryop_to_token(expr->binary_expr.operator))); - } - } - else - { - if (!type_is_integer(type_rhs_inline) && (type_rhs_inline->type_kind != TYPE_VECTOR || !type_is_integer(type_rhs_inline->array.base))) - { - RETURN_SEMA_ERROR(right, - "The right side was '%s' but only integer vectors are valid on the right side of %s when the left side is a pointer vector.", - type_to_error_string(right->type), - token_type_to_string(binaryop_to_token(expr->binary_expr.operator))); - } - } + if (!sema_expr_analyse_op_assign_enum_ptr(context, right, flat, base, type_rhs_inline, false, expr->binary_expr.operator)) return false; goto END; } diff --git a/test/test_suite/enumerations/inc_assign_enum_vector.c3t b/test/test_suite/enumerations/inc_assign_enum_vector.c3t new file mode 100644 index 000000000..27c658623 --- /dev/null +++ b/test/test_suite/enumerations/inc_assign_enum_vector.c3t @@ -0,0 +1,63 @@ +// #target: macos-x64 +module test; +import std::io; +enum Foo +{ + ABC, + DEF, + GHK, + LDV, + HELLO, +} +fn int main() +{ + Foo[<2>] x = { ABC, DEF }; + for (int i = 0; i < 10; i++) + { + x++; + x += 1; + x -= 1; + x += { 1, 1 }; + x -= { 1, 1 }; + } + return 0; +} + +/* #expect: test.ll + +entry: + %x = alloca <2 x i32>, align 8 + %i = alloca i32, align 4 + store <2 x i32> , ptr %x, align 8 + store i32 0, ptr %i, align 4 + br label %loop.cond + +loop.cond: ; preds = %loop.body, %entry + %0 = load i32, ptr %i, align 4 + %lt = icmp slt i32 %0, 10 + br i1 %lt, label %loop.body, label %loop.exit + +loop.body: ; preds = %loop.cond + %1 = load <2 x i32>, ptr %x, align 8 + %add = add <2 x i32> %1, + store <2 x i32> %add, ptr %x, align 8 + %2 = load <2 x i32>, ptr %x, align 8 + %add1 = add <2 x i32> %2, + store <2 x i32> %add1, ptr %x, align 8 + %3 = load <2 x i32>, ptr %x, align 8 + %sub = sub <2 x i32> %3, + store <2 x i32> %sub, ptr %x, align 8 + %4 = load <2 x i32>, ptr %x, align 8 + %add2 = add <2 x i32> %4, + store <2 x i32> %add2, ptr %x, align 8 + %5 = load <2 x i32>, ptr %x, align 8 + %sub3 = sub <2 x i32> %5, + store <2 x i32> %sub3, ptr %x, align 8 + %6 = load i32, ptr %i, align 4 + %add4 = add i32 %6, 1 + store i32 %add4, ptr %i, align 4 + br label %loop.cond + +loop.exit: ; preds = %loop.cond + ret i32 0 +} \ No newline at end of file diff --git a/test/test_suite/expressions/vector_ptr.assign_add_sub.c3t b/test/test_suite/expressions/vector_ptr.assign_add_sub.c3t index ca40dbf67..f177bf9ef 100644 --- a/test/test_suite/expressions/vector_ptr.assign_add_sub.c3t +++ b/test/test_suite/expressions/vector_ptr.assign_add_sub.c3t @@ -24,6 +24,8 @@ fn int main() g2 = g2 - 1; z++; g--; + g2 += { 1, 1 }; + g2 -= { 1, 1 }; assert(z2.x == z.x); assert(g2.x == g.x); } @@ -74,10 +76,10 @@ loop.body: ; preds = %loop.cond %ptroffset_any1 = getelementptr [500 x i8], <1 x ptr> %15, <1 x i64> store <1 x ptr> %ptroffset_any1, ptr %g, align 8 %16 = load <2 x ptr>, ptr %z2, align 16 - %ptradd_any = getelementptr i8, <2 x ptr> %16, i32 500 + %ptradd_any = getelementptr i8, <2 x ptr> %16, <2 x i64> store <2 x ptr> %ptradd_any, ptr %z2, align 16 %17 = load <2 x ptr>, ptr %g2, align 16 - %ptradd_any2 = getelementptr i8, <2 x ptr> %17, i32 -500 + %ptradd_any2 = getelementptr i8, <2 x ptr> %17, <2 x i64> store <2 x ptr> %ptradd_any2, ptr %g2, align 16 %18 = load <2 x ptr>, ptr %z2, align 16 %ptradd_any3 = getelementptr i8, <2 x ptr> %18, <2 x i64> @@ -91,23 +93,29 @@ loop.body: ; preds = %loop.cond %21 = load <1 x ptr>, ptr %g, align 8 %ptroffset_any6 = getelementptr [500 x i8], <1 x ptr> %21, <1 x i64> store <1 x ptr> %ptroffset_any6, ptr %g, align 8 - %22 = load <2 x ptr>, ptr %z2, align 16 - %23 = extractelement <2 x ptr> %22, i64 0 - %24 = load <1 x ptr>, ptr %z, align 8 - %25 = extractelement <1 x ptr> %24, i64 0 - %eq = icmp eq ptr %23, %25 + %22 = load <2 x ptr>, ptr %g2, align 16 + %ptradd_any7 = getelementptr i8, <2 x ptr> %22, <2 x i64> + store <2 x ptr> %ptradd_any7, ptr %g2, align 16 + %23 = load <2 x ptr>, ptr %g2, align 16 + %ptradd_any8 = getelementptr i8, <2 x ptr> %23, <2 x i64> + store <2 x ptr> %ptradd_any8, ptr %g2, align 16 + %24 = load <2 x ptr>, ptr %z2, align 16 + %25 = extractelement <2 x ptr> %24, i64 0 + %26 = load <1 x ptr>, ptr %z, align 8 + %27 = extractelement <1 x ptr> %26, i64 0 + %eq = icmp eq ptr %25, %27 call void @llvm.assume(i1 %eq) - %26 = load <2 x ptr>, ptr %g2, align 16 - %27 = extractelement <2 x ptr> %26, i64 0 - %28 = load <1 x ptr>, ptr %g, align 8 - %29 = extractelement <1 x ptr> %28, i64 0 - %eq7 = icmp eq ptr %27, %29 - call void @llvm.assume(i1 %eq7) - %30 = load i32, ptr %i, align 4 - %add = add i32 %30, 1 + %28 = load <2 x ptr>, ptr %g2, align 16 + %29 = extractelement <2 x ptr> %28, i64 0 + %30 = load <1 x ptr>, ptr %g, align 8 + %31 = extractelement <1 x ptr> %30, i64 0 + %eq9 = icmp eq ptr %29, %31 + call void @llvm.assume(i1 %eq9) + %32 = load i32, ptr %i, align 4 + %add = add i32 %32, 1 store i32 %add, ptr %i, align 4 br label %loop.cond loop.exit: ; preds = %loop.cond ret i32 0 -} +} \ No newline at end of file