diff --git a/vlib/builtin/map.v b/vlib/builtin/map.v index 9d95ddd0d8..82195c53e6 100644 --- a/vlib/builtin/map.v +++ b/vlib/builtin/map.v @@ -207,7 +207,7 @@ pub struct map { mut: // Highest even index in the hashtable even_index u32 - // Number of cached hashbits left for rehasing + // Number of cached hashbits left for rehashing cached_hashbits byte // Used for right-shifting out used hashbits shift byte @@ -346,6 +346,14 @@ fn new_map_init_2(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, fre return out } +pub fn (mut m map) move() map { + r := *m + unsafe { + C.memset(m, 0, sizeof(map)) + } + return r +} + [inline] fn (m &map) key_to_index(pkey voidptr) (u32, u32) { hash := m.hash_fn(pkey) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 480e436961..ceabdefd87 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1442,10 +1442,13 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { call_expr.return_type = table.int_type } return call_expr.return_type - } else if left_type_sym.kind == .map && method_name in ['clone', 'keys'] { + } else if left_type_sym.kind == .map && method_name in ['clone', 'keys', 'move'] { mut ret_type := table.void_type match method_name { - 'clone' { + 'clone', 'move' { + if method_name[0] == `m` { + c.fail_if_immutable(call_expr.left) + } ret_type = left_type } 'keys' { @@ -2737,9 +2740,9 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { if left_sym.kind == .map && !c.inside_unsafe && assign_stmt.op in [.assign, .decl_assign] && right_sym.kind == .map && (left is ast.Ident && !left.is_blank_ident()) && right is ast.Ident { - // Do not allow `a = b`, only `a = b.clone()` - c.error('use `map2 $assign_stmt.op.str() map1.clone()` instead of `map2 $assign_stmt.op.str() map1` (or use `unsafe`)', - assign_stmt.pos) + // Do not allow `a = b` + c.error('cannot copy map: call `move` or `clone` method first (or use `unsafe`)', + right.position()) } left_is_ptr := left_type.is_ptr() || left_sym.is_pointer() if left_is_ptr && c.for_in_mut_val_name != left.str() && left.str() !in c.fn_mut_arg_names { diff --git a/vlib/v/checker/tests/array_or_map_assign_err.out b/vlib/v/checker/tests/array_or_map_assign_err.out index bd9abec435..b299e84c09 100644 --- a/vlib/v/checker/tests/array_or_map_assign_err.out +++ b/vlib/v/checker/tests/array_or_map_assign_err.out @@ -10,18 +10,19 @@ vlib/v/checker/tests/array_or_map_assign_err.vv:5:5: error: use `array2 = array1 4 | mut a3 := []int{} 5 | a3 = a1 | ^ - 6 | + 6 | 7 | m1 := {'one': 1} -vlib/v/checker/tests/array_or_map_assign_err.vv:8:5: error: use `map2 := map1.clone()` instead of `map2 := map1` (or use `unsafe`) - 6 | +vlib/v/checker/tests/array_or_map_assign_err.vv:8:8: error: cannot copy map: call `move` or `clone` method first (or use `unsafe`) + 6 | 7 | m1 := {'one': 1} 8 | m2 := m1 - | ~~ + | ~~ 9 | mut m3 := map[string]int{} 10 | m3 = m1 -vlib/v/checker/tests/array_or_map_assign_err.vv:10:5: error: use `map2 = map1.clone()` instead of `map2 = map1` (or use `unsafe`) +vlib/v/checker/tests/array_or_map_assign_err.vv:10:7: error: cannot copy map: call `move` or `clone` method first (or use `unsafe`) 8 | m2 := m1 9 | mut m3 := map[string]int{} 10 | m3 = m1 - | ^ - 11 | } + | ~~ + 11 | + 12 | _ = a2 diff --git a/vlib/v/checker/tests/array_or_map_assign_err.vv b/vlib/v/checker/tests/array_or_map_assign_err.vv index 11c000e22c..09c082e4ed 100644 --- a/vlib/v/checker/tests/array_or_map_assign_err.vv +++ b/vlib/v/checker/tests/array_or_map_assign_err.vv @@ -8,4 +8,7 @@ fn main() { m2 := m1 mut m3 := map[string]int{} m3 = m1 + + _ = a2 + _ = m2 } diff --git a/vlib/v/checker/tests/immutable_map.out b/vlib/v/checker/tests/immutable_map.out new file mode 100644 index 0000000000..3c5f7e3ef7 --- /dev/null +++ b/vlib/v/checker/tests/immutable_map.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/immutable_map.vv:3:2: error: `m` is immutable, declare it with `mut` to make it mutable + 1 | fn main() { + 2 | m := map[string]int + 3 | m['test']++ + | ^ + 4 | m['test']-- + 5 | _ = m.move() +vlib/v/checker/tests/immutable_map.vv:4:2: error: `m` is immutable, declare it with `mut` to make it mutable + 2 | m := map[string]int + 3 | m['test']++ + 4 | m['test']-- + | ^ + 5 | _ = m.move() + 6 | m.delete('s') +vlib/v/checker/tests/immutable_map.vv:5:6: error: `m` is immutable, declare it with `mut` to make it mutable + 3 | m['test']++ + 4 | m['test']-- + 5 | _ = m.move() + | ^ + 6 | m.delete('s') + 7 | } +vlib/v/checker/tests/immutable_map.vv:6:2: error: `m` is immutable, declare it with `mut` to make it mutable + 4 | m['test']-- + 5 | _ = m.move() + 6 | m.delete('s') + | ^ + 7 | } diff --git a/vlib/v/checker/tests/immutable_map_postfix.vv b/vlib/v/checker/tests/immutable_map.vv similarity index 67% rename from vlib/v/checker/tests/immutable_map_postfix.vv rename to vlib/v/checker/tests/immutable_map.vv index 13a4a503c6..f3e00081d2 100644 --- a/vlib/v/checker/tests/immutable_map_postfix.vv +++ b/vlib/v/checker/tests/immutable_map.vv @@ -2,4 +2,6 @@ fn main() { m := map[string]int m['test']++ m['test']-- + _ = m.move() + m.delete('s') } diff --git a/vlib/v/checker/tests/immutable_map_postfix.out b/vlib/v/checker/tests/immutable_map_postfix.out deleted file mode 100644 index 4948e8aa96..0000000000 --- a/vlib/v/checker/tests/immutable_map_postfix.out +++ /dev/null @@ -1,13 +0,0 @@ -vlib/v/checker/tests/immutable_map_postfix.vv:3:2: error: `m` is immutable, declare it with `mut` to make it mutable - 1 | fn main() { - 2 | m := map[string]int - 3 | m['test']++ - | ^ - 4 | m['test']-- - 5 | } -vlib/v/checker/tests/immutable_map_postfix.vv:4:2: error: `m` is immutable, declare it with `mut` to make it mutable - 2 | m := map[string]int - 3 | m['test']++ - 4 | m['test']-- - | ^ - 5 | } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 23505b273a..184c64cf24 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -479,7 +479,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) { g.gen_str_for_type(node.receiver_type) } mut has_cast := false - if left_sym.kind == .map && node.name == 'clone' { + if left_sym.kind == .map && node.name in ['clone', 'move'] { receiver_type_name = 'map' } // TODO performance, detect `array` method differently