vlang

/

v Public
0 commits 39 issues 0 pull requests 0 contributors Discussions Projects CI

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) and at_exit in v.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.