diff --git a/releasenotes.md b/releasenotes.md index 9dc6aa12c..de306a5b8 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -20,7 +20,7 @@ - Support &a[0] returning the distinct type when applying it to a distinct of a pointer. - Fix error when calling `HashMap.remove` on uninitialized `HashMap`. - Fix issue with resolved try-unwrap in defer. -- Fix issue with overloaded subscript and ++/--. +- Fix issue with overloaded subscript and ++/-- and assign ops (e.g. `*=`) - Fix issue with properties in different targets not being respected #1633. - Indexing an Optional slice would crash in codegen #1636. - SimpleHeapAllocator bug when splitting blocks allowed memory overrun. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index ce0c80fe1..68c1add6b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -3072,7 +3072,7 @@ INLINE bool type_underlying_is_numeric(Type *type) INLINE bool type_underlying_may_add_sub(Type *type) { type = type_flatten(type); - return type->type_kind == TYPE_ENUM || type_is_numeric(type_flatten(type)); + return type->type_kind == TYPE_ENUM || type->type_kind == TYPE_POINTER || type_is_numeric(type_flatten(type)); } INLINE bool type_flat_is_vector(Type *type) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index f4dc0ee69..e2a67cb21 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -79,7 +79,8 @@ static bool sema_expr_analyse_ct_identifier_assign(SemaContext *context, Expr *e static bool sema_expr_analyse_ct_type_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right); static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right); static bool sema_expr_analyse_comp(SemaContext *context, Expr *expr, Expr *left, Expr *right); -static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool int_only, bool allow_bitstruct); +static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool int_only, + bool allow_bitstruct, bool is_add_sub); // -- unary static inline bool sema_expr_analyse_addr(SemaContext *context, Expr *expr, bool *failed_ref, CheckType check); @@ -110,6 +111,7 @@ static inline const char *sema_addr_may_take_of_ident(Expr *inner); // -- subscript helpers static bool sema_subscript_rewrite_index_const_list(Expr *const_list, ArraySize index, bool from_back, Expr *result); static Type *sema_subscript_find_indexable_type_recursively(Type **type, Expr **parent); +static bool sema_analyse_assign_mutate_overloaded_subscript(SemaContext *context, Expr *main, Expr *subscript_expr, Type *type); // -- binary helper functions static void expr_binary_unify_failability(Expr *expr, Expr *left, Expr *right); @@ -176,7 +178,7 @@ static inline bool sema_identifier_find_possible_inferred(SemaContext *context, static inline bool sema_expr_analyse_enum_constant(SemaContext *context, Expr *expr, const char *name, Decl *decl); static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr); -static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr); +static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr, bool mutate); static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *expr, Type *parent_type, Expr *identifier, bool *missing_ref); static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *expr, Expr *parent, Expr *identifier, bool *missing_ref); @@ -2995,7 +2997,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, expr_replace(expr, current_expr->const_expr.untyped_list[index_value]); return true; } - if (!sema_cast_rvalue(context, subscripted)) return false; + if (!sema_cast_rvalue(context, subscripted, true)) return false; if (overload) { @@ -4846,7 +4848,7 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr, bo Type *underlying_type = type_no_optional(parent->type)->canonical; if (underlying_type->type_kind == TYPE_POINTER && underlying_type != type_voidptr) { - if (!sema_cast_rvalue(context, parent)) return false; + if (!sema_cast_rvalue(context, parent, true)) return false; expr_rewrite_insert_deref(expr->access_expr.parent); parent = expr->access_expr.parent; } @@ -5610,11 +5612,12 @@ static bool sema_binary_analyse_ct_common_assign(SemaContext *context, Expr *exp } /** - * Analyse *= /= %= ^= |= &= + * Analyse *= /= %= ^= |= &= += -= * * @return true if analysis worked. */ -static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool int_only, bool allow_bitstruct) +static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool int_only, + bool allow_bitstruct, bool is_add_sub) { if (left->expr_kind == EXPR_CT_IDENT) { @@ -5627,6 +5630,11 @@ static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr * // 2. Verify that the left side is assignable. if (!sema_expr_check_assign(context, left)) return false; + Type *left_type_canonical = left->type->canonical; + + // 3. Check that it is readable + if (!sema_cast_rvalue(context, left, false)) return false; + Type *no_fail = type_no_optional(left->type); Type *flat = type_flatten(no_fail); @@ -5634,25 +5642,53 @@ static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr * if (int_only && !type_flat_is_intlike(flat)) { if (allow_bitstruct && flat->type_kind == TYPE_BITSTRUCT) goto BITSTRUCT_OK; - SEMA_ERROR(left, "Expected an integer here."); - return false; + RETURN_SEMA_ERROR(left, "Expected an integer here."); } // 4. In any case, these ops are only defined on numbers. - if (!type_underlying_is_numeric(flat)) + if (!type_underlying_is_numeric(flat) && !(is_add_sub && type_underlying_may_add_sub(left->type))) { - SEMA_ERROR(left, "Expected a numeric type here."); - return false; + RETURN_SEMA_ERROR(left, "Expected a numeric type here."); } BITSTRUCT_OK: - // 5. Cast the right hand side to the one on the left + + // 5. Analyse RHS if (!sema_analyse_expr(context, right)) return false; - if (!cast_implicit_binary(context, right, no_fail, false)) return false; + + + // 3. Copy type & set properties. if (IS_OPTIONAL(right) && !IS_OPTIONAL(left)) { - RETURN_SEMA_ERROR(right, "The expression may not be optional."); + RETURN_SEMA_ERROR(right, "Cannot assign an optional value to a non-optional."); } + + expr->type = left->type; + bool optional = IS_OPTIONAL(left) || IS_OPTIONAL(right); + + // 5. In the pointer case we have to treat this differently. + if (left_type_canonical->type_kind == TYPE_POINTER) + { + // 7. Finally, check that the right side is indeed an integer. + if (!type_is_integer(right->type->canonical)) + { + 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))); + } + goto END; + } + + if (flat->type_kind == TYPE_ENUM) + { + if (!cast_implicit(context, right, type_base(flat), false)) return false; + goto END; + } + + // Otherwise cast left to right. + if (!cast_implicit_binary(context, right, no_fail, false)) return false; + // 6. Check for zero in case of div or mod. if (sema_cast_const(right)) { @@ -5661,18 +5697,10 @@ static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr * switch (right->const_expr.const_kind) { case CONST_INTEGER: - if (int_is_zero(right->const_expr.ixx)) - { - SEMA_ERROR(right, "Division by zero not allowed."); - return false; - } + if (int_is_zero(right->const_expr.ixx)) RETURN_SEMA_ERROR(right, "Division by zero not allowed."); break; case CONST_FLOAT: - if (right->const_expr.fxx.f == 0) - { - SEMA_ERROR(right, "Division by zero not allowed."); - return false; - } + if (right->const_expr.fxx.f == 0) RETURN_SEMA_ERROR(right, "Division by zero not allowed."); break; default: break; @@ -5685,8 +5713,7 @@ static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr * case CONST_INTEGER: if (int_is_zero(right->const_expr.ixx)) { - SEMA_ERROR(right, "% by zero not allowed."); - return false; + RETURN_SEMA_ERROR(right, "% by zero not allowed."); } break; default: @@ -5699,89 +5726,20 @@ static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr * { expr->expr_kind = EXPR_BITASSIGN; } - // 7. Assign type - expr->type = left->type; - return true; -} - - -/** - * Handle a += b, a -= b - * @return true if analysis succeeded. - */ -static bool sema_expr_analyse_add_sub_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right) -{ - if (left->expr_kind == EXPR_CT_IDENT) - { - return sema_binary_analyse_ct_common_assign(context, expr, left); - } - - // 1. Analyse the left hand side - if (!sema_analyse_expr(context, left)) return false; - - // 2. Ensure the left hand side is assignable - if (!sema_expr_check_assign(context, left)) return false; - - Type *left_type_canonical = left->type->canonical; - - // 4. Analyse right hand side - REMINDER("Possible deep cast here."); - if (!sema_analyse_expr(context, right)) return false; - // 3. Copy type & set properties. - if (IS_OPTIONAL(right) && !IS_OPTIONAL(left)) - { - SEMA_ERROR(right, "Cannot assign an optional value to a non-optional."); - return false; - } - expr->type = left->type; - bool optional = IS_OPTIONAL(left) || IS_OPTIONAL(right); - - - // 5. In the pointer case we have to treat this differently. - if (left_type_canonical->type_kind == TYPE_POINTER) - { - - expr->type = left->type; - - // 7. Finally, check that the right side is indeed an integer. - if (!type_is_integer(right->type->canonical)) - { - 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))); - return false; - } - return true; - } - - Type *lhs_flat = type_flatten(left_type_canonical); - if (lhs_flat->type_kind == TYPE_ENUM) - { - if (!cast_implicit(context, right, type_base(lhs_flat), false)) return false; - expr->type = type_add_optional(expr->type, optional); - return true; - } - - // 8. Otherwise we cast rhs to lhs - if (!cast_implicit_binary(context, right, left->type, false)) return false; - - // 9. We expect a numeric type on both left and right - if (!type_underlying_may_add_sub(left->type)) - { - SEMA_ERROR(left, "Expected a numeric type here."); - return false; - } - REMINDER("Check if can remove"); - if (left->expr_kind == EXPR_BITACCESS) +END: + // Handle the subscript assign variant. + if (left->expr_kind == EXPR_SUBSCRIPT_ASSIGN) { - expr->expr_kind = EXPR_BITASSIGN; + return sema_analyse_assign_mutate_overloaded_subscript(context, expr, left, left_type_canonical); } - expr->type = type_add_optional(expr->type, optional); + // 7. Assign type + expr->type = type_add_optional(left->type, optional); return true; } + static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, Expr *right, Type *left_type, Type *right_type, Expr *parent, const char *error_message, bool allow_bool_vec) { @@ -7147,6 +7105,85 @@ static inline bool sema_expr_analyse_ct_incdec(SemaContext *context, Expr *expr, return true; } +static bool sema_analyse_assign_mutate_overloaded_subscript(SemaContext *context, Expr *main, Expr *subscript_expr, Type *type) +{ + Expr *increased = exprptr(subscript_expr->subscript_assign_expr.expr); + Type *type_check = increased->type->canonical; + Expr *index = exprptr(subscript_expr->subscript_assign_expr.index); + Decl *operator = sema_find_operator(context, type_check, OVERLOAD_ELEMENT_REF); + Expr **args = NULL; + if (operator) + { + vec_add(args, exprptr(subscript_expr->subscript_assign_expr.index)); + if (!sema_insert_method_call(context, subscript_expr, operator, exprptr(subscript_expr->subscript_assign_expr.expr), args)) return false; + expr_rewrite_insert_deref(subscript_expr); + main->type = subscript_expr->type; + return true; + } + operator = sema_find_operator(context, type_check, OVERLOAD_ELEMENT_AT); + if (!operator) + { + RETURN_SEMA_ERROR(main, "There is no overload for [] for %s.", type_quoted_error_string(increased->type)); + } + Type *return_type = typeget(operator->func_decl.signature.rtype); + if (type_no_optional(return_type->canonical) != type->canonical) + { + RETURN_SEMA_ERROR(main, "There is a type mismatch between overload for [] and []= for %s.", type_quoted_error_string(increased->type)); + } + bool is_optional_result = type_is_optional(increased->type) || type_is_optional(return_type); + Type *result_type = type_add_optional(subscript_expr->type, is_optional_result); + expr_insert_addr(increased); + Decl *temp_val = decl_new_generated_var(increased->type, VARDECL_LOCAL, increased->span); + Decl *index_val = decl_new_generated_var(index->type, VARDECL_LOCAL, index->span); + Decl *value_val = decl_new_generated_var(return_type, VARDECL_LOCAL, main->span); + Decl *result_val = decl_new_generated_var(result_type, VARDECL_LOCAL, main->span); + Expr *decl_expr = expr_generate_decl(temp_val, increased); + Expr *decl_index_expr = expr_generate_decl(index_val, index); + Expr *mutate = expr_copy(main); + mutate->resolve_status = RESOLVE_NOT_DONE; + mutate->type = NULL; + switch (main->expr_kind) + { + case EXPR_UNARY: + case EXPR_POST_UNARY: + mutate->unary_expr.expr = expr_variable(value_val); + break; + case EXPR_BINARY: + mutate->binary_expr.left = exprid(expr_variable(value_val)); + break; + default: + UNREACHABLE + } + main->expr_kind = EXPR_EXPRESSION_LIST; + main->expression_list = NULL; + // temp = indexed + vec_add(main->expression_list, decl_expr); + // temp_index = index + vec_add(main->expression_list, decl_index_expr); + Expr *get_expr = expr_new(EXPR_ACCESS, increased->span); + vec_add(args, expr_variable(index_val)); + Expr *temp_val_1 = expr_variable(temp_val); + expr_rewrite_insert_deref(temp_val_1); + if (!sema_insert_method_call(context, get_expr, operator, temp_val_1, args)) return false; + Expr *value_val_expr = expr_generate_decl(value_val, get_expr); + // temp_value = func(temp, temp_index) + vec_add(main->expression_list, value_val_expr); + // temp_result = temp_value++, temp_result *= temp_value etc + vec_add(main->expression_list, expr_generate_decl(result_val, mutate)); + + args = NULL; + vec_add(args, expr_variable(index_val)); + vec_add(args, expr_variable(value_val)); + Expr *temp_val_2 = expr_variable(temp_val); + expr_rewrite_insert_deref(temp_val_2); + if (!sema_insert_method_call(context, subscript_expr, declptr(subscript_expr->subscript_assign_expr.method), temp_val_2, args)) return false; + ASSERT0(subscript_expr->expr_kind == EXPR_CALL); + subscript_expr->call_expr.has_optional_arg = false; + vec_add(main->expression_list, subscript_expr); + vec_add(main->expression_list, expr_variable(result_val)); + return sema_expr_analyse_expr_list(context, main); + +} /** * Analyse foo++ foo-- --foo ++foo * @return false if analysis fails. @@ -7170,78 +7207,15 @@ 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) && type->type_kind != TYPE_POINTER) + if (!type_underlying_may_add_sub(type)) { RETURN_SEMA_ERROR(inner, "The expression must be a number or a pointer."); } if (inner->expr_kind == EXPR_SUBSCRIPT_ASSIGN) { - Expr *increased = exprptr(inner->subscript_assign_expr.expr); - Type *type_check = increased->type->canonical; - Expr *index = exprptr(inner->subscript_assign_expr.index); - Decl *operator = sema_find_operator(context, type_check, OVERLOAD_ELEMENT_REF); - Expr **args = NULL; - if (operator) - { - vec_add(args, exprptr(inner->subscript_assign_expr.index)); - if (!sema_insert_method_call(context, inner, operator, exprptr(inner->subscript_assign_expr.expr), args)) return false; - expr_rewrite_insert_deref(inner); - goto OK; - } - operator = sema_find_operator(context, type_check, OVERLOAD_ELEMENT_AT); - if (!operator) - { - RETURN_SEMA_ERROR(expr, "There is no overload for [] for %s.", type_quoted_error_string(increased->type)); - } - Type *return_type = typeget(operator->func_decl.signature.rtype); - if (type_no_optional(return_type->canonical) != type->canonical) - { - RETURN_SEMA_ERROR(expr, "There is a type mismatch between overload for [] and []= for %s.", type_quoted_error_string(increased->type)); - } - bool is_optional_result = type_is_optional(increased->type) || type_is_optional(return_type); - Type *result_type = type_add_optional(inner->type, is_optional_result); - expr_insert_addr(increased); - Decl *temp_val = decl_new_generated_var(increased->type, VARDECL_LOCAL, increased->span); - Decl *index_val = decl_new_generated_var(index->type, VARDECL_LOCAL, index->span); - Decl *value_val = decl_new_generated_var(return_type, VARDECL_LOCAL, expr->span); - Decl *result_val = decl_new_generated_var(result_type, VARDECL_LOCAL, expr->span); - Expr *decl_expr = expr_generate_decl(temp_val, increased); - Expr *decl_index_expr = expr_generate_decl(index_val, index); - Expr *unary = expr_new_expr(expr->expr_kind, expr); - unary->unary_expr.expr = expr_variable(value_val); - unary->unary_expr.operator = expr->unary_expr.operator; - unary->unary_expr.no_wrap = expr->unary_expr.no_wrap; - expr->expr_kind = EXPR_EXPRESSION_LIST; - expr->expression_list = NULL; - // temp = indexed - vec_add(expr->expression_list, decl_expr); - // temp_index = index - vec_add(expr->expression_list, decl_index_expr); - Expr *get_expr = expr_new(EXPR_ACCESS, increased->span); - vec_add(args, expr_variable(index_val)); - Expr *temp_val_1 = expr_variable(temp_val); - expr_rewrite_insert_deref(temp_val_1); - if (!sema_insert_method_call(context, get_expr, operator, temp_val_1, args)) return false; - Expr *value_val_expr = expr_generate_decl(value_val, get_expr); - // temp_value = func(temp, temp_index) - vec_add(expr->expression_list, value_val_expr); - // temp_result = temp_value++ - vec_add(expr->expression_list, expr_generate_decl(result_val, unary)); - - args = NULL; - vec_add(args, expr_variable(index_val)); - vec_add(args, expr_variable(value_val)); - Expr *temp_val_2 = expr_variable(temp_val); - expr_rewrite_insert_deref(temp_val_2); - if (!sema_insert_method_call(context, inner, declptr(inner->subscript_assign_expr.method), temp_val_2, args)) return false; - ASSERT0(inner->expr_kind == EXPR_CALL); - inner->call_expr.has_optional_arg = false; - vec_add(expr->expression_list, inner); - vec_add(expr->expression_list, expr_variable(result_val)); - return sema_expr_analyse_expr_list(context, expr); - } -OK: + return sema_analyse_assign_mutate_overloaded_subscript(context, expr, inner, type); + } // 6. Done, the result is same as the inner type. expr->type = inner->type; return true; @@ -7440,20 +7414,20 @@ static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr) return sema_expr_analyse_add(context, expr, left, right); case BINARYOP_ADD_ASSIGN: case BINARYOP_SUB_ASSIGN: - return sema_expr_analyse_add_sub_assign(context, expr, left, right); + return sema_expr_analyse_op_assign(context, expr, left, right, false, false, true); case BINARYOP_SUB: return sema_expr_analyse_sub(context, expr, left, right); case BINARYOP_DIV: return sema_expr_analyse_div(context, expr, left, right); case BINARYOP_MULT_ASSIGN: case BINARYOP_DIV_ASSIGN: - return sema_expr_analyse_op_assign(context, expr, left, right, false, false); + return sema_expr_analyse_op_assign(context, expr, left, right, false, false, false); case BINARYOP_BIT_AND_ASSIGN: case BINARYOP_BIT_OR_ASSIGN: case BINARYOP_BIT_XOR_ASSIGN: - return sema_expr_analyse_op_assign(context, expr, left, right, true, true); + return sema_expr_analyse_op_assign(context, expr, left, right, true, true, false); case BINARYOP_MOD_ASSIGN: - return sema_expr_analyse_op_assign(context, expr, left, right, true, false); + return sema_expr_analyse_op_assign(context, expr, left, right, true, false, false); case BINARYOP_MOD: return sema_expr_analyse_mod(context, expr, left, right); case BINARYOP_AND: @@ -9378,7 +9352,7 @@ bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allo { if (!sema_analyse_inferred_expr(context, to, expr)) return false; } - if (!sema_cast_rvalue(context, expr)) return false; + if (!sema_cast_rvalue(context, expr, true)) return false; Type *to_canonical = to ? to->canonical : NULL; Type *rhs_type = expr->type; Type *rhs_type_canonical = rhs_type->canonical; @@ -9450,7 +9424,7 @@ static inline bool sema_cast_ct_ident_rvalue(SemaContext *context, Expr *expr) return true; } -static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr) +static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr, bool mutate) { if (!expr_ok(expr)) return false; switch (expr->expr_kind) @@ -9478,15 +9452,15 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr) RETURN_SEMA_ERROR(expr, "A macro name must be followed by '('."); } // We may have kept FOO.x.y as a reference, fold it now if y is not an aggregate. - sema_expr_flatten_const_ident(expr->access_expr.parent); + if (mutate) sema_expr_flatten_const_ident(expr->access_expr.parent); return true; case EXPR_TYPEINFO: RETURN_SEMA_ERROR(expr, "A type must be followed by either (...) or '.' unless passed as a macro type argument or assigned to a compile time type variable."); case EXPR_CT_IDENT: - if (!sema_cast_ct_ident_rvalue(context, expr)) return false; + if (mutate && !sema_cast_ct_ident_rvalue(context, expr)) return false; break; case EXPR_IDENTIFIER: - if (!sema_cast_ident_rvalue(context, expr)) return false; + if (mutate && !sema_cast_ident_rvalue(context, expr)) return false; break; case EXPR_SUBSCRIPT: case EXPR_SLICE: @@ -9525,7 +9499,7 @@ bool sema_analyse_ct_expr(SemaContext *context, Expr *expr) expr->const_expr.typeid = cond_val->canonical; expr->type = type_typeid; } - if (!sema_cast_rvalue(context, expr)) return false; + if (!sema_cast_rvalue(context, expr, true)) return false; if (!sema_cast_const(expr)) { RETURN_SEMA_ERROR(expr, "Expected a compile time expression."); @@ -9654,7 +9628,7 @@ bool sema_expr_check_discard(SemaContext *context, Expr *expr) bool sema_analyse_expr(SemaContext *context, Expr *expr) { - return sema_analyse_expr_value(context, expr) && sema_cast_rvalue(context, expr); + return sema_analyse_expr_value(context, expr) && sema_cast_rvalue(context, expr, true); } bool sema_cast_const(Expr *expr) @@ -9793,7 +9767,7 @@ bool sema_analyse_inferred_expr(SemaContext *context, Type *infer_type, Expr *ex if (!sema_analyse_expr_dispatch(context, expr, CHECK_VALUE)) return expr_poison(expr); break; } - if (!sema_cast_rvalue(context, expr)) return false; + if (!sema_cast_rvalue(context, expr, true)) return false; expr->resolve_status = RESOLVE_DONE; return true; } diff --git a/test/test_suite/constants/assign_to_const.c3 b/test/test_suite/constants/assign_to_const.c3 index b6574ec07..a5ee70633 100644 --- a/test/test_suite/constants/assign_to_const.c3 +++ b/test/test_suite/constants/assign_to_const.c3 @@ -4,5 +4,5 @@ import std::io; fn void main() { const NUM = 4; - NUM += 1; // #error: You cannot assign to a constant expression + NUM += 1; // #error: You cannot assign to a constant } \ No newline at end of file diff --git a/test/test_suite/contracts/out_subscript.c3 b/test/test_suite/contracts/out_subscript.c3 index 684ea8d22..9d9e97cbc 100644 --- a/test/test_suite/contracts/out_subscript.c3 +++ b/test/test_suite/contracts/out_subscript.c3 @@ -6,7 +6,7 @@ fn void tes2t(char* z, char[] out_data, char[] in_data) { z[0] = 2; z[0] += 1; // #error: 'out' parameters may not be read out_data[0] = 3; - out_data[0] += 1; // #error: 'out' parameters may not be read + out_data[0] *= 1; // #error: 'out' parameters may not be read out_data[0..3]; // #error: 'out' parameters may not be read out_data[0..3] = 23; } \ No newline at end of file diff --git a/test/test_suite/methods/operator_assign_mutate.c3t b/test/test_suite/methods/operator_assign_mutate.c3t new file mode 100644 index 000000000..3b7535e71 --- /dev/null +++ b/test/test_suite/methods/operator_assign_mutate.c3t @@ -0,0 +1,101 @@ +// #target: macos-x64 +module test; +import std::io, std::time, std::math, std::collections; + +List() stones; + +struct Foo +{ + int[3] x; +} + +fn int Foo.get(self, usz i) @operator([]) => self.x[i]; +fn void Foo.set(&self, usz i, int z) @operator([]=) => self.x[i] = z; + +fn void main() { + stones.push(1); + stones.push(100); + stones[0] *= 3; + stones[1] *= 2; + stones[0] += 31; + Foo z; + z[1] += 5; + z[1] *= 3; +} + +/* #expect: test.ll + +define void @test.main() #0 { +entry: + %z = alloca %Foo, align 4 + %.anon = alloca ptr, align 8 + %.anon3 = alloca i32, align 4 + %.anon4 = alloca i32, align 4 + %coerce = alloca %Foo, align 8 + %.anon6 = alloca i32, align 4 + %.anon9 = alloca ptr, align 8 + %.anon10 = alloca i32, align 4 + %.anon11 = alloca i32, align 4 + %coerce13 = alloca %Foo, align 8 + %.anon17 = alloca i32, align 4 + call void @"std_collections_list$long$.List.push"(ptr @test.stones, i64 1) #2 + call void @"std_collections_list$long$.List.push"(ptr @test.stones, i64 100) #2 + %0 = call ptr @"std_collections_list$long$.List.get_ref"(ptr @test.stones, i64 0) #2 + %1 = load i64, ptr %0, align 8 + %mul = mul i64 %1, 3 + store i64 %mul, ptr %0, align 8 + %2 = call ptr @"std_collections_list$long$.List.get_ref"(ptr @test.stones, i64 1) #2 + %3 = load i64, ptr %2, align 8 + %mul1 = mul i64 %3, 2 + store i64 %mul1, ptr %2, align 8 + %4 = call ptr @"std_collections_list$long$.List.get_ref"(ptr @test.stones, i64 0) #2 + %5 = load i64, ptr %4, align 8 + %add = add i64 %5, 31 + store i64 %add, ptr %4, align 8 + store i32 0, ptr %z, align 4 + %ptradd = getelementptr inbounds i8, ptr %z, i64 4 + store i32 0, ptr %ptradd, align 4 + %ptradd2 = getelementptr inbounds i8, ptr %z, i64 8 + store i32 0, ptr %ptradd2, align 4 + store ptr %z, ptr %.anon, align 8 + store i32 1, ptr %.anon3, align 4 + %6 = load ptr, ptr %.anon, align 8 + %7 = load i32, ptr %.anon3, align 4 + %sext = sext i32 %7 to i64 + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %coerce, ptr align 4 %6, i32 12, i1 false) + %lo = load i64, ptr %coerce, align 8 + %ptradd5 = getelementptr inbounds i8, ptr %coerce, i64 8 + %hi = load i32, ptr %ptradd5, align 8 + %8 = call i32 @test.Foo.get(i64 %lo, i32 %hi, i64 %sext) + store i32 %8, ptr %.anon4, align 4 + %9 = load i32, ptr %.anon4, align 4 + %add7 = add i32 %9, 5 + store i32 %add7, ptr %.anon4, align 4 + store i32 %add7, ptr %.anon6, align 4 + %10 = load i32, ptr %.anon3, align 4 + %sext8 = sext i32 %10 to i64 + %11 = load ptr, ptr %.anon, align 8 + %12 = load i32, ptr %.anon4, align 4 + call void @test.Foo.set(ptr %11, i64 %sext8, i32 %12) + store ptr %z, ptr %.anon9, align 8 + store i32 1, ptr %.anon10, align 4 + %13 = load ptr, ptr %.anon9, align 8 + %14 = load i32, ptr %.anon10, align 4 + %sext12 = sext i32 %14 to i64 + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %coerce13, ptr align 4 %13, i32 12, i1 false) + %lo14 = load i64, ptr %coerce13, align 8 + %ptradd15 = getelementptr inbounds i8, ptr %coerce13, i64 8 + %hi16 = load i32, ptr %ptradd15, align 8 + %15 = call i32 @test.Foo.get(i64 %lo14, i32 %hi16, i64 %sext12) + store i32 %15, ptr %.anon11, align 4 + %16 = load i32, ptr %.anon11, align 4 + %mul18 = mul i32 %16, 3 + store i32 %mul18, ptr %.anon11, align 4 + store i32 %mul18, ptr %.anon17, align 4 + %17 = load i32, ptr %.anon10, align 4 + %sext19 = sext i32 %17 to i64 + %18 = load ptr, ptr %.anon9, align 8 + %19 = load i32, ptr %.anon11, align 4 + call void @test.Foo.set(ptr %18, i64 %sext19, i32 %19) + ret void +}