From 005e49db7134829bdcd2f29b16273fee70f81a84 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 27 Jun 2026 15:52:45 +0300 Subject: [PATCH] markused: mark mut-receiver interface implementors (fix skip-unused dropping anon fn) (#27380) --- vlib/v/markused/walker.v | 5 +++ .../interface_mut_receiver_anon_fn.run.out | 1 + .../interface_mut_receiver_anon_fn.vv | 34 +++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 vlib/v/tests/skip_unused/interface_mut_receiver_anon_fn.run.out create mode 100644 vlib/v/tests/skip_unused/interface_mut_receiver_anon_fn.vv diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index 76db5e7fc..20a5b9a19 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -3177,6 +3177,11 @@ pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) { } } ast.Interface { + // Include implementors that require a mutable receiver (stored in + // `mut_types`, not `types`); otherwise interface methods reached only + // via dispatch on a `mut` receiver are never walked, and symbols they + // reference (e.g. an anon fn passed to a C callback) get pruned by + // -skip-unused. See https://github.com/vlang/v/issues/27354 for typ in isym.info.implementor_types(true) { if typ == ast.map_type { w.features.used_maps++ diff --git a/vlib/v/tests/skip_unused/interface_mut_receiver_anon_fn.run.out b/vlib/v/tests/skip_unused/interface_mut_receiver_anon_fn.run.out new file mode 100644 index 000000000..19f86f493 --- /dev/null +++ b/vlib/v/tests/skip_unused/interface_mut_receiver_anon_fn.run.out @@ -0,0 +1 @@ +done diff --git a/vlib/v/tests/skip_unused/interface_mut_receiver_anon_fn.vv b/vlib/v/tests/skip_unused/interface_mut_receiver_anon_fn.vv new file mode 100644 index 000000000..c5dee2cca --- /dev/null +++ b/vlib/v/tests/skip_unused/interface_mut_receiver_anon_fn.vv @@ -0,0 +1,34 @@ +module main + +// Regression test for https://github.com/vlang/v/issues/27354 +// An anon fn passed to a C callback inside a method that is reached only via +// interface dispatch on a `mut` receiver must be marked used by markused, +// otherwise -skip-unused (the default) drops its definition and the generated +// C fails with `use of undeclared identifier 'anon_fn_...'`. + +fn C.atexit(cb voidptr) int + +interface Handler { + setup() +} + +struct Impl { +mut: + n int +} + +fn (mut i Impl) setup() { + C.atexit(fn () {}) +} + +fn run(mut h Handler) { + h.setup() +} + +fn main() { + mut impl := Impl{ + n: 99 + } + run(mut impl) + println('done') +} -- 2.39.5