diff --git a/releasenotes.md b/releasenotes.md index d1609048e..67347c21e 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -16,6 +16,7 @@ - Deprecate `add_array` in favour of `push_all` on lists. - Fix max module name to 31 chars and the entire module path to 63 characters. - Improve error message for missing `$endif`. +- `foo[x][y] = b` now interpreted as `(*&foo[x])[y] = b` which allows overloads to do chained [] accesses. ### Fixes - List.remove_at would incorrectly trigger ASAN. diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index e56068677..e58c6eb11 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3691,6 +3691,10 @@ static inline bool sema_expr_resolve_subscript_index(SemaContext *context, Expr if (!subscript_type) { if (check_valid) return false; + if (overload_type == OVERLOAD_ELEMENT_REF) + { + RETURN_SEMA_ERROR(expr, "Getting a reference to a subscript of %s is not possible.", type_quoted_error_string(subscripted->type)); + } RETURN_SEMA_ERROR(expr, "Indexing a value of type %s is not possible.", type_quoted_error_string(subscripted->type)); } if (!overload) current_type = type_flatten(current_expr->type); @@ -3789,13 +3793,27 @@ static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr { // Evaluate the expression to index. Expr *subscripted = exprptr(expr->subscript_expr.expr); - if (subscripted->expr_kind == EXPR_CT_IDENT) + switch (subscripted->expr_kind) { - if (!sema_analyse_expr_lvalue(context, subscripted, NULL)) return false; - } - else - { - if (!sema_analyse_expr(context, subscripted)) return false; + case EXPR_CT_IDENT: + if (!sema_analyse_expr_lvalue(context, subscripted, NULL)) return false; + break; + case EXPR_SUBSCRIPT: + { + Expr *inner = expr_copy(subscripted); + subscripted->expr_kind = EXPR_UNARY; + subscripted->unary_expr.operator = UNARYOP_ADDR; + subscripted->unary_expr.expr = inner; + + inner = expr_copy(subscripted); + subscripted->expr_kind = EXPR_UNARY; + subscripted->unary_expr.operator = UNARYOP_DEREF; + subscripted->unary_expr.expr = inner; + FALLTHROUGH; + } + default: + if (!sema_analyse_expr(context, subscripted)) return false; + break; } if (!sema_expr_check_assign(context, expr, NULL)) return false; diff --git a/test/test_suite/expressions/incdec_overload.c3t b/test/test_suite/expressions/incdec_overload.c3t index 88f879031..7ebf2b229 100644 --- a/test/test_suite/expressions/incdec_overload.c3t +++ b/test/test_suite/expressions/incdec_overload.c3t @@ -2,7 +2,14 @@ module test; import std; -alias Abc = HashMap{int, int}; +struct Abc +{ + HashMap{int, int} y; +} + +fn void Abc.set(&self, int key, int value) @operator([]=) => self.y[key] = value; +fn int? Abc.get(&self, int key) @operator([]) => self.y[key]; + Abc m; fn void main() { @@ -30,96 +37,96 @@ entry: %retparam17 = alloca i32, align 4 %.anon22 = alloca i32, align 4 %anon.f23 = alloca i64, align 8 - %0 = call i8 @"std_collections_map$int$int$.HashMap.set"(ptr @test.m, i32 3, i32 100) - %1 = call i64 @"std_collections_map$int$int$.HashMap.get"(ptr %retparam, ptr @test.m, i32 3) - %not_err = icmp eq i64 %1, 0 - %2 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) - br i1 %2, label %after_check, label %assign_optional + call void @test.Abc.set(ptr @test.m, i32 3, i32 100) + %0 = call i64 @test.Abc.get(ptr %retparam, ptr @test.m, i32 3) + %not_err = icmp eq i64 %0, 0 + %1 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %1, label %after_check, label %assign_optional assign_optional: ; preds = %entry - store i64 %1, ptr %anon.f, align 8 + store i64 %0, ptr %anon.f, align 8 br label %after_assign after_check: ; preds = %entry - %3 = load i32, ptr %retparam, align 4 - store i32 %3, ptr %.anon, align 4 + %2 = load i32, ptr %retparam, align 4 + store i32 %2, ptr %.anon, align 4 store i64 0, ptr %anon.f, align 8 br label %after_assign after_assign: ; preds = %after_check, %assign_optional %optval = load i64, ptr %anon.f, align 8 %not_err3 = icmp eq i64 %optval, 0 - %4 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true) - br i1 %4, label %after_check5, label %assign_optional4 + %3 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true) + br i1 %3, label %after_check5, label %assign_optional4 assign_optional4: ; preds = %after_assign store i64 %optval, ptr %anon.f2, align 8 br label %after_assign6 after_check5: ; preds = %after_assign - %5 = load i32, ptr %.anon, align 4 - %sub = sub i32 %5, 1 + %4 = load i32, ptr %.anon, align 4 + %sub = sub i32 %4, 1 store i32 %sub, ptr %.anon, align 4 - store i32 %5, ptr %.anon1, align 4 + store i32 %4, ptr %.anon1, align 4 store i64 0, ptr %anon.f2, align 8 br label %after_assign6 after_assign6: ; preds = %after_check5, %assign_optional4 %optval7 = load i64, ptr %anon.f, align 8 %not_err8 = icmp eq i64 %optval7, 0 - %6 = call i1 @llvm.expect.i1(i1 %not_err8, i1 true) - br i1 %6, label %after_check9, label %voiderr + %5 = call i1 @llvm.expect.i1(i1 %not_err8, i1 true) + br i1 %5, label %after_check9, label %voiderr after_check9: ; preds = %after_assign6 - %7 = load i32, ptr %.anon, align 4 - %8 = call i8 @"std_collections_map$int$int$.HashMap.set"(ptr @test.m, i32 3, i32 %7) + %6 = load i32, ptr %.anon, align 4 + call void @test.Abc.set(ptr @test.m, i32 3, i32 %6) br label %voiderr voiderr: ; preds = %after_check9, %after_assign6 %optval10 = load i64, ptr %anon.f2, align 8 %not_err11 = icmp eq i64 %optval10, 0 - %9 = call i1 @llvm.expect.i1(i1 %not_err11, i1 true) - br i1 %9, label %after_check13, label %assign_optional12 + %7 = call i1 @llvm.expect.i1(i1 %not_err11, i1 true) + br i1 %7, label %after_check13, label %assign_optional12 assign_optional12: ; preds = %voiderr store i64 %optval10, ptr %x.f, align 8 br label %after_assign14 after_check13: ; preds = %voiderr - %10 = load i32, ptr %.anon1, align 4 - store i32 %10, ptr %x, align 4 + %8 = load i32, ptr %.anon1, align 4 + store i32 %8, ptr %x, align 4 store i64 0, ptr %x.f, align 8 br label %after_assign14 after_assign14: ; preds = %after_check13, %assign_optional12 - %11 = call i64 @"std_collections_map$int$int$.HashMap.get"(ptr %retparam17, ptr @test.m, i32 3) - %not_err18 = icmp eq i64 %11, 0 - %12 = call i1 @llvm.expect.i1(i1 %not_err18, i1 true) - br i1 %12, label %after_check20, label %assign_optional19 + %9 = call i64 @test.Abc.get(ptr %retparam17, ptr @test.m, i32 3) + %not_err18 = icmp eq i64 %9, 0 + %10 = call i1 @llvm.expect.i1(i1 %not_err18, i1 true) + br i1 %10, label %after_check20, label %assign_optional19 assign_optional19: ; preds = %after_assign14 - store i64 %11, ptr %anon.f16, align 8 + store i64 %9, ptr %anon.f16, align 8 br label %after_assign21 after_check20: ; preds = %after_assign14 - %13 = load i32, ptr %retparam17, align 4 - store i32 %13, ptr %.anon15, align 4 + %11 = load i32, ptr %retparam17, align 4 + store i32 %11, ptr %.anon15, align 4 store i64 0, ptr %anon.f16, align 8 br label %after_assign21 after_assign21: ; preds = %after_check20, %assign_optional19 %optval24 = load i64, ptr %anon.f16, align 8 %not_err25 = icmp eq i64 %optval24, 0 - %14 = call i1 @llvm.expect.i1(i1 %not_err25, i1 true) - br i1 %14, label %after_check27, label %assign_optional26 + %12 = call i1 @llvm.expect.i1(i1 %not_err25, i1 true) + br i1 %12, label %after_check27, label %assign_optional26 assign_optional26: ; preds = %after_assign21 store i64 %optval24, ptr %anon.f23, align 8 br label %after_assign28 after_check27: ; preds = %after_assign21 - %15 = load i32, ptr %.anon15, align 4 - %add = add i32 %15, 1 + %13 = load i32, ptr %.anon15, align 4 + %add = add i32 %13, 1 store i32 %add, ptr %.anon15, align 4 store i32 %add, ptr %.anon22, align 4 store i64 0, ptr %anon.f23, align 8 @@ -128,27 +135,27 @@ after_check27: ; preds = %after_assign21 after_assign28: ; preds = %after_check27, %assign_optional26 %optval29 = load i64, ptr %anon.f16, align 8 %not_err30 = icmp eq i64 %optval29, 0 - %16 = call i1 @llvm.expect.i1(i1 %not_err30, i1 true) - br i1 %16, label %after_check31, label %voiderr32 + %14 = call i1 @llvm.expect.i1(i1 %not_err30, i1 true) + br i1 %14, label %after_check31, label %voiderr32 after_check31: ; preds = %after_assign28 - %17 = load i32, ptr %.anon15, align 4 - %18 = call i8 @"std_collections_map$int$int$.HashMap.set"(ptr @test.m, i32 3, i32 %17) + %15 = load i32, ptr %.anon15, align 4 + call void @test.Abc.set(ptr @test.m, i32 3, i32 %15) br label %voiderr32 voiderr32: ; preds = %after_check31, %after_assign28 %optval33 = load i64, ptr %anon.f23, align 8 %not_err34 = icmp eq i64 %optval33, 0 - %19 = call i1 @llvm.expect.i1(i1 %not_err34, i1 true) - br i1 %19, label %after_check36, label %assign_optional35 + %16 = call i1 @llvm.expect.i1(i1 %not_err34, i1 true) + br i1 %16, label %after_check36, label %assign_optional35 assign_optional35: ; preds = %voiderr32 store i64 %optval33, ptr %x.f, align 8 br label %after_assign37 after_check36: ; preds = %voiderr32 - %20 = load i32, ptr %.anon22, align 4 - store i32 %20, ptr %x, align 4 + %17 = load i32, ptr %.anon22, align 4 + store i32 %17, ptr %x, align 4 store i64 0, ptr %x.f, align 8 br label %after_assign37 diff --git a/test/test_suite/overloading/set_not_set_overload.c3t b/test/test_suite/overloading/set_not_set_overload.c3t index c2e14d7f1..eb993c868 100644 --- a/test/test_suite/overloading/set_not_set_overload.c3t +++ b/test/test_suite/overloading/set_not_set_overload.c3t @@ -21,7 +21,7 @@ fn void main () x[0][0] = 4; char[20][20] z; Map foo = { z[:10], 10 }; - foo[3][4] = 123; + (*&&foo[3])[4] = 123; } /* #expect: test.ll @@ -39,22 +39,19 @@ entry: %lo = load i64, ptr %coerce, align 8 %ptradd = getelementptr inbounds i8, ptr %coerce, i64 8 %hi = load i32, ptr %ptradd, align 8 - call void @"std_collections_list$a3$int$.List.push"(ptr @test.x, i64 %lo, i32 %hi) #4 - %1 = load i64, ptr @test.x, align 8 - %lt = icmp ult i64 0, %1 - call void @llvm.assume(i1 %lt) - %2 = load ptr, ptr getelementptr inbounds (i8, ptr @test.x, i64 32), align 8 - store i32 4, ptr %2, align 4 + call void @"std_collections_list$a3$int$.List.push"(ptr @test.x, i64 %lo, i32 %hi) #3 + %1 = call ptr @"std_collections_list$a3$int$.List.get_ref"(ptr @test.x, i64 0) #3 + store i32 4, ptr %1, align 4 call void @llvm.memset.p0.i64(ptr align 16 %z, i8 0, i64 400, i1 false) - %3 = insertvalue %"char[20][]" undef, ptr %z, 0 - %4 = insertvalue %"char[20][]" %3, i64 10, 1 - store %"char[20][]" %4, ptr %foo, align 8 + %2 = insertvalue %"char[20][]" undef, ptr %z, 0 + %3 = insertvalue %"char[20][]" %2, i64 10, 1 + store %"char[20][]" %3, ptr %foo, align 8 %ptradd1 = getelementptr inbounds i8, ptr %foo, i64 16 store i32 10, ptr %ptradd1, align 8 - %5 = call { ptr, i64 } @test.Map.get(ptr byval(%Map) align 8 %foo, i32 3) #4 - store { ptr, i64 } %5, ptr %result, align 8 - %6 = load ptr, ptr %result, align 8 - %ptradd2 = getelementptr inbounds i8, ptr %6, i64 4 + %4 = call { ptr, i64 } @test.Map.get(ptr byval(%Map) align 8 %foo, i32 3) #3 + store { ptr, i64 } %4, ptr %result, align 8 + %5 = load ptr, ptr %result, align 8 + %ptradd2 = getelementptr inbounds i8, ptr %5, i64 4 store i8 123, ptr %ptradd2, align 1 ret void } diff --git a/test/unit/stdlib/collections/map.c3 b/test/unit/stdlib/collections/map.c3 index b8394d72c..fedd1fd8b 100644 --- a/test/unit/stdlib/collections/map.c3 +++ b/test/unit/stdlib/collections/map.c3 @@ -111,7 +111,7 @@ fn void test_ref() t.init(tmem); (&t["a"]).init(tmem); - (*&t["a"])["b"] = "ab"; + t["a"]["b"] = "ab"; test::eq("ab", t["a"]["b"]!!); }