checker: fix checking of expression as mut receiver, when calling methods of arrays/maps (#20410)

This commit is contained in:
shove 2024-01-07 04:25:19 +08:00 committed by GitHub
parent a321ef694b
commit 3d8425dafd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 16 deletions

View File

@ -2066,10 +2066,7 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
node.pos) node.pos)
} }
if method.params[0].is_mut { if method.params[0].is_mut {
to_lock, pos := c.fail_if_immutable(mut node.left) to_lock, pos := c.check_for_mut_receiver(mut node.left)
if !node.left.is_lvalue() {
c.error('cannot pass expression as `mut`', node.left.pos())
}
// node.is_mut = true // node.is_mut = true
if to_lock != '' && rec_share != .shared_t { if to_lock != '' && rec_share != .shared_t {
c.error('${to_lock} is `shared` and must be `lock`ed to be passed as `mut`', c.error('${to_lock} is `shared` and must be `lock`ed to be passed as `mut`',
@ -2684,7 +2681,7 @@ fn (mut c Checker) map_builtin_method_call(mut node ast.CallExpr, left_type_ ast
c.error('`.${method_name}()` does not have any arguments', node.args[0].pos) c.error('`.${method_name}()` does not have any arguments', node.args[0].pos)
} }
if method_name[0] == `m` { if method_name[0] == `m` {
c.fail_if_immutable(mut node.left) c.check_for_mut_receiver(mut node.left)
} }
if node.left.is_auto_deref_var() || left_type.has_flag(.shared_f) { if node.left.is_auto_deref_var() || left_type.has_flag(.shared_f) {
ret_type = node.left_type.deref() ret_type = node.left_type.deref()
@ -2712,7 +2709,7 @@ fn (mut c Checker) map_builtin_method_call(mut node ast.CallExpr, left_type_ ast
} }
} }
'delete' { 'delete' {
c.fail_if_immutable(mut node.left) c.check_for_mut_receiver(mut node.left)
if node.args.len != 1 { if node.args.len != 1 {
c.error('expected 1 argument, but got ${node.args.len}', node.pos) c.error('expected 1 argument, but got ${node.args.len}', node.pos)
} }
@ -2789,7 +2786,7 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
c.error('the `sort()` method can be called only on mutable receivers, but `${node.left}` is a call expression', c.error('the `sort()` method can be called only on mutable receivers, but `${node.left}` is a call expression',
node.pos) node.pos)
} }
c.fail_if_immutable(mut node.left) c.check_for_mut_receiver(mut node.left)
} }
// position of `a` and `b` doesn't matter, they're the same // position of `a` and `b` doesn't matter, they're the same
scope_register_a_b(mut node.scope, node.pos, elem_typ) scope_register_a_b(mut node.scope, node.pos, elem_typ)
@ -2943,13 +2940,13 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
} }
node.return_type = array_info.elem_type node.return_type = array_info.elem_type
if method_name == 'pop' { if method_name == 'pop' {
c.fail_if_immutable(mut node.left) c.check_for_mut_receiver(mut node.left)
node.receiver_type = node.left_type.ref() node.receiver_type = node.left_type.ref()
} else { } else {
node.receiver_type = node.left_type node.receiver_type = node.left_type
} }
} else if method_name == 'delete' { } else if method_name == 'delete' {
c.fail_if_immutable(mut node.left) c.check_for_mut_receiver(mut node.left)
unwrapped_left_sym := c.table.sym(unwrapped_left_type) unwrapped_left_sym := c.table.sym(unwrapped_left_type)
if method := c.table.find_method(unwrapped_left_sym, method_name) { if method := c.table.find_method(unwrapped_left_sym, method_name) {
node.receiver_type = method.receiver_type node.receiver_type = method.receiver_type
@ -2959,6 +2956,14 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
return node.return_type return node.return_type
} }
fn (mut c Checker) check_for_mut_receiver(mut expr ast.Expr) (string, token.Pos) {
to_lock, pos := c.fail_if_immutable(mut expr)
if !expr.is_lvalue() {
c.error('cannot pass expression as `mut`', expr.pos())
}
return to_lock, pos
}
fn scope_register_it(mut s ast.Scope, pos token.Pos, typ ast.Type) { fn scope_register_it(mut s ast.Scope, pos token.Pos, typ ast.Type) {
scope_register_var_name(mut s, pos, typ, 'it') scope_register_var_name(mut s, pos, typ, 'it')
} }

View File

@ -4,3 +4,9 @@ vlib/v/checker/tests/fn_return_array_sort_err.vv:6:14: error: the `sort()` metho
6 | ret_array().sort() 6 | ret_array().sort()
| ~~~~~~ | ~~~~~~
7 | } 7 | }
vlib/v/checker/tests/fn_return_array_sort_err.vv:6:2: error: cannot pass expression as `mut`
4 |
5 | fn main() {
6 | ret_array().sort()
| ~~~~~~~~~~~
7 | }

View File

@ -1,5 +1,20 @@
vlib/v/checker/tests/mut_receiver_lit.vv:10:1: error: cannot pass expression as `mut` vlib/v/checker/tests/mut_receiver_lit.vv:12:2: error: cannot pass expression as `mut`
8 | } 10 |
9 | 11 | fn non_mut_receiver() {
10 | Box{}.set(0) 12 | Box{}.set(0)
| ~~~~~ | ~~~~~
13 | }
14 |
vlib/v/checker/tests/mut_receiver_lit.vv:18:11: error: cannot pass expression as `mut`
16 | fn array_chained_method_calls() {
17 | path := 'hello/file.txt'
18 | _ = path.split('.').pop()
| ~~~~~~~~~~
19 | }
20 |
vlib/v/checker/tests/mut_receiver_lit.vv:24:4: error: cannot pass expression as `mut`
22 | fn map_chained_method_calls() {
23 | mut m := map[int]int{}
24 | m.clone().delete(0)
| ~~~~~~~
25 | }

View File

@ -1,3 +1,4 @@
// error in non-mut as receover
struct Box { struct Box {
mut: mut:
value int value int
@ -7,4 +8,18 @@ fn (mut box Box) set(value int) {
box.value = value box.value = value
} }
fn non_mut_receiver() {
Box{}.set(0) Box{}.set(0)
}
// error in array chained method calls
fn array_chained_method_calls() {
path := 'hello/file.txt'
_ = path.split('.').pop()
}
// error in map chained method calls
fn map_chained_method_calls() {
mut m := map[int]int{}
m.clone().delete(0)
}

View File

@ -2,6 +2,6 @@
// Phenomenon: cgen brackets are asymmetric in chained method calls. // Phenomenon: cgen brackets are asymmetric in chained method calls.
fn test_main() { fn test_main() {
mut path := 'hello/file.txt' mut path := 'hello/file.txt'
extension := path.split('.').pop() extension := path.split('.').last()
assert extension == 'txt' assert extension == 'txt'
} }