From 25432ba0fc4ac50dc97a1053706dd4e0ef464c49 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Wed, 19 Feb 2025 14:47:14 +0000 Subject: [PATCH 1/3] Options to generate uncallable C++ functions. Downstream code generators may need to know about the existence of certain C++ functions even if those functions can't be called. This is counterintuitive but: * A type can't even be allocated if it contains pure virtual functions or if its constructor is private. * A type may not be relocatable if it contains a deleted move constructor. This PR provides command line options to reveal the existence of these functions. Subsequent PRs will announce their special status using the ParseCallbacks mechanism. Part of https://github.com/google/autocxx/issues/124. --- .../tests/uncallable_functions.rs | 46 ++++++++++++++++ .../tests/headers/uncallable_functions.hpp | 9 ++++ bindgen/codegen/mod.rs | 20 ++++--- bindgen/ir/function.rs | 5 +- bindgen/options/cli.rs | 18 +++++++ bindgen/options/mod.rs | 54 +++++++++++++++++++ 6 files changed, 142 insertions(+), 10 deletions(-) create mode 100644 bindgen-tests/tests/expectations/tests/uncallable_functions.rs create mode 100644 bindgen-tests/tests/headers/uncallable_functions.hpp diff --git a/bindgen-tests/tests/expectations/tests/uncallable_functions.rs b/bindgen-tests/tests/expectations/tests/uncallable_functions.rs new file mode 100644 index 0000000000..fddb5d41fc --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/uncallable_functions.rs @@ -0,0 +1,46 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[repr(C)] +pub struct Test__bindgen_vtable { + pub Test_a: unsafe extern "C" fn(this: *mut Test), +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Test { + pub vtable_: *const Test__bindgen_vtable, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of Test"][::std::mem::size_of::() - 8usize]; + ["Alignment of Test"][::std::mem::align_of::() - 8usize]; +}; +unsafe extern "C" { + #[link_name = "\u{1}_ZN4Test1bEv"] + pub fn Test_b(this: *mut Test); +} +unsafe extern "C" { + #[link_name = "\u{1}_ZN4Test1cEv"] + pub fn Test_c(this: *mut Test); +} +impl Default for Test { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl Test { + #[inline] + pub unsafe fn b(&mut self) { + Test_b(self) + } + #[inline] + pub unsafe fn c(&mut self) { + Test_c(self) + } +} +unsafe extern "C" { + #[link_name = "\u{1}_ZN4Test1aEv"] + pub fn Test_a(this: *mut ::std::os::raw::c_void); +} diff --git a/bindgen-tests/tests/headers/uncallable_functions.hpp b/bindgen-tests/tests/headers/uncallable_functions.hpp new file mode 100644 index 0000000000..9187470c65 --- /dev/null +++ b/bindgen-tests/tests/headers/uncallable_functions.hpp @@ -0,0 +1,9 @@ +// bindgen-flags: --generate-deleted-functions --generate-private-functions --generate-pure-virtual-functions --generate-inline-functions -- -x c++ -std=c++14 + +class Test { +public: + virtual void a() = 0; + void b() = delete; +private: + void c() {} +}; diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index f5518e432d..8c623ff952 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4564,14 +4564,20 @@ impl CodeGenerator for Function { } } - // Pure virtual methods have no actual symbol, so we can't generate - // something meaningful for them. - let is_dynamic_function = match self.kind() { - FunctionKind::Method(ref method_kind) - if method_kind.is_pure_virtual() => - { - return None; + let is_pure_virtual = match self.kind() { + FunctionKind::Method(ref method_kind) => { + method_kind.is_pure_virtual() } + _ => false, + }; + if is_pure_virtual && !ctx.options().generate_pure_virtual_functions { + // Pure virtual methods have no actual symbol, so we can't generate + // something meaningful for them. Downstream code postprocessors + // might want to find out about them. + return None; + } + + let is_dynamic_function = match self.kind() { FunctionKind::Function => { ctx.options().dynamic_library_name.is_some() } diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs index 83b748a5f4..ccae390278 100644 --- a/bindgen/ir/function.rs +++ b/bindgen/ir/function.rs @@ -731,8 +731,7 @@ impl ClangSubItemParser for Function { if visibility != CXVisibility_Default { return Err(ParseError::Continue); } - - if cursor.access_specifier() == CX_CXXPrivate { + if cursor.access_specifier() == CX_CXXPrivate && !context.options().generate_private_functions { return Err(ParseError::Continue); } @@ -752,7 +751,7 @@ impl ClangSubItemParser for Function { return Err(ParseError::Continue); } - if cursor.is_deleted_function() { + if cursor.is_deleted_function() && !context.options().generate_deleted_functions { return Err(ParseError::Continue); } diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs index 1efddb02f3..4f96263439 100644 --- a/bindgen/options/cli.rs +++ b/bindgen/options/cli.rs @@ -507,6 +507,18 @@ struct BindgenCommand { /// bitfields. This flag is ignored if the `--respect-cxx-access-specs` flag is used. #[arg(long, value_name = "VISIBILITY")] default_visibility: Option, + /// Whether to generate C++ functions marked with "=delete" even though they + /// can't be called. + #[arg(long)] + generate_deleted_functions: bool, + /// Whether to generate C++ "pure virtual" functions even though they can't + /// be called. + #[arg(long)] + generate_pure_virtual_functions: bool, + /// Whether to generate C++ private functions even though they can't + /// be called. + #[arg(long)] + generate_private_functions: bool, /// Whether to emit diagnostics or not. #[cfg(feature = "experimental")] #[arg(long, requires = "experimental")] @@ -653,6 +665,9 @@ where wrap_static_fns_path, wrap_static_fns_suffix, default_visibility, + generate_deleted_functions, + generate_pure_virtual_functions, + generate_private_functions, #[cfg(feature = "experimental")] emit_diagnostics, generate_shell_completions, @@ -943,6 +958,9 @@ where wrap_static_fns_path, wrap_static_fns_suffix, default_visibility, + generate_deleted_functions, + generate_pure_virtual_functions, + generate_private_functions, } ); diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index 1a675401a4..c6b85519db 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -2193,4 +2193,58 @@ options! { }, as_args: "--clang-macro-fallback-build-dir", } + /// Whether to always report C++ "deleted" functions. + generate_deleted_functions: bool { + methods: { + /// Set whether to generate C++ functions even marked "=deleted" + /// + /// Although not useful to call these functions, downstream code + /// generators may need to know whether they've been deleted in + /// order to determine the relocatability of a C++ type + /// (specifically by virtue of which constructors exist.) + pub fn generate_deleted_functions(mut self, doit: bool) -> Self { + self.options.generate_deleted_functions = doit; + self + } + + }, + as_args: "--generate-deleted-functions", + }, + /// Whether to always report C++ "pure virtual" functions. + generate_pure_virtual_functions: bool { + methods: { + /// Set whether to generate C++ functions that are pure virtual. + /// + /// These functions can't be called, so the only reason + /// to generate them is if downstream postprocessors + /// need to know of their existence. This is necessary, + /// for instance, to determine whether a type itself is + /// pure virtual and thus can't be allocated. + /// Downstream code generators may choose to make code to + /// allow types to be allocated but need to avoid doing so + /// if the type contains pure virtual functions. + pub fn generate_pure_virtual_functions(mut self, doit: bool) -> Self { + self.options.generate_pure_virtual_functions = doit; + self + } + + }, + as_args: "--generate-pure-virtual-functions", + }, + /// Whether to always report C++ "private" functions. + generate_private_functions: bool { + methods: { + /// Set whether to generate C++ functions that are private. + /// + /// These functions can't be called, so the only reason + /// to generate them is if downstream postprocessors + /// need to know of their existence. + pub fn generate_private_functions(mut self, doit: bool) -> Self { + self.options.generate_private_functions = doit; + self + } + + }, + as_args: "--generate-private-functions", + }, } From e932199e94ad1a6261fddfa0d230af4f90816a4e Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Wed, 19 Feb 2025 15:22:30 +0000 Subject: [PATCH 2/3] Formatting fixes. --- bindgen/ir/function.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs index ccae390278..3c97ddb277 100644 --- a/bindgen/ir/function.rs +++ b/bindgen/ir/function.rs @@ -731,7 +731,8 @@ impl ClangSubItemParser for Function { if visibility != CXVisibility_Default { return Err(ParseError::Continue); } - if cursor.access_specifier() == CX_CXXPrivate && !context.options().generate_private_functions { + if cursor.access_specifier() == CX_CXXPrivate && + !context.options().generate_private_functions { return Err(ParseError::Continue); } @@ -751,7 +752,8 @@ impl ClangSubItemParser for Function { return Err(ParseError::Continue); } - if cursor.is_deleted_function() && !context.options().generate_deleted_functions { + if cursor.is_deleted_function() && + !context.options().generate_deleted_functions { return Err(ParseError::Continue); } From 572238ec3370d40ccba992e9c681ba1cd6c019f8 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Wed, 19 Feb 2025 15:52:39 +0000 Subject: [PATCH 3/3] Further formatting fix. --- bindgen/ir/function.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs index 3c97ddb277..a1a53ce03c 100644 --- a/bindgen/ir/function.rs +++ b/bindgen/ir/function.rs @@ -732,7 +732,8 @@ impl ClangSubItemParser for Function { return Err(ParseError::Continue); } if cursor.access_specifier() == CX_CXXPrivate && - !context.options().generate_private_functions { + !context.options().generate_private_functions + { return Err(ParseError::Continue); } @@ -753,7 +754,8 @@ impl ClangSubItemParser for Function { } if cursor.is_deleted_function() && - !context.options().generate_deleted_functions { + !context.options().generate_deleted_functions + { return Err(ParseError::Continue); }