Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions vlib/v/checker/infix.v
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,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 {
Expand Down Expand Up @@ -459,7 +463,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
Expand All @@ -477,7 +482,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
Expand Down Expand Up @@ -949,7 +955,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)'
Expand Down
14 changes: 0 additions & 14 deletions vlib/v/checker/tests/pointer_ops.out
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 | }
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
25 changes: 25 additions & 0 deletions vlib/v/tests/pointers/ptr_arithmetic_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Loading