From dcf695c72654602fcb8cb4efcd16d38205bfef16 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 5 Feb 2026 00:34:17 +0100 Subject: [PATCH] Fix `list[0].i = 5` when `list[0]` returns a pointer. #2888 --- releasenotes.md | 1 + src/compiler/compiler_internal.h | 2 +- src/compiler/parse_global.c | 10 ++++++++++ src/compiler/sema_expr.c | 7 ++++++- .../overloading/operator_pointer.c3t | 19 +++++++++++++++++++ 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 test/test_suite/overloading/operator_pointer.c3t diff --git a/releasenotes.md b/releasenotes.md index 5d76b981c..e28a74b82 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -14,6 +14,7 @@ - Add error message if directory with output file name already exists - Regression where nested lambdas would be evaluated twice. - Compiler crash when using arrays of vectors in lists. #2889 +- Fix `list[0].i = 5` when `list[0]` returns a pointer. #2888 ## 0.7.9 Change list diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 4229ed761..b3f3b4e35 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -4553,7 +4553,7 @@ INLINE bool expr_is_const_ref(Expr *expr) INLINE bool expr_is_const_pointer(Expr *expr) { - ASSERT(expr->resolve_status == RESOLVE_DONE); + ASSERT_SPAN(expr, expr->resolve_status == RESOLVE_DONE); return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_POINTER; } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 56af7bb8c..6c5e0daca 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1281,6 +1281,16 @@ bool parse_attribute(ParseContext *c, Attr **attribute_ref, bool expect_eos) expr->overload_expr = try_consume(c, TOKEN_EQ) ? OVERLOAD_ELEMENT_SET : OVERLOAD_ELEMENT_AT; RANGE_EXTEND_PREV(expr); break; + case TOKEN_LESS: + case TOKEN_LESS_EQ: + case TOKEN_GREATER: + case TOKEN_GREATER_EQ: + RETURN_PRINT_ERROR_HERE("Comparisons '<', '<=', '>', '>=' cannot be overloaded, only '==' and '!=' are supported."); + case TOKEN_PLUSPLUS: + case TOKEN_MINUSMINUS: + RETURN_PRINT_ERROR_HERE("Increment and decrement operators '++' and '--' cannot be directly overloaded, use '+=' and '-=' instead."); + case TOKEN_DOT: + RETURN_PRINT_ERROR_HERE("You cannot overload '.'."); default: PARSE_EXPR: expr = parse_constant_expr(c); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 4b88a67de..5e4ef1e29 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -6349,10 +6349,13 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr, bo Expr *parent = expr->access_unresolved_expr.parent; if (missing_ref) *missing_ref = false; + int times_to_deref = 1; if (expr->access_unresolved_expr.is_ref) { + times_to_deref = 2; expr_set_to_ref(parent); } + // 1. Resolve the left hand if (!sema_analyse_expr(context, parent)) return false; @@ -6441,11 +6444,13 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr, bo // 7. Is this a pointer? If so we insert a deref. Type *underlying_type = type_no_optional(parent->type)->canonical; - if (underlying_type->type_kind == TYPE_POINTER && underlying_type != type_voidptr) + while (times_to_deref > 0 && underlying_type->type_kind == TYPE_POINTER && underlying_type != type_voidptr) { + times_to_deref--; if (!sema_cast_rvalue(context, parent, true)) return false; if (!sema_expr_rewrite_insert_deref(context, expr->access_unresolved_expr.parent)) return false; parent = expr->access_unresolved_expr.parent; + underlying_type = type_no_optional(parent->type)->canonical; } // 8. Depending on parent type, we have some hard coded types diff --git a/test/test_suite/overloading/operator_pointer.c3t b/test/test_suite/overloading/operator_pointer.c3t new file mode 100644 index 000000000..e32f12bf9 --- /dev/null +++ b/test/test_suite/overloading/operator_pointer.c3t @@ -0,0 +1,19 @@ +struct Node +{ + int i; +} + +struct MyList +{ + Node** entries; +} +fn Node* MyList.get(&self, usz index) @operator([]) => self.entries[index]; +fn Node** MyList.get_ref(&self, usz index) @operator(&[]) => &self.entries[index]; +fn int main() +{ + Node n; + Node* np = &n; + MyList list = {.entries = &np}; + list[0].i = 5; + return 0; +} \ No newline at end of file