From eaad37911979ce7b91f3cac7215b034927e57d30 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sun, 24 May 2026 21:25:57 +0300 Subject: [PATCH] cgen: guard C extern fn decls with #ifndef to coexist with macros (fix #27098) (#27236) --- vlib/v/gen/c/fn.v | 10 ++++++-- .../c_extern_macro_name_guard_nix.c.must_have | 8 +++++++ .../testdata/c_extern_macro_name_guard_nix.vv | 24 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 vlib/v/gen/c/testdata/c_extern_macro_name_guard_nix.c.must_have create mode 100644 vlib/v/gen/c/testdata/c_extern_macro_name_guard_nix.vv diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index dd412a588..c87e60048 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1312,7 +1312,11 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { } } } else if g.inside_c_extern { - c_extern_fn_header := 'extern ${type_name} ${fn_attrs}${name.all_after_first('C__')}(' + c_name := name.all_after_first('C__') + // Guard with #ifndef so the declaration is skipped when the C symbol + // is actually a preprocessor macro (e.g. WIFSTOPPED from ). + g.definitions.writeln('#ifndef ${c_name}') + c_extern_fn_header := 'extern ${type_name} ${fn_attrs}${c_name}(' g.definitions.write_string(c_extern_fn_header) } else { if !(node.is_pub || g.pref.is_debug) { @@ -1360,7 +1364,9 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { && !g.pref.is_test) || skip { // Just a function header. Builtin function bodies are defined in builtin.o g.definitions.writeln(');') // NO BODY') - if !g.inside_c_extern { + if g.inside_c_extern { + g.definitions.writeln('#endif') + } else { g.writeln(');') } return diff --git a/vlib/v/gen/c/testdata/c_extern_macro_name_guard_nix.c.must_have b/vlib/v/gen/c/testdata/c_extern_macro_name_guard_nix.c.must_have new file mode 100644 index 000000000..4277015b2 --- /dev/null +++ b/vlib/v/gen/c/testdata/c_extern_macro_name_guard_nix.c.must_have @@ -0,0 +1,8 @@ +#ifndef WIFSTOPPED +extern bool WIFSTOPPED(i32 _d1); +#ifndef WCOREDUMP +extern bool WCOREDUMP(i32 _d1); +#ifndef WIFCONTINUED +extern bool WIFCONTINUED(i32 _d1); +#ifndef WSTOPSIG +extern int WSTOPSIG(i32 _d1); diff --git a/vlib/v/gen/c/testdata/c_extern_macro_name_guard_nix.vv b/vlib/v/gen/c/testdata/c_extern_macro_name_guard_nix.vv new file mode 100644 index 000000000..5a47a3d6e --- /dev/null +++ b/vlib/v/gen/c/testdata/c_extern_macro_name_guard_nix.vv @@ -0,0 +1,24 @@ +// vtest vflags: -no-parallel +// Regression test for https://github.com/vlang/v/issues/27098 . +// When a C function name (e.g. `WIFSTOPPED` from ) is actually +// a preprocessor macro, V should still be able to emit an `extern` forward +// declaration without breaking C compilation. The declaration is guarded by +// `#ifndef`, so the C preprocessor skips it whenever the symbol is a macro. + +#flag -lc + +fn C.WIFSTOPPED(i32) bool + +fn C.WCOREDUMP(i32) bool + +fn C.WIFCONTINUED(i32) bool + +fn C.WSTOPSIG(i32) int + +fn main() { + x := i32(0) + _ = C.WIFSTOPPED(x) + _ = C.WCOREDUMP(x) + _ = C.WIFCONTINUED(x) + _ = C.WSTOPSIG(x) +} -- 2.39.5