From 1cd3c9cb315a4199767de4b48dc082b4fe623a7b Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 26 Feb 2026 10:37:14 +0300 Subject: [PATCH] cgen: fix modifying a struct pointer by passing it as mut to a function (fixes #17788) --- vlib/v/gen/c/assign.v | 13 +++++- .../mut_arg_struct_pointer_rebind_test.v | 42 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 vlib/v/tests/mut_arg_struct_pointer_rebind_test.v diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 913710c02..52ecec01b 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -678,6 +678,13 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { mut op_expected_right := ast.no_type is_shared_re_assign := !is_decl && node.left_types[i].has_flag(.shared_f) && left is ast.Ident && left_sym.kind in [.array, .map, .struct] + mut is_mut_arg_pointer_rebind := false + // Keep pointer traversal assignments on auto-deref vars as local pointer rebinds. + if !is_decl && !is_shared_re_assign && !var_type.has_flag(.option) + && var_type.is_any_kind_of_pointer() && unwrapped_val_type.is_any_kind_of_pointer() + && left.is_auto_deref_var() && !val.is_auto_deref_var() && node.op == .assign { + is_mut_arg_pointer_rebind = true + } if node.op == .plus_assign && unaliased_right_sym.kind == .string { if mut left is ast.IndexExpr { if g.table.sym(left.left_type).kind == .array_fixed { @@ -872,7 +879,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { g.op_arg(left, op_expected_left, var_type) } else { if !is_decl && !is_shared_re_assign && left.is_auto_deref_var() - && !var_type.has_flag(.option) { + && !var_type.has_flag(.option) && !is_mut_arg_pointer_rebind { g.write('*') } if node_.op == .assign && var_type.has_flag(.option_mut_param_t) { @@ -1167,7 +1174,9 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { if op_overloaded { g.op_arg(val, op_expected_right, val_type) } else { - exp_type := if var_type.is_ptr() + exp_type := if is_mut_arg_pointer_rebind { + var_type + } else if var_type.is_ptr() && (left.is_auto_deref_var() || var_type.has_flag(.shared_f)) { var_type.deref() } else { diff --git a/vlib/v/tests/mut_arg_struct_pointer_rebind_test.v b/vlib/v/tests/mut_arg_struct_pointer_rebind_test.v new file mode 100644 index 000000000..ae7e0376d --- /dev/null +++ b/vlib/v/tests/mut_arg_struct_pointer_rebind_test.v @@ -0,0 +1,42 @@ +module main + +@[heap] +struct Node { +mut: + children []&Node +} + +@[heap] +struct Tree { +mut: + root &Node +} + +fn find_leaf(mut node Node) &Node { + for { + if node.children.len == 0 { + return node + } + node = node.children[0] + } + return node +} + +fn test_mut_struct_arg_passed_as_pointer_keeps_original_tree_root() { + mut n1 := &Node{} + mut t := &Tree{ + root: n1 + } + + mut n2 := &Node{} + t.root.children << n2 + mut n3 := &Node{} + t.root.children[0].children << n3 + + found := find_leaf(mut t.root) + assert found == n3 + assert t.root.children.len == 1 + assert t.root.children[0] == n2 + assert t.root.children[0].children.len == 1 + assert t.root.children[0].children[0] == n3 +} -- 2.39.5