From 92a2ba8fd56c8d476e229695e7dcf324e9e44ceb Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 14:52:06 +0300 Subject: [PATCH] cgen: fix segfault or error c code when shared variable initialized (fixes #26379) --- vlib/v/gen/c/assign.v | 5 ++ vlib/v/gen/c/cgen.v | 6 +- vlib/v/tests/concurrency/shared_assign_test.v | 62 +++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 60c974ccf..7a1b15084 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -83,6 +83,11 @@ fn (mut g Gen) expr_in_value_context(expr ast.Expr, value_type ast.Type, expecte else {} } + if expected_type.has_flag(.shared_f) && !value_type.has_flag(.shared_f) && value_type.is_ptr() + && !expected_type.has_option_or_result() { + g.expr_with_cast(expr_copy, value_type, expected_type) + return + } g.expr(expr_copy) } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 710a1ecb9..4d12ffd64 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4691,9 +4691,6 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ if expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f) && !expected_type.has_option_or_result() { shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*` - if got_type_raw.is_ptr() { - g.error('cannot convert reference to `shared`', expr.pos()) - } if exp_sym.kind == .array { g.writeln('(${shared_styp}*)__dup_shared_array(&(${shared_styp}){.mtx = {0}, .val =') } else if exp_sym.kind == .map { @@ -4703,6 +4700,9 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ } old_is_shared := g.is_shared g.is_shared = false + if got_type_raw.is_ptr() { + g.write('*'.repeat(got_type_raw.nr_muls())) + } g.expr(expr) g.is_shared = old_is_shared g.writeln('}, sizeof(${shared_styp}))') diff --git a/vlib/v/tests/concurrency/shared_assign_test.v b/vlib/v/tests/concurrency/shared_assign_test.v index 8382d2030..7026d4e8f 100644 --- a/vlib/v/tests/concurrency/shared_assign_test.v +++ b/vlib/v/tests/concurrency/shared_assign_test.v @@ -1,3 +1,8 @@ +import os +import rand + +const vexe = @VEXE + struct Foo { mut: a int @@ -107,3 +112,60 @@ fn test_re_assign_map() { assert m[0] == 1 } } + +struct SharedAssignStructA { + commands []string +} + +struct SharedAssignStructB { + s SharedAssignStructA +} + +fn test_assign_from_pointer_ident_with_nested_struct() { + mut src := &SharedAssignStructB{} + shared shared_copy := src + shared spread_copy := SharedAssignStructB{ + ...src + } + rlock shared_copy, spread_copy { + assert shared_copy.s.commands == []string{} + assert spread_copy.s.commands == []string{} + } +} + +fn test_assign_from_pointer_ident_emits_shared_copy_in_c_output() { + tmp_dir := os.join_path(os.vtmp_dir(), 'shared_assign_cgen_${rand.ulid()}') + os.mkdir_all(tmp_dir) or { panic(err) } + defer { + os.rmdir_all(tmp_dir) or {} + } + source_path := os.join_path(tmp_dir, 'main.v') + c_path := os.join_path(tmp_dir, 'main.c') + source := [ + 'module main', + '', + 'struct StructA {', + '\tcommands []string', + '}', + '', + 'struct StructB {', + '\ts StructA', + '}', + '', + 'fn main() {', + '\tmut src := &StructB{}', + '\tshared copy := src', + '\trlock copy {', + '\t\tprintln(copy.s)', + '\t}', + '}', + ].join_lines() + os.write_file(source_path, source) or { panic(err) } + res := + os.execute('${os.quoted_path(vexe)} -o ${os.quoted_path(c_path)} ${os.quoted_path(source_path)}') + assert res.exit_code == 0, res.output + csrc := os.read_file(c_path) or { panic(err) } + assert csrc.contains('__shared__main__StructB* copy = (__shared__main__StructB*)__dup__shared__main__StructB') + assert csrc.contains('*src}, sizeof(__shared__main__StructB))') + assert !csrc.contains('__shared__main__StructB* copy = src;') +} -- 2.39.5