From 79c0a15a95aa2a2479bd9c7eef56f7bcd8960f22 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 15:23:01 +0300 Subject: [PATCH] ast: fix assignment error with recursive definition in sum types (fixes #14160) --- vlib/v/ast/table.v | 7 ++++ vlib/v/gen/c/assign.v | 5 +-- vlib/v/gen/c/cgen.v | 35 +++++++++++++------ .../map_sumtype_value_init_test.v | 32 +++++++++++++++++ 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 8db0fd0f4..83ba63308 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1959,6 +1959,13 @@ fn (t &Table) sumtype_check_variant_in_type(parent_info SumType, variant Type, i return true } } + if !is_as { + for v in parent_info.variants { + if t.can_implicit_array_cast(variant, v) { + return true + } + } + } return false } diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index ca757822c..798cc8407 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -365,8 +365,9 @@ fn (mut g Gen) expr_opt_with_cast(expr ast.Expr, expr_typ ast.Type, ret_typ ast. if ret_sym.kind == .sum_type { exp_sym := g.table.sym(expr_typ) fname := g.get_sumtype_casting_fn(expr_typ, ret_typ) - g.call_cfn_for_casting_expr(fname, expr, ret_typ, expr_typ, ret_sym.cname, - expr_typ.is_ptr(), exp_sym.kind == .function, g.styp(expr_typ)) + g.call_cfn_for_casting_expr(fname, expr, ret_typ, expr_typ, expr_typ, + ret_sym.cname, expr_typ.is_ptr(), exp_sym.kind == .function, + g.styp(expr_typ)) } else { g.write('*((${g.base_type(expr_typ)}*)') g.expr(expr) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index a2c99c575..e5d625c28 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4022,7 +4022,7 @@ fn (mut g Gen) fn_ptr_cast_typ(func ast.FnType) string { return g.fn_ptr_decl_str(func, ptr_name).replace_once(ptr_name, '') } -fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Type, got ast.Type, exp_styp string, +fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Type, got ast.Type, actual_got ast.Type, exp_styp string, got_is_ptr bool, got_is_fn bool, got_styp string) { mut rparen_n := 1 mut mutable_is_mut_arg_pos := 0 @@ -4036,7 +4036,11 @@ fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Ty } else { expr } - expr_is_lvalue := if is_interface_cast { + needs_got_cast := is_sumtype_cast && actual_got != 0 + && g.table.can_implicit_array_cast(actual_got, got) + expr_is_lvalue := if needs_got_cast { + false + } else if is_interface_cast { interface_cast_source_expr.is_lvalue() } else { expr.is_lvalue() @@ -4125,7 +4129,11 @@ fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Ty } else if is_stack_rooted_interface_expr && g.expr_root_ident_unwraps_option_or_result(expr) { g.inside_assign_fn_var = true } - g.expr(expr) + if needs_got_cast { + g.expr_with_cast(expr, actual_got, got) + } else { + g.expr(expr) + } g.inside_assign_fn_var = old_inside_assign_fn_var g.left_is_opt = old_left_is_opt g.inside_sumtype_cast = old_inside_sumtype_cast @@ -4185,6 +4193,11 @@ fn (g &Gen) find_matching_sumtype_variant(expected_type ast.Type, got_type ast.T return variant } } + for variant in variants { + if g.table.can_implicit_array_cast(got_type, variant) { + return variant + } + } if got_type.is_any_kind_of_pointer() { deref_got_type := got_type.deref() for variant in variants { @@ -4434,8 +4447,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ if exp_sym.info.is_generic { fname = g.generic_fn_name(exp_sym.info.concrete_types, fname) } - g.call_cfn_for_casting_expr(fname, expr, expected_type, got_type, exp_styp, true, - false, got_styp) + g.call_cfn_for_casting_expr(fname, expr, expected_type, got_type, got_type, exp_styp, + true, false, got_styp) g.inside_cast_in_heap-- } else { got_styp := g.cc_type(got_type, true) @@ -4482,12 +4495,12 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ if suppress_auto_heap_deref { old_inside_assign_fn_var := g.inside_assign_fn_var g.inside_assign_fn_var = true - g.call_cfn_for_casting_expr(fname, expr, expected_type, got_type, exp_styp, - effective_got_is_ptr, got_is_fn, got_styp) + g.call_cfn_for_casting_expr(fname, expr, expected_type, got_type, got_type, + exp_styp, effective_got_is_ptr, got_is_fn, got_styp) g.inside_assign_fn_var = old_inside_assign_fn_var } else { - g.call_cfn_for_casting_expr(fname, expr, expected_type, got_type, exp_styp, - effective_got_is_ptr, got_is_fn, got_styp) + g.call_cfn_for_casting_expr(fname, expr, expected_type, got_type, got_type, + exp_styp, effective_got_is_ptr, got_is_fn, got_styp) } } return @@ -4600,8 +4613,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ return } else { g.call_cfn_for_casting_expr(fname, expr, expected_type, sumtype_got_type, - unwrapped_exp_sym.cname, sumtype_cast_got_is_ptr, sumtype_got_is_fn, - sumtype_got_styp) + actual_sumtype_got_type, unwrapped_exp_sym.cname, sumtype_cast_got_is_ptr, + sumtype_got_is_fn, sumtype_got_styp) } } return diff --git a/vlib/v/tests/builtin_maps/map_sumtype_value_init_test.v b/vlib/v/tests/builtin_maps/map_sumtype_value_init_test.v index a1da90ffe..402fb97d0 100644 --- a/vlib/v/tests/builtin_maps/map_sumtype_value_init_test.v +++ b/vlib/v/tests/builtin_maps/map_sumtype_value_init_test.v @@ -1,10 +1,17 @@ type Child = Tree | string +type RecursiveChild = int | []RecursiveChild + struct Tree { name string child map[string]Child } +struct RecursiveFields { + a int + b []int +} + fn test_map_sumtype_value_init() { tree := Tree{ name: 'foo' @@ -16,3 +23,28 @@ fn test_map_sumtype_value_init() { ret := tree.child['a'] or { Child('') } assert ret == Child('value') } + +fn test_recursive_sumtype_array_variant_assignment_in_map() { + ints := [1, 2, 3] + mut got := map[string]RecursiveChild{} + got['nums'] = ints + assert got == { + 'nums': RecursiveChild([RecursiveChild(1), RecursiveChild(2), RecursiveChild(3)]) + } +} + +fn test_recursive_sumtype_array_variant_comptime_selector_assignment_in_map() { + data := RecursiveFields{120, [130, 140]} + mut got := map[string]RecursiveChild{} + $for field in RecursiveFields.fields { + $if field.typ is int { + got[field.name] = data.$(field.name) + } $else $if field.typ is []int { + got[field.name] = data.$(field.name) + } + } + assert got == { + 'a': RecursiveChild(120) + 'b': RecursiveChild([RecursiveChild(130), RecursiveChild(140)]) + } +} -- 2.39.5