From 4a4f703299c356e36ee08804bb8a65b91be64e0e Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 15 Apr 2026 15:58:24 +0300 Subject: [PATCH] cgen: fix c backend bug on tuple reassign to struct local (fixes #26594) --- vlib/v/gen/c/assign.v | 7 +- ...tuple_reassign_to_heap_struct_local_test.v | 83 +++++++++++++++++++ 2 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 vlib/v/tests/assign/tuple_reassign_to_heap_struct_local_test.v diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 7dd7ffd9a..37eb4ce1e 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -2213,6 +2213,7 @@ fn (mut g Gen) gen_multi_return_assign(node &ast.AssignStmt, return_type ast.Typ } left_type := if recompute_types { mr_types[i] } else { node.left_types[i] } styp := if ident.name in g.defer_vars { '' } else { g.styp(left_type) } + needs_auto_heap_alloc := is_auto_heap && node.op == .decl_assign if node.op == .decl_assign { g.write('${styp} ') } @@ -2229,7 +2230,7 @@ fn (mut g Gen) gen_multi_return_assign(node &ast.AssignStmt, return_type ast.Typ } if left_type.has_flag(.option) { base_typ := g.base_type(left_type) - tmp_var := if is_auto_heap { + tmp_var := if needs_auto_heap_alloc { if aligned != 0 { 'HEAP_align(${styp}, ${mr_var_name}.arg${i}, ${aligned})' } else { @@ -2261,7 +2262,7 @@ fn (mut g Gen) gen_multi_return_assign(node &ast.AssignStmt, return_type ast.Typ 'memcpy(&${g.expr_string(lx)}, &${mr_var_name}.arg${i}, sizeof(${styp}));') } else { if cur_indexexpr != -1 { - if is_auto_heap { + if needs_auto_heap_alloc { if aligned != 0 { g.writeln('HEAP_align(${styp}, ${mr_var_name}.arg${i}, ${aligned}) });') } else { @@ -2274,7 +2275,7 @@ fn (mut g Gen) gen_multi_return_assign(node &ast.AssignStmt, return_type ast.Typ } g.cur_indexexpr.delete(cur_indexexpr) } else { - if is_auto_heap { + if needs_auto_heap_alloc { if aligned != 0 { g.writeln(' = HEAP_align(${styp}, ${mr_var_name}.arg${i}, ${aligned});') } else { diff --git a/vlib/v/tests/assign/tuple_reassign_to_heap_struct_local_test.v b/vlib/v/tests/assign/tuple_reassign_to_heap_struct_local_test.v new file mode 100644 index 000000000..3228cec5c --- /dev/null +++ b/vlib/v/tests/assign/tuple_reassign_to_heap_struct_local_test.v @@ -0,0 +1,83 @@ +@[heap; minify] +struct Cfg { + id string + rows []int + meta map[string]string +} + +struct State { + value int +} + +struct CrudState { + value int +} + +struct Caps {} + +struct Store { +mut: + data map[string]State +} + +fn (store &Store) get(id string) ?State { + if id in store.data { + return store.data[id] + } + return none +} + +@[heap] +struct Window { +mut: + store Store +} + +fn resolve_a(cfg Cfg, mut _ Window) (Cfg, State, bool, Caps) { + return cfg, State{ + value: 1 + }, true, Caps{} +} + +fn resolve_b(cfg Cfg, mut _ Window) (Cfg, CrudState) { + return cfg, CrudState{ + value: 2 + } +} + +fn (mut window Window) trigger(cfg Cfg) (Cfg, State, CrudState) { + resolved_cfg0, source_state0, has_source, _ := resolve_a(cfg, mut window) + mut resolved_cfg := resolved_cfg0 + mut source_state := source_state0 + mut crud_state := CrudState{} + crud_enabled := true + if crud_enabled { + resolved_cfg, crud_state = resolve_b(resolved_cfg, mut window) + if has_source { + source_state = window.store.get(resolved_cfg.id) or { source_state } + } + } + return resolved_cfg, source_state, crud_state +} + +fn test_tuple_reassign_to_heap_struct_local() { + mut window := &Window{ + store: Store{ + data: { + 'x': State{ + value: 99 + } + } + } + } + resolved_cfg, source_state, crud_state := window.trigger(Cfg{ + id: 'x' + rows: [1, 2, 3] + meta: { + 'a': 'b' + } + }) + assert resolved_cfg.id == 'x' + assert source_state.value == 99 + assert crud_state.value == 2 +} -- 2.39.5