diff --git a/bindgen-tests/tests/expectations/tests/issue-3295-aligned-struct-with-alignof.rs b/bindgen-tests/tests/expectations/tests/issue-3295-aligned-struct-with-alignof.rs new file mode 100644 index 0000000000..3f79d717ba --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/issue-3295-aligned-struct-with-alignof.rs @@ -0,0 +1,45 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct base_t { + pub __bindgen_anon_1: base_t__bindgen_ty_1, +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct base_t__bindgen_ty_1 { + pub aaa: ::std::os::raw::c_int, +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct base_t__bindgen_ty_1__bindgen_ty_1 { + pub aaa: ::std::os::raw::c_int, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of base_t__bindgen_ty_1__bindgen_ty_1", + ][::std::mem::size_of::() - 4usize]; + [ + "Alignment of base_t__bindgen_ty_1__bindgen_ty_1", + ][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: base_t__bindgen_ty_1__bindgen_ty_1::aaa", + ][::std::mem::offset_of!(base_t__bindgen_ty_1__bindgen_ty_1, aaa) - 0usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of base_t__bindgen_ty_1", + ][::std::mem::size_of::() - 4usize]; + [ + "Alignment of base_t__bindgen_ty_1", + ][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: base_t__bindgen_ty_1::aaa", + ][::std::mem::offset_of!(base_t__bindgen_ty_1, aaa) - 0usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of base_t"][::std::mem::size_of::() - 4usize]; + ["Alignment of base_t"][::std::mem::align_of::() - 4usize]; +}; diff --git a/bindgen-tests/tests/headers/issue-3295-aligned-struct-with-alignof.h b/bindgen-tests/tests/headers/issue-3295-aligned-struct-with-alignof.h new file mode 100644 index 0000000000..1cfd37b1f7 --- /dev/null +++ b/bindgen-tests/tests/headers/issue-3295-aligned-struct-with-alignof.h @@ -0,0 +1,17 @@ +// bindgen-flags: --rust-target=1.77 + +// Regression test for #3295: clang exposes the anonymous struct declared +// inside `__alignof__(...)` of an aligned attribute as a child of the +// surrounding struct. Bindgen used to treat that child as an extra +// anonymous member, doubling the surrounding struct's layout and +// producing failing size assertions in the generated bindings. + +typedef struct base_t { + struct { + int aaa; + } __attribute__((aligned( + __alignof__( + struct {int aaa;} + ) + ))); +} base_t; diff --git a/bindgen/clang.rs b/bindgen/clang.rs index 9e614da9f8..ca6674530d 100644 --- a/bindgen/clang.rs +++ b/bindgen/clang.rs @@ -389,6 +389,54 @@ impl Cursor { unsafe { clang_getCursorExtent(self.x) } } + /// Returns `true` if the start of `other`'s source range falls within + /// `self`'s source range. + /// + /// This is useful for detecting child cursors that clang exposes + /// underneath a parent but whose source location is actually outside the + /// parent's body (e.g. anonymous types declared inside an attribute + /// expression on the parent). + pub(crate) fn extent_contains(&self, other: &Cursor) -> bool { + unsafe { + let parent_range = clang_getCursorExtent(self.x); + let parent_start = clang_getRangeStart(parent_range); + let parent_end = clang_getRangeEnd(parent_range); + let child_start = + clang_getRangeStart(clang_getCursorExtent(other.x)); + + let mut parent_file = mem::zeroed::(); + let mut child_file = mem::zeroed::(); + let mut parent_start_off: c_uint = 0; + let mut parent_end_off: c_uint = 0; + let mut child_start_off: c_uint = 0; + clang_getFileLocation( + parent_start, + &mut parent_file, + ptr::null_mut(), + ptr::null_mut(), + &mut parent_start_off, + ); + clang_getFileLocation( + parent_end, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + &mut parent_end_off, + ); + clang_getFileLocation( + child_start, + &mut child_file, + ptr::null_mut(), + ptr::null_mut(), + &mut child_start_off, + ); + + parent_file == child_file && + child_start_off >= parent_start_off && + child_start_off < parent_end_off + } + } + /// Get the raw declaration comment for this referent, if one exists. pub(crate) fn raw_comment(&self) -> Option { let s = unsafe { diff --git a/bindgen/ir/comp.rs b/bindgen/ir/comp.rs index a41fa6f0b2..42f24c9b28 100644 --- a/bindgen/ir/comp.rs +++ b/bindgen/ir/comp.rs @@ -1431,7 +1431,21 @@ impl CompInfo { // A declaration of an union or a struct without name // could also be an unnamed field, unfortunately. - if cur.is_anonymous() && cur.kind() != CXCursor_EnumDecl + // + // However, anonymous types declared *outside* the + // parent struct's body — for example an anonymous + // struct that appears inside an `__alignof__` + // expression on the parent's + // `__attribute__((aligned(...)))` — are exposed by + // clang as child cursors of the parent struct, even + // though they are not real members. Skip those by + // checking that the child's source range is inside + // the parent's source range; otherwise we would + // append a phantom field and corrupt the parent's + // layout (#3295). + if cur.is_anonymous() && + cur.kind() != CXCursor_EnumDecl && + cursor.extent_contains(&cur) { let ty = cur.cur_type(); let public = cur.public_accessible();