Skip to content

Commit

Permalink
Options to generate uncallable C++ functions.
Browse files Browse the repository at this point in the history
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 google/autocxx#124.
  • Loading branch information
adetaylor committed Feb 19, 2025
1 parent 61603fc commit 25432ba
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 10 deletions.
46 changes: 46 additions & 0 deletions bindgen-tests/tests/expectations/tests/uncallable_functions.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions bindgen-tests/tests/headers/uncallable_functions.hpp
Original file line number Diff line number Diff line change
@@ -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() {}
};
20 changes: 13 additions & 7 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
5 changes: 2 additions & 3 deletions bindgen/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand Down
18 changes: 18 additions & 0 deletions bindgen/options/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FieldVisibilityKind>,
/// 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")]
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
}
);

Expand Down
54 changes: 54 additions & 0 deletions bindgen/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
}

0 comments on commit 25432ba

Please sign in to comment.