From f4f6f6e0f7dfdc14f720d1af0ec8e82bbffa5225 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 14 Apr 2026 12:45:33 +0300 Subject: [PATCH] cgen: Instantiating struct with global on heap causes c error (fixes #22970) --- vlib/v/gen/c/assign.v | 46 +++++++++++++++++++ ...truct_field_default_shadowed_global_test.v | 38 +++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 vlib/v/tests/structs/struct_field_default_shadowed_global_test.v diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index d7eed6781..7957e08ff 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -118,6 +118,34 @@ fn assign_expr_unwraps_option_or_result(expr ast.Expr) bool { } } +fn (mut g Gen) decl_assign_struct_init_needs_tmp(expr ast.Expr) bool { + node := match expr { + ast.StructInit { + expr + } + ast.ParExpr { + if expr.expr is ast.StructInit { + expr.expr + } else { + return false + } + } + else { + return false + } + } + sym := g.table.final_sym(g.unwrap_generic(g.recheck_concrete_type(node.typ))) + if sym.info !is ast.Struct { + return false + } + info := sym.info as ast.Struct + if node.no_keys { + return node.init_fields.len < info.fields.len + } + init_field_names := node.init_fields.map(it.name) + return info.fields.any(it.name !in init_field_names) +} + fn (mut g Gen) gen_self_recursing_anon_fn_capture_patch(left ast.Expr, anon_fn ast.AnonFn) { if left !is ast.Ident || anon_fn.inherited_vars.len == 0 { return @@ -1665,6 +1693,19 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { g.expr(left) g.write(',') } + mut decl_tmp_var := '' + mut decl_stmt_str := '' + if is_decl && g.inside_ternary == 0 && !node.is_static && !var_type.has_flag(.shared_f) + && g.decl_assign_struct_init_needs_tmp(val) { + decl_stmt_str = g.go_before_last_stmt().trim_space() + g.empty_line = true + decl_tmp_var = g.new_tmp_var() + g.write('${styp} ') + if is_auto_heap && !(val_type.is_ptr() && val_type.has_flag(.option)) { + g.write('*') + } + g.write('${decl_tmp_var} ${op} ') + } mut cloned := false if g.is_autofree { if right_sym.kind in [.array, .string] && !unwrapped_val_type.has_flag(.shared_f) { @@ -1946,6 +1987,11 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { g.write(' })') g.is_arraymap_set = g.cur_indexexpr.len > 0 } + if decl_tmp_var != '' { + g.writeln(';') + g.write2(decl_stmt_str, ' ') + g.write(decl_tmp_var) + } g.is_shared = false } g.right_is_opt = false diff --git a/vlib/v/tests/structs/struct_field_default_shadowed_global_test.v b/vlib/v/tests/structs/struct_field_default_shadowed_global_test.v new file mode 100644 index 000000000..59d36d78a --- /dev/null +++ b/vlib/v/tests/structs/struct_field_default_shadowed_global_test.v @@ -0,0 +1,38 @@ +@[has_globals] +module main + +struct Game { +mut: + object_id u32 +} + +__global ( + g Game +) + +struct StackGameObject { +mut: + id u32 = g.object_id++ +} + +@[heap] +struct HeapGameObject { +mut: + id u32 = g.object_id++ +} + +fn reset_game_state() { + g = Game{} +} + +fn test_stack_struct_field_default_can_use_shadowed_global() { + reset_game_state() + mut g := StackGameObject{} + assert g.id == 0 +} + +fn test_heap_struct_field_default_can_use_shadowed_global() { + reset_game_state() + mut g := HeapGameObject{} + assert g.id == 0 +} -- 2.39.5