From 777460dfd3f7cf551cb57ac9a6f29831aa85f164 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 30 May 2026 23:42:35 +0300 Subject: [PATCH] cgen: clone comptime selector assignments (#27295) --- vlib/v/checker/assign.v | 7 +++- .../tests/comptime_selector_map_copy_err.out | 7 ++++ .../tests/comptime_selector_map_copy_err.vv | 20 ++++++++++++ vlib/v/gen/c/cgen.v | 2 +- ...tofree_comptime_selector_clone.c.must_have | 2 ++ .../autofree_comptime_selector_clone.out | 1 + .../autofree_comptime_selector_clone.vv | 32 +++++++++++++++++++ .../comptime_selector_map_move_test.v | 27 ++++++++++++++++ 8 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 vlib/v/checker/tests/comptime_selector_map_copy_err.out create mode 100644 vlib/v/checker/tests/comptime_selector_map_copy_err.vv create mode 100644 vlib/v/gen/c/testdata/autofree_comptime_selector_clone.c.must_have create mode 100644 vlib/v/gen/c/testdata/autofree_comptime_selector_clone.out create mode 100644 vlib/v/gen/c/testdata/autofree_comptime_selector_clone.vv create mode 100644 vlib/v/tests/comptime/comptime_selector_map_move_test.v diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index b6f931682..94561a954 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -881,8 +881,13 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.', } } } + right_is_lvalue := if right is ast.ComptimeSelector { + right.left.is_lvalue() + } else { + right.is_lvalue() + } if left_sym.kind == .map && is_assign && right_sym.kind == .map && !c.inside_unsafe - && !left.is_blank_ident() && right.is_lvalue() && right !is ast.ComptimeSelector + && !left.is_blank_ident() && right_is_lvalue && (!right_type.is_ptr() || (right is ast.Ident && assign_expr_is_auto_deref(right))) { // Do not allow `a = b` c.error('cannot copy map: call `move` or `clone` method (or use a reference)', diff --git a/vlib/v/checker/tests/comptime_selector_map_copy_err.out b/vlib/v/checker/tests/comptime_selector_map_copy_err.out new file mode 100644 index 000000000..d043e9cdb --- /dev/null +++ b/vlib/v/checker/tests/comptime_selector_map_copy_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/comptime_selector_map_copy_err.vv:8:18: error: cannot copy map: call `move` or `clone` method (or use a reference) + 6 | $for field in T.fields { + 7 | $if field.unaliased_typ is map[string]string { + 8 | values := val.$(field.name) + | ~~~~~~~~~~~~~ + 9 | _ = values + 10 | } diff --git a/vlib/v/checker/tests/comptime_selector_map_copy_err.vv b/vlib/v/checker/tests/comptime_selector_map_copy_err.vv new file mode 100644 index 000000000..0b6551ba8 --- /dev/null +++ b/vlib/v/checker/tests/comptime_selector_map_copy_err.vv @@ -0,0 +1,20 @@ +struct Item { + values map[string]string +} + +fn process[T](val T) { + $for field in T.fields { + $if field.unaliased_typ is map[string]string { + values := val.$(field.name) + _ = values + } + } +} + +fn main() { + process(Item{ + values: { + 'a': 'b' + } + }) +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 57a27d013..a15ab02ba 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5569,7 +5569,7 @@ fn (mut g Gen) get_ternary_name(name string) string { } fn (mut g Gen) gen_clone_assignment(var_type ast.Type, val ast.Expr, typ ast.Type, add_eq bool) bool { - if val !in [ast.Ident, ast.SelectorExpr] { + if val !in [ast.Ident, ast.SelectorExpr, ast.ComptimeSelector] { return false } right_sym := g.table.sym(typ) diff --git a/vlib/v/gen/c/testdata/autofree_comptime_selector_clone.c.must_have b/vlib/v/gen/c/testdata/autofree_comptime_selector_clone.c.must_have new file mode 100644 index 000000000..03ced7047 --- /dev/null +++ b/vlib/v/gen/c/testdata/autofree_comptime_selector_clone.c.must_have @@ -0,0 +1,2 @@ +builtin__string_clone_static(val.name) +builtin__array_clone_static_to_depth(val.tags, 1) diff --git a/vlib/v/gen/c/testdata/autofree_comptime_selector_clone.out b/vlib/v/gen/c/testdata/autofree_comptime_selector_clone.out new file mode 100644 index 000000000..9766475a4 --- /dev/null +++ b/vlib/v/gen/c/testdata/autofree_comptime_selector_clone.out @@ -0,0 +1 @@ +ok diff --git a/vlib/v/gen/c/testdata/autofree_comptime_selector_clone.vv b/vlib/v/gen/c/testdata/autofree_comptime_selector_clone.vv new file mode 100644 index 000000000..86f2aeac6 --- /dev/null +++ b/vlib/v/gen/c/testdata/autofree_comptime_selector_clone.vv @@ -0,0 +1,32 @@ +// vtest vflags: -autofree +struct Item { + name string + tags []string +} + +fn process[T](val T) string { + mut result := '' + $for field in T.fields { + $if field.unaliased_typ is string { + name := val.$(field.name) + result += name + } $else $if field.unaliased_typ is []string { + arr := val.$(field.name) + for tag in arr { + result += tag + } + } + } + return result +} + +fn main() { + item := Item{ + name: 'test' + tags: ['a', 'b', 'c'] + } + assert process(item) == 'testabc' + assert item.name == 'test' + assert item.tags == ['a', 'b', 'c'] + println('ok') +} diff --git a/vlib/v/tests/comptime/comptime_selector_map_move_test.v b/vlib/v/tests/comptime/comptime_selector_map_move_test.v new file mode 100644 index 000000000..dd8cd46a4 --- /dev/null +++ b/vlib/v/tests/comptime/comptime_selector_map_move_test.v @@ -0,0 +1,27 @@ +struct MapHolder { + values map[string]string +} + +fn make_map_holder() MapHolder { + return MapHolder{ + values: { + 'a': 'b' + } + } +} + +fn first_map_from_temporary[T]() map[string]string { + $for field in T.fields { + $if field.unaliased_typ is map[string]string { + values := make_map_holder().$(field.name) + return values + } + } + return {} +} + +fn test_comptime_selector_map_move_from_temporary() { + assert first_map_from_temporary[MapHolder]() == { + 'a': 'b' + } +} -- 2.39.5