From 689f363cc938bd171256792987aabdba10588f77 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 11 Mar 2026 15:48:37 +0300 Subject: [PATCH] cgen: invalid memory access panic when trying cleanup threads (fixes #22776) --- vlib/v/gen/c/fn.v | 29 +++++++++++++- .../fns/mut_closure_fixed_array_thread_test.v | 39 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 vlib/v/tests/fns/mut_closure_fixed_array_thread_test.v diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index b25c71367..90577e600 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -731,6 +731,19 @@ fn (mut g Gen) closure_ctx(node ast.FnDecl, concrete_types []ast.Type) string { return 'struct _V_${fn_name}_Ctx' } +fn (g &Gen) is_mut_closure_fixed_array(var ast.Param) bool { + return var.is_mut && g.table.final_sym(var.typ).kind == .array_fixed +} + +fn (mut g Gen) mut_closure_fixed_array_field_styp(var ast.Param) string { + info := g.table.final_sym(var.typ).info as ast.ArrayFixed + mut elem_styp := g.styp(info.elem_type.set_nr_muls(0)) + if info.elem_type.is_ptr() { + elem_styp += '*'.repeat(info.elem_type.nr_muls()) + } + return '${elem_styp}*' +} + fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) { is_amp := g.is_amp g.is_amp = false @@ -753,6 +766,18 @@ fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) { mut has_inherited := false mut is_ptr := false var_name := c_name(var.name) + if g.is_mut_closure_fixed_array(var) { + if obj := node.decl.scope.find_var(var.name) { + if obj.has_inherited { + has_inherited = true + g.writeln('.${var_name} = ${closure_ctx}->${var_name},') + } + } + if !has_inherited { + g.writeln('.${var_name} = ${var_name},') + } + continue + } if obj := node.decl.scope.find_var(var.name) { is_ptr = obj.typ.is_ptr() if obj.has_inherited { @@ -826,7 +851,9 @@ fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) { g.definitions.writeln('${ctx_struct} {') for var in node.inherited_vars { var_sym := g.table.sym(var.typ) - if var_sym.info is ast.FnType { + if g.is_mut_closure_fixed_array(var) { + g.definitions.writeln('\t${g.mut_closure_fixed_array_field_styp(var)} ${c_name(var.name)};') + } else if var_sym.info is ast.FnType { mut sig := g.fn_var_signature(var.typ, var_sym.info.func.return_type, var_sym.info.func.params.map(it.typ), c_name(var.name)) g.definitions.writeln('\t' + sig + ';') diff --git a/vlib/v/tests/fns/mut_closure_fixed_array_thread_test.v b/vlib/v/tests/fns/mut_closure_fixed_array_thread_test.v new file mode 100644 index 000000000..2ac9db740 --- /dev/null +++ b/vlib/v/tests/fns/mut_closure_fixed_array_thread_test.v @@ -0,0 +1,39 @@ +module main + +const fixed_array_size = 4 + +fn forward_threads(threads [fixed_array_size]thread) thread { + return threads[0] +} + +fn worker_thread() {} + +fn test_mut_closure_capture_can_update_fixed_array_elements() { + mut values := [fixed_array_size]int{} + update := fn [mut values] () { + values[0] = 42 + values[3] = 7 + } + update() + assert values[0] == 42 + assert values[3] == 7 +} + +fn test_mut_closure_capture_of_fixed_array_sees_late_updates() { + mut values := [fixed_array_size]int{} + read := fn [mut values] () int { + return values[0] + } + values[0] = 99 + assert read() == 99 +} + +fn test_mut_closure_capture_of_fixed_array_of_threads_sees_late_updates() { + mut threads := [fixed_array_size]thread{} + read := fn [mut threads] () thread { + return forward_threads(threads) + } + threads[0] = spawn worker_thread() + assert read() == threads[0] + threads[0].wait() +} -- 2.39.5