Skip to content

Commit

Permalink
Don't generate bindings for deleted member functions. (#2044)
Browse files Browse the repository at this point in the history
Closes #2044
Fixes #2043

See #2043 for details.
  • Loading branch information
martinboehme authored and emilio committed Apr 30, 2021
1 parent f26d57d commit 910d2be
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 4 deletions.
21 changes: 21 additions & 0 deletions src/clang.rs
Expand Up @@ -469,6 +469,27 @@ impl Cursor {
unsafe { clang_Cursor_isFunctionInlined(self.x) != 0 }
}

/// Is the referent a defaulted function?
pub fn is_defaulted_function(&self) -> bool {
unsafe { clang_CXXMethod_isDefaulted(self.x) != 0 }
}

/// Is the referent a deleted function?
pub fn is_deleted_function(&self) -> bool {
// Unfortunately, libclang doesn't yet have an API for checking if a
// member function is deleted, but the following should be a good
// enough approximation.
// Deleted functions are implicitly inline according to paragraph 4 of
// [dcl.fct.def.delete] in the C++ standard. Normal inline functions
// have a definition in the same translation unit, so if this is an
// inline function without a definition, and it's not a defaulted
// function, we can reasonably safely conclude that it's a deleted
// function.
self.is_inlined_function() &&
self.definition().is_none() &&
!self.is_defaulted_function()
}

/// Get the width of this cursor's referent bit field, or `None` if the
/// referent is not a bit field.
pub fn bit_width(&self) -> Option<u32> {
Expand Down
11 changes: 7 additions & 4 deletions src/ir/function.rs
Expand Up @@ -597,10 +597,13 @@ impl ClangSubItemParser for Function {
return Err(ParseError::Continue);
}

if !context.options().generate_inline_functions &&
cursor.is_inlined_function()
{
return Err(ParseError::Continue);
if cursor.is_inlined_function() {
if !context.options().generate_inline_functions {
return Err(ParseError::Continue);
}
if cursor.is_deleted_function() {
return Err(ParseError::Continue);
}
}

let linkage = cursor.linkage();
Expand Down
91 changes: 91 additions & 0 deletions tests/expectations/tests/deleted-function.rs
@@ -0,0 +1,91 @@
#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]

#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct A {
pub _address: u8,
}
#[test]
fn bindgen_test_layout_A() {
assert_eq!(
::std::mem::size_of::<A>(),
1usize,
concat!("Size of: ", stringify!(A))
);
assert_eq!(
::std::mem::align_of::<A>(),
1usize,
concat!("Alignment of ", stringify!(A))
);
}
extern "C" {
#[link_name = "\u{1}_ZN1A17inline_definitionEv"]
pub fn A_inline_definition(this: *mut A);
}
extern "C" {
#[link_name = "\u{1}_ZN1A22out_of_line_definitionEv"]
pub fn A_out_of_line_definition(this: *mut A);
}
impl A {
#[inline]
pub unsafe fn inline_definition(&mut self) {
A_inline_definition(self)
}
#[inline]
pub unsafe fn out_of_line_definition(&mut self) {
A_out_of_line_definition(self)
}
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct B {
pub _address: u8,
}
#[test]
fn bindgen_test_layout_B() {
assert_eq!(
::std::mem::size_of::<B>(),
1usize,
concat!("Size of: ", stringify!(B))
);
assert_eq!(
::std::mem::align_of::<B>(),
1usize,
concat!("Alignment of ", stringify!(B))
);
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct C {
pub _address: u8,
}
#[test]
fn bindgen_test_layout_C() {
assert_eq!(
::std::mem::size_of::<C>(),
1usize,
concat!("Size of: ", stringify!(C))
);
assert_eq!(
::std::mem::align_of::<C>(),
1usize,
concat!("Alignment of ", stringify!(C))
);
}
extern "C" {
#[link_name = "\u{1}_ZN1CC1ERS_"]
pub fn C_C(this: *mut C, arg1: *mut C);
}
impl C {
#[inline]
pub unsafe fn new(arg1: *mut C) -> Self {
let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
C_C(__bindgen_tmp.as_mut_ptr(), arg1);
__bindgen_tmp.assume_init()
}
}
35 changes: 35 additions & 0 deletions tests/headers/deleted-function.hpp
@@ -0,0 +1,35 @@
// bindgen-flags: --generate-inline-functions -- -std=c++11

class A {
public:
// Deleted function should not get a binding.
void deleted() = delete;

// Inline functions should get bindings, whether they are defined inline
// (in the class) or out of line.
inline void inline_definition() {}
inline void out_of_line_definition();

// TODO: This is an edge case that we get wrong: An inline function
// without a definition in the same translation unit should still get a
// binding. We currently can't distinguish this case from a deleted member
// function because libclang doesn't provide a direct way to query for
// deleted member functions. This seems acceptable, however, as an inline
// function without a definition in the same translation unit is unlikely
// to be useful in practice.
inline void inline_without_definition();
};

void A::out_of_line_definition() {}

class B {
public:
// Deleted copy constructor should not get a binding.
B(B&) = delete;
};

class C {
public:
// Defaulted copy constructor should get a binding.
C(C&) = default;
};

0 comments on commit 910d2be

Please sign in to comment.