From 219dcb12cfc9ddf782258c89ba3cd361a3d569c9 Mon Sep 17 00:00:00 2001 From: wenxuanjun <41050170+wenxuanjun@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:12:28 +0800 Subject: [PATCH] checker: fix infix arithmetic on explicit struct pointers (#26810) --- vlib/v/checker/infix.v | 12 ++++++-- vlib/v/checker/tests/pointer_ops.out | 14 ---------- ...e_pointer_arithmetic_should_be_checked.out | 28 +++++++++++++++++++ ...fe_pointer_arithmetic_should_be_checked.vv | 21 ++++++++++++++ vlib/v/tests/pointers/ptr_arithmetic_test.v | 25 +++++++++++++++++ 5 files changed, 83 insertions(+), 17 deletions(-) diff --git a/vlib/v/checker/infix.v b/vlib/v/checker/infix.v index c37d3c24f..a9709b9b9 100644 --- a/vlib/v/checker/infix.v +++ b/vlib/v/checker/infix.v @@ -298,6 +298,10 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { } } mut return_type := left_type + left_is_explicit_ptr := left_type.is_any_kind_of_pointer() && !node.left.is_auto_deref_var() + && left_final_sym.kind != .voidptr + right_is_explicit_ptr := right_type.is_any_kind_of_pointer() && !node.right.is_auto_deref_var() + && right_final_sym.kind != .voidptr if node.op != .key_is { match mut node.left { @@ -564,7 +568,8 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { c.error('infix `${node.op}` is not defined for pointer values', left_right_pos) } - if !c.pref.translated && left_sym.kind in [.array, .array_fixed, .map, .struct] { + if !c.pref.translated && !left_is_explicit_ptr + && left_sym.kind in [.array, .array_fixed, .map, .struct] { if left_sym.has_method_with_generic_parent(op_str) { if method := left_sym.find_method_with_generic_parent(op_str) { return_type = method.return_type @@ -589,7 +594,8 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { left_right_pos) } } - } else if !c.pref.translated && right_sym.kind in [.array, .array_fixed, .map, .struct] { + } else if !c.pref.translated && !right_is_explicit_ptr + && right_sym.kind in [.array, .array_fixed, .map, .struct] { if right_sym.has_method_with_generic_parent(op_str) { if method := right_sym.find_method_with_generic_parent(op_str) { return_type = method.return_type @@ -1182,7 +1188,7 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { c.error('infix expr: cannot use `${error_right_sym.name}` (right expression) as `${error_left_sym.name}`', left_right_pos) } else if left_type.is_ptr() { - for_ptr_op := c.table.type_is_for_pointer_arithmetic(left_type) + for_ptr_op := left_is_explicit_ptr || c.table.type_is_for_pointer_arithmetic(left_type) if left_sym.language == .v && !c.pref.translated && !c.inside_unsafe && !for_ptr_op && right_type.is_int() { sugg := ' (you can use it inside an `unsafe` block)' diff --git a/vlib/v/checker/tests/pointer_ops.out b/vlib/v/checker/tests/pointer_ops.out index 5afb7cff6..a027964a9 100644 --- a/vlib/v/checker/tests/pointer_ops.out +++ b/vlib/v/checker/tests/pointer_ops.out @@ -61,13 +61,6 @@ vlib/v/checker/tests/pointer_ops.vv:15:3: error: infix `%` is not defined for po | ~~~~~~~ 16 | } 17 | } -vlib/v/checker/tests/pointer_ops.vv:15:3: error: mismatched types `&Foo` and `int literal` - 13 | _ = p[3] - 14 | mut foo := &Foo{} - 15 | foo % 3 - | ~~~~~~~ - 16 | } - 17 | } vlib/v/checker/tests/pointer_ops.vv:22:7: error: `+` cannot be used with `voidptr` 20 | unsafe { 21 | mut p := nil @@ -131,10 +124,3 @@ vlib/v/checker/tests/pointer_ops.vv:30:3: error: infix `%` is not defined for po | ~~~~~~~ 31 | } 32 | } -vlib/v/checker/tests/pointer_ops.vv:30:3: error: mismatched types `&Foo` and `int literal` - 28 | _ = p[3] - 29 | mut foo := &Foo{} - 30 | foo % 3 - | ~~~~~~~ - 31 | } - 32 | } diff --git a/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out b/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out index 2e8ee54a4..8603297bd 100644 --- a/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out +++ b/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out @@ -26,3 +26,31 @@ vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:12:6: warnin | ~~~~~ 13 | _ := q 14 | _ := v +vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:23:11: warning: pointer arithmetic is only allowed in `unsafe` blocks + 21 | fn test_struct_ptr_infix() { + 22 | v := Foo{} + 23 | mut q := &v - 1 + | ~~~~~~ + 24 | q = q + 3 + 25 | _ := q +vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:24:6: warning: pointer arithmetic is only allowed in `unsafe` blocks + 22 | v := Foo{} + 23 | mut q := &v - 1 + 24 | q = q + 3 + | ~~~~~ + 25 | _ := q + 26 | _ := v +vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:32:11: warning: pointer arithmetic is only allowed in `unsafe` blocks + 30 | v := Foo{} + 31 | p := &v + 32 | mut q := &p - 1 + | ~~~~~~ + 33 | q = q + 3 + 34 | _ := q +vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:33:6: warning: pointer arithmetic is only allowed in `unsafe` blocks + 31 | p := &v + 32 | mut q := &p - 1 + 33 | q = q + 3 + | ~~~~~ + 34 | _ := q + 35 | _ := p diff --git a/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv b/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv index 0c8697855..c91cd2ad0 100644 --- a/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv +++ b/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv @@ -13,3 +13,24 @@ fn test_ptr_infix() { _ := q _ := v } + +struct Foo { + x int +} + +fn test_struct_ptr_infix() { + v := Foo{} + mut q := &v - 1 + q = q + 3 + _ := q + _ := v +} + +fn test_ptr_to_struct_ptr_infix() { + v := Foo{} + p := &v + mut q := &p - 1 + q = q + 3 + _ := q + _ := p +} diff --git a/vlib/v/tests/pointers/ptr_arithmetic_test.v b/vlib/v/tests/pointers/ptr_arithmetic_test.v index 8ab1356ab..d6395ec2f 100644 --- a/vlib/v/tests/pointers/ptr_arithmetic_test.v +++ b/vlib/v/tests/pointers/ptr_arithmetic_test.v @@ -68,3 +68,28 @@ fn test_ptr_arithmetic_over_struct() { } assert pa == unsafe { &a[0] } } + +fn test_ptr_infix_arithmetic_over_struct() { + mut a := [3]Abc{} + a[0].x = 10 + a[1].x = 100 + a[2].x = 1000 + unsafe { + pa := &a[0] + next := pa + 1 + assert next.x == 100 + last := next + 1 + assert last.x == 1000 + back := last - 2 + assert back.x == 10 + + refs := [&a[0], &a[1], &a[2]] + ppa := &refs[0] + next_ptr := ppa + 1 + assert (*next_ptr).x == 100 + last_ptr := next_ptr + 1 + assert (*last_ptr).x == 1000 + back_ptr := last_ptr - 2 + assert (*back_ptr).x == 10 + } +} -- 2.39.5