cgen, skip-unused: anon fn passed to a C callback (via interface-dispatched method) is not marked used #21
Description
An anonymous function passed as a C callback argument is referenced via closure_create in the generated C, but its definition is omitted by -skip-unused (the default), so the C compiler fails with use of undeclared identifier 'anon_fn_...'. The anon fn is reached only through a C function-pointer call inside a method that is itself dispatched via an interface, and markused never marks it used.
Compiling the same program with -no-skip-unused succeeds, which pins the fault to the markused / skip-unused pass.
This is the root cause of several recurring auto-submitted reports (e.g. vlib/net/mbedtls/ssl_connection.c.v SSLListener.init_sni passing a capturing closure to C.mbedtls_ssl_conf_sni, and at_exit(fn () {...})).
V version: V 0.5.1, current master (5e36217f8)
OS/Backend: reproduced on macOS arm64; originally reported on Linux/FreeBSD.
Reproducer
module main
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')
}
Compile with:
v file.v
Current behavior
error: use of undeclared identifier 'anon_fn_..._145'
atexit(builtin__closure__closure_create(anon_fn_..._145, ...))
The generated C emits the closure context struct and the closure_create(...) call site, but neither a forward declaration nor a body for anon_fn_..._145.
Building with -no-skip-unused compiles and runs cleanly, confirming the anon fn is dropped by the used-functions analysis.
Expected behavior
The anon fn reached through the C callback should be marked used and emitted, so the program compiles under the default -skip-unused.
Notes
- Reported via the bugs.vlang.io crash reporter. Real-world instances: mbedtls SNI callback (
anon_fn_..._mbedtls_ssl_context_..._8883) andat_exitinv.dotgraph(anon_fn_..._257__81). - The combination that triggers it: the closure is passed to a
C.function pointer, and the enclosing method is reached only via interface dispatch (so markused must follow the interface method to find the anon fn).[!NOTE] You can use the 👍 reaction to increase the issue's priority for developers.Please note that only the 👍 reaction to the issue itself counts as a vote. Other reactions and those to comments will not be taken into account.