From 06c31c8aac51f02d246a1ec4a75b5d1315a9024c Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 16:17:29 +0300 Subject: [PATCH] cgen: fix incorrect casting of pointers to interface or sumtype (fixes #24309) --- vlib/v/gen/c/cgen.v | 50 +++++++++++++++++ ...pass_voidptr_as_interface_reference_test.v | 53 +++++++++++++++++++ .../pointers/pointers_multilevel_casts_test.v | 29 ++++++++++ 3 files changed, 132 insertions(+) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index b043a49e7..ffdd92f48 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4209,6 +4209,37 @@ fn (g &Gen) find_matching_sumtype_variant(expected_type ast.Type, got_type ast.T return got_type } +fn (mut g Gen) sumtype_ptr_cast_payload_field(sumtype_typ ast.Type, target_ptr_typ ast.Type) string { + matched_variant := g.find_matching_sumtype_variant(sumtype_typ, target_ptr_typ.deref()) + if g.table.sumtype_has_variant(sumtype_typ, matched_variant, false) { + matched_variant_sym := g.table.sym(matched_variant) + return g.get_sumtype_variant_name(matched_variant, matched_variant_sym) + } + sumtype_info := g.table.final_sym(sumtype_typ).info as ast.SumType + first_variant := g.unwrap_generic(sumtype_info.variants[0]) + first_variant_sym := g.table.sym(first_variant) + return g.get_sumtype_variant_name(first_variant, first_variant_sym) +} + +fn (mut g Gen) write_payload_pointer_from_ptr_wrapper(expr ast.Expr, wrapper_typ ast.Type, + target_ptr_typ ast.Type) { + wrapper_sym := g.table.final_sym(wrapper_typ) + g.write('((') + g.expr(expr) + match wrapper_sym.kind { + .interface { + g.write(')->_object') + } + .sum_type { + field_name := g.sumtype_ptr_cast_payload_field(wrapper_typ, target_ptr_typ) + g.write(')->_${field_name}') + } + else {} + } + + g.write(')') +} + // use instead of expr() when you need a var to use as reference fn (mut g Gen) expr_with_var(expr ast.Expr, expected_type ast.Type, do_cast bool) string { stmt_str := g.go_before_last_stmt().trim_space() @@ -8552,6 +8583,25 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) { return } styp := g.styp(node_typ) + source_wrapper_typ := if expr_type.is_ptr() && expr_type.nr_muls() == 1 { + g.unwrap_generic(expr_type.deref()) + } else { + ast.void_type + } + source_expr_is_wrapper_address := node.expr is ast.PrefixExpr + && (node.expr as ast.PrefixExpr).op == .amp + source_wrapper_is_payload := source_wrapper_typ != ast.void_type + && g.table.final_sym(source_wrapper_typ).kind in [.interface, .sum_type] + target_points_to_wrapper_layout := node_typ.is_ptr() && source_wrapper_typ != ast.void_type + && g.styp(node_typ.deref()) == g.styp(source_wrapper_typ) + if node_typ.is_ptr() && expr_type.is_ptr() && source_wrapper_is_payload + && !source_expr_is_wrapper_address && !target_points_to_wrapper_layout + && final_sym.kind !in [.interface, .sum_type] { + g.write('((${styp})') + g.write_payload_pointer_from_ptr_wrapper(node.expr, source_wrapper_typ, node_typ) + g.write(')') + return + } if (g.pref.translated || g.file.is_translated) && sym.kind == .function { // TODO: handle the type in fn casts, not just exprs /* diff --git a/vlib/v/tests/interfaces/interface_edge_cases/pass_voidptr_as_interface_reference_test.v b/vlib/v/tests/interfaces/interface_edge_cases/pass_voidptr_as_interface_reference_test.v index 04619ed7f..9e7fc295e 100644 --- a/vlib/v/tests/interfaces/interface_edge_cases/pass_voidptr_as_interface_reference_test.v +++ b/vlib/v/tests/interfaces/interface_edge_cases/pass_voidptr_as_interface_reference_test.v @@ -17,3 +17,56 @@ fn test_passing_voidptr_as_an_interface_reference() { // but perhaps it should be forced by the compiler to be in unsafe{} assert f(unsafe { voidptr(u64(0)) }) == '&nil' } + +interface IActionable24309 { +mut: + count int +} + +struct Stack24309 { +mut: + count int = 4 +} + +fn (mut s Stack24309) inc() { + s.count++ +} + +type ActionFn24309 = fn (item &IActionable24309) + +struct Action24309 { + actionable &IActionable24309 + action_fn ActionFn24309 = unsafe { nil } +} + +struct EventManager24309 { +mut: + receivers map[string][]Action24309 +} + +fn stack_on_test_24309(item &IActionable24309) { + mut stack := unsafe { &Stack24309(item) } + stack.inc() +} + +fn (mut em EventManager24309) on_test_24309() { + unsafe { + for receiver in em.receivers['test'] { + receiver.action_fn(receiver.actionable) + } + } +} + +fn test_pointer_to_interface_cast_uses_underlying_object_pointer() { + stack := Stack24309{} + mut em := EventManager24309{} + action := Action24309{ + actionable: &stack + action_fn: stack_on_test_24309 + } + unsafe { em.receivers['test'] << action } + em.on_test_24309() + em.on_test_24309() + em.on_test_24309() + assert stack.count == 7 +} diff --git a/vlib/v/tests/pointers/pointers_multilevel_casts_test.v b/vlib/v/tests/pointers/pointers_multilevel_casts_test.v index 806d27155..2e112682d 100644 --- a/vlib/v/tests/pointers/pointers_multilevel_casts_test.v +++ b/vlib/v/tests/pointers/pointers_multilevel_casts_test.v @@ -3,6 +3,27 @@ struct Struct { x int } +struct EventA24309 { +mut: + value int +} + +struct EventB24309 { +mut: + value int +} + +type Event24309 = EventA24309 | EventB24309 + +fn increment_sumtype_value_24309(event &Event24309) int { + unsafe { + mut event_a := &EventA24309(event) + event_a.value++ + event_a.value++ + return event_a.value + } +} + fn test_byte_pointer_casts() { unsafe { pb := &u8(1) @@ -74,3 +95,11 @@ fn test_pointer_casts_with_indexing() { assert &u64(pnumbers)[idx] == 456 } } + +fn test_sumtype_pointer_cast_uses_underlying_variant_pointer() { + mut event := Event24309(EventA24309{ + value: 4 + }) + assert increment_sumtype_value_24309(&event) == 6 + assert (event as EventA24309).value == 6 +} -- 2.39.5