diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 6bbb7ab591..a686bdec32 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -2009,13 +2009,22 @@ impl CodeGenerator for CompInfo { let mut needs_default_impl = false; let mut needs_debug_impl = false; let mut needs_partialeq_impl = false; + let mut needs_packed_wrapper = false; if let Some(comment) = item.comment(ctx) { attributes.push(attributes::doc(comment)); } + + // We can't specify both packed(N) and align(N), but the packed() + // should be redundant in this case. if packed && !is_opaque { let n = layout.map_or(1, |l| l.align); assert!(ctx.options().rust_features().repr_packed_n || n == 1); - let packed_repr = if n == 1 { + if ctx.options().rust_features().repr_align && + explicit_align.is_some() + { + needs_packed_wrapper = true; + } + let packed_repr = if n == 1 || needs_packed_wrapper { "packed".to_string() } else { format!("packed({})", n) @@ -2023,16 +2032,16 @@ impl CodeGenerator for CompInfo { attributes.push(attributes::repr_list(&["C", &packed_repr])); } else { attributes.push(attributes::repr("C")); - } - if ctx.options().rust_features().repr_align { - if let Some(explicit) = explicit_align { - // Ensure that the struct has the correct alignment even in - // presence of alignas. - let explicit = helpers::ast_ty::int_expr(explicit as i64); - attributes.push(quote! { - #[repr(align(#explicit))] - }); + if ctx.options().rust_features().repr_align { + if let Some(explicit) = explicit_align { + // Ensure that the struct has the correct alignment even in + // presence of alignas. + let explicit = helpers::ast_ty::int_expr(explicit as i64); + attributes.push(quote! { + #[repr(align(#explicit))] + }); + } } } @@ -2086,15 +2095,21 @@ impl CodeGenerator for CompInfo { attributes.push(attributes::must_use()); } + let layout_ident = if needs_packed_wrapper { + ctx.rust_ident(canonical_name.to_owned() + "__packed") + } else { + canonical_ident.clone() + }; + let mut tokens = if is_union && struct_layout.is_rust_union() { quote! { #( #attributes )* - pub union #canonical_ident + pub union #layout_ident } } else { quote! { #( #attributes )* - pub struct #canonical_ident + pub struct #layout_ident } }; @@ -2105,6 +2120,19 @@ impl CodeGenerator for CompInfo { }); result.push(tokens); + if needs_packed_wrapper { + let attributes = attributes::derives(&derives); + let align = proc_macro2::TokenStream::from_str( + &explicit_align.unwrap().to_string(), + ) + .unwrap(); + result.push(quote! { + #attributes + #[repr(C, align(#align))] + pub struct #canonical_ident(pub #layout_ident); + }); + } + // Generate the inner types and all that stuff. // // TODO: In the future we might want to be smart, and use nested @@ -2204,12 +2232,17 @@ impl CodeGenerator for CompInfo { let uninit_decl = if !check_field_offset.is_empty() { // FIXME: When MSRV >= 1.59.0, we can use // > const PTR: *const #canonical_ident = ::#prefix::mem::MaybeUninit::uninit().as_ptr(); + let layout_cast = if needs_packed_wrapper { + Some(quote!(as *const #layout_ident)) + } else { + None + }; Some(quote! { // Use a shared MaybeUninit so that rustc with // opt-level=0 doesn't take too much stack space, // see #2218. const UNINIT: ::#prefix::mem::MaybeUninit<#canonical_ident> = ::#prefix::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); + let ptr = UNINIT.as_ptr()#layout_cast; }) } else { None diff --git a/src/codegen/struct_layout.rs b/src/codegen/struct_layout.rs index 657be0b489..9e47dbd412 100644 --- a/src/codegen/struct_layout.rs +++ b/src/codegen/struct_layout.rs @@ -24,6 +24,7 @@ pub struct StructLayoutTracker<'a> { padding_count: usize, latest_field_layout: Option, max_field_align: usize, + has_bitfield: bool, last_field_was_bitfield: bool, } @@ -103,6 +104,7 @@ impl<'a> StructLayoutTracker<'a> { padding_count: 0, latest_field_layout: None, max_field_align: 0, + has_bitfield: false, last_field_was_bitfield: false, } } @@ -145,6 +147,7 @@ impl<'a> StructLayoutTracker<'a> { ); self.latest_field_layout = Some(layout); + self.has_bitfield = true; self.last_field_was_bitfield = true; // NB: We intentionally don't update the max_field_align here, since our // bitfields code doesn't necessarily guarantee it, so we need to @@ -365,8 +368,14 @@ impl<'a> StructLayoutTracker<'a> { return true; } - if self.max_field_align >= layout.align { - return false; + if self.is_packed && !self.has_bitfield { + if self.max_field_align > layout.align { + return false; + } + } else { + if self.max_field_align >= layout.align { + return false; + } } // We can only generate up-to a 8-bytes of alignment unless we support diff --git a/tests/expectations/struct_with_anon_struct_array_float.rs b/tests/expectations/struct_with_anon_struct_array_float.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/tests/expectations/struct_with_anon_struct_array_float.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/expectations/tests/packed-align-conflict.rs b/tests/expectations/tests/packed-align-conflict.rs new file mode 100644 index 0000000000..c57fbc6fbf --- /dev/null +++ b/tests/expectations/tests/packed-align-conflict.rs @@ -0,0 +1,48 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct B__packed { + pub a: u8, + pub b: u16, + pub c: u8, +} +#[derive(Debug, Default, Copy, Clone)] +#[repr(C, align(2))] +pub struct B(pub B__packed); +#[test] +fn bindgen_test_layout_B() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr() as *const B__packed; + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(B)) + ); + assert_eq!( + ::std::mem::align_of::(), + 2usize, + concat!("Alignment of ", stringify!(B)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).a) as usize - ptr as usize }, + 0usize, + concat!("Offset of field: ", stringify!(B), "::", stringify!(a)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).b) as usize - ptr as usize }, + 1usize, + concat!("Offset of field: ", stringify!(B), "::", stringify!(b)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).c) as usize - ptr as usize }, + 3usize, + concat!("Offset of field: ", stringify!(B), "::", stringify!(c)) + ); +} diff --git a/tests/headers/packed-align-conflict.h b/tests/headers/packed-align-conflict.h new file mode 100644 index 0000000000..e1ac00904d --- /dev/null +++ b/tests/headers/packed-align-conflict.h @@ -0,0 +1,8 @@ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; + +struct B { + uint8_t a; + uint16_t b; + uint8_t c; +} __attribute__((aligned(2), packed));