From 5a4810b4064169bbb05803b6eed8e9b50c2ac6e3 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 23 Apr 2026 22:07:34 +0300 Subject: [PATCH] cgen: fix Fatal error of compilation generic structs (fixes #26675) --- vlib/v/gen/c/cgen.v | 1 + vlib/v/gen/c/struct.v | 34 +++++++++++++++++-- ...s_mutual_recursive_struct_reference_test.v | 25 ++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 58d8861c7..e88ed2237 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -278,6 +278,7 @@ mut: cur_fn &ast.FnDecl = unsafe { nil } // same here cur_lock ast.LockExpr cur_struct_init_typ ast.Type + zero_struct_init_stack []ast.Type autofree_methods map[ast.Type]string generated_free_methods map[ast.Type]bool generated_free_fn_names map[string]bool diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index e46b0c72d..e27517387 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -60,6 +60,15 @@ fn (mut g Gen) struct_init(node ast.StructInit) { node.typ } mut sym := g.table.final_sym(unwrapped_typ) + old_cur_struct_init_typ := g.cur_struct_init_typ + if node.typ != 0 { + g.cur_struct_init_typ = node.typ + } + g.zero_struct_init_stack << g.zero_struct_init_type(struct_init_typ) + defer { + g.cur_struct_init_typ = old_cur_struct_init_typ + g.zero_struct_init_stack.delete_last() + } if sym.kind == .sum_type { if unwrapped_typ.is_ptr() { // handle promotions to a sumtype for generic functions like this one: `fn (d Struct) a[T]() T { return d }` @@ -655,6 +664,27 @@ fn (mut g Gen) init_shared_field(field ast.StructField) { g.write('}, sizeof(${shared_styp}))') } +fn (mut g Gen) zero_struct_init_type(typ ast.Type) ast.Type { + mut resolved_typ := g.unwrap_generic(g.recheck_concrete_type(typ)) + if resolved_typ == 0 { + resolved_typ = typ + } + return resolved_typ.clear_option_and_result().clear_flag(.shared_f).clear_flag(.atomic_f) +} + +fn (mut g Gen) zero_struct_init_would_recurse(typ ast.Type) bool { + resolved_typ := g.zero_struct_init_type(typ) + return resolved_typ in g.zero_struct_init_stack +} + +fn (mut g Gen) write_zero_struct_init(default_init ast.StructInit) { + if g.zero_struct_init_would_recurse(default_init.typ) { + g.write(g.type_default(default_init.typ)) + return + } + g.struct_init(default_init) +} + fn (mut g Gen) zero_struct_field(field ast.StructField) bool { old_inside_cast_in_heap := g.inside_cast_in_heap g.inside_cast_in_heap = 0 @@ -700,7 +730,7 @@ fn (mut g Gen) zero_struct_field(field ast.StructField) bool { g.expr_with_tmp_var(default_init, field.typ, field.typ, tmp_var, true) } } else { - g.struct_init(default_init) + g.write_zero_struct_init(default_init) } return true } else if sym.language == .v && !field.typ.is_ptr() && sym.mod != 'builtin' @@ -709,7 +739,7 @@ fn (mut g Gen) zero_struct_field(field ast.StructField) bool { typ: field.typ } g.write('.${field_name} = ') - g.struct_init(default_init) + g.write_zero_struct_init(default_init) return true } } diff --git a/vlib/v/tests/generics/generics_mutual_recursive_struct_reference_test.v b/vlib/v/tests/generics/generics_mutual_recursive_struct_reference_test.v index 228924685..95f0b5303 100644 --- a/vlib/v/tests/generics/generics_mutual_recursive_struct_reference_test.v +++ b/vlib/v/tests/generics/generics_mutual_recursive_struct_reference_test.v @@ -14,3 +14,28 @@ fn test_mutual_recursive_generic_struct_reference() { foo := foo_new[int]() assert foo != unsafe { nil } } + +pub struct Issue26675List[T] { + front ?&Issue26675ListNode[T] + back ?&Issue26675ListNode[T] +} + +pub struct Issue26675ListNode[T] { + value T + list ?&Issue26675List[T] + prev ?&Issue26675ListNode[T] + next ?&Issue26675ListNode[T] +} + +pub fn Issue26675ListNode.new[T]() &Issue26675ListNode[T] { + return &Issue26675ListNode[T]{} +} + +struct Issue26675Foo { + node &Issue26675ListNode[Issue26675Foo] +} + +fn test_recursive_generic_struct_instantiation_does_not_crash() { + node := Issue26675ListNode.new[Issue26675Foo]() + assert node != unsafe { nil } +} -- 2.39.5