diff options
author | Poliorcetics <poliorcetics@users.noreply.github.com> | 2022-09-23 08:30:49 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-23 08:30:49 +0200 |
commit | 04c0cd0ff45ff3f47d845c2b3b983e2dc06b9ca3 (patch) | |
tree | ade1aff4f9f59833844b0e827f10ad9a948b6ada | |
parent | 86f059f081160ba01de31fc2ec6e20e52b97968d (diff) |
Option to wrap union members in ManuallyDrop (#2185)
-rw-r--r-- | src/codegen/mod.rs | 153 | ||||
-rw-r--r-- | src/lib.rs | 59 | ||||
-rw-r--r-- | src/options.rs | 61 | ||||
-rw-r--r-- | tests/expectations/tests/union_with_non_copy_member.rs | 239 | ||||
-rw-r--r-- | tests/headers/union_with_non_copy_member.h | 20 |
5 files changed, 499 insertions, 33 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index f1422d6b..98ab922a 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1231,7 +1231,7 @@ trait FieldCodegen<'a> { } impl<'a> FieldCodegen<'a> for Field { - type Extra = (); + type Extra = &'a str; fn codegen<F, M>( &self, @@ -1244,7 +1244,7 @@ impl<'a> FieldCodegen<'a> for Field { struct_layout: &mut StructLayoutTracker, fields: &mut F, methods: &mut M, - _: (), + parent_canonical_name: Self::Extra, ) where F: Extend<proc_macro2::TokenStream>, M: Extend<proc_macro2::TokenStream>, @@ -1261,7 +1261,7 @@ impl<'a> FieldCodegen<'a> for Field { struct_layout, fields, methods, - (), + parent_canonical_name, ); } Field::Bitfields(ref unit) => { @@ -1275,7 +1275,7 @@ impl<'a> FieldCodegen<'a> for Field { struct_layout, fields, methods, - (), + parent_canonical_name, ); } } @@ -1283,7 +1283,7 @@ impl<'a> FieldCodegen<'a> for Field { } impl<'a> FieldCodegen<'a> for FieldData { - type Extra = (); + type Extra = &'a str; fn codegen<F, M>( &self, @@ -1296,7 +1296,7 @@ impl<'a> FieldCodegen<'a> for FieldData { struct_layout: &mut StructLayoutTracker, fields: &mut F, methods: &mut M, - _: (), + parent_canonical_name: Self::Extra, ) where F: Extend<proc_macro2::TokenStream>, M: Extend<proc_macro2::TokenStream>, @@ -1313,16 +1313,7 @@ impl<'a> FieldCodegen<'a> for FieldData { // NB: If supported, we use proper `union` types. let ty = if parent.is_union() && !struct_layout.is_rust_union() { - result.saw_bindgen_union(); - if ctx.options().enable_cxx_namespaces { - quote! { - root::__BindgenUnionField<#ty> - } - } else { - quote! { - __BindgenUnionField<#ty> - } - } + wrap_non_copy_type_for_union(ctx, parent_canonical_name, result, ty) } else if let Some(item) = field_ty.is_incomplete_array(ctx) { result.saw_incomplete_array(); @@ -1433,6 +1424,54 @@ impl<'a> FieldCodegen<'a> for FieldData { } } +fn wrap_non_copy_type_for_union( + ctx: &BindgenContext, + parent_canonical_name: &str, + result: &mut CodegenResult, + field_ty: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let union_style = union_style_from_ctx_and_name(ctx, parent_canonical_name); + + match union_style { + NonCopyUnionStyle::ManuallyDrop => { + if ctx.options().use_core { + quote! { + ::core::mem::ManuallyDrop<#field_ty> + } + } else { + quote! { + ::std::mem::ManuallyDrop<#field_ty> + } + } + } + NonCopyUnionStyle::BindgenWrapper => { + result.saw_bindgen_union(); + if ctx.options().enable_cxx_namespaces { + quote! { + root::__BindgenUnionField<#field_ty> + } + } else { + quote! { + __BindgenUnionField<#field_ty> + } + } + } + } +} + +fn union_style_from_ctx_and_name( + ctx: &BindgenContext, + canonical_name: &str, +) -> NonCopyUnionStyle { + if ctx.options().bindgen_wrapper_union.matches(canonical_name) { + NonCopyUnionStyle::BindgenWrapper + } else if ctx.options().manually_drop_union.matches(canonical_name) { + NonCopyUnionStyle::ManuallyDrop + } else { + ctx.options().default_non_copy_union_style + } +} + impl BitfieldUnit { /// Get the constructor name for this bitfield unit. fn ctor_name(&self) -> proc_macro2::TokenStream { @@ -1499,7 +1538,7 @@ fn access_specifier( } impl<'a> FieldCodegen<'a> for BitfieldUnit { - type Extra = (); + type Extra = &'a str; fn codegen<F, M>( &self, @@ -1512,7 +1551,7 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { struct_layout: &mut StructLayoutTracker, fields: &mut F, methods: &mut M, - _: (), + parent_canonical_name: Self::Extra, ) where F: Extend<proc_macro2::TokenStream>, M: Extend<proc_macro2::TokenStream>, @@ -1525,16 +1564,12 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { let unit_field_ty = helpers::bitfield_unit(ctx, layout); let field_ty = { if parent.is_union() && !struct_layout.is_rust_union() { - result.saw_bindgen_union(); - if ctx.options().enable_cxx_namespaces { - quote! { - root::__BindgenUnionField<#unit_field_ty> - } - } else { - quote! { - __BindgenUnionField<#unit_field_ty> - } - } + wrap_non_copy_type_for_union( + ctx, + parent_canonical_name, + result, + unit_field_ty.clone(), + ) } else { unit_field_ty.clone() } @@ -1859,7 +1894,7 @@ impl CodeGenerator for CompInfo { &mut struct_layout, &mut fields, &mut methods, - (), + &canonical_name, ); } // Check whether an explicit padding field is needed @@ -1965,7 +2000,10 @@ impl CodeGenerator for CompInfo { explicit_align = Some(layout.align); } - if !struct_layout.is_rust_union() { + if !struct_layout.is_rust_union() && + union_style_from_ctx_and_name(ctx, &canonical_name) == + NonCopyUnionStyle::BindgenWrapper + { let ty = helpers::blob(ctx, layout); fields.push(quote! { pub bindgen_union_field: #ty , @@ -2092,6 +2130,15 @@ impl CodeGenerator for CompInfo { #( #attributes )* pub union #canonical_ident } + } else if is_union && + !struct_layout.is_rust_union() && + union_style_from_ctx_and_name(ctx, &canonical_name) == + NonCopyUnionStyle::ManuallyDrop + { + quote! { + #( #attributes )* + pub union #canonical_ident + } } else { quote! { #( #attributes )* @@ -3398,6 +3445,52 @@ impl std::str::FromStr for AliasVariation { } } +/// Enum for how non-Copy unions should be translated. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum NonCopyUnionStyle { + /// Wrap members in a type generated by bindgen. + BindgenWrapper, + /// Wrap members in [`::core::mem::ManuallyDrop`]. + /// + /// Note: `ManuallyDrop` was stabilized in Rust 1.20.0, do not use it if your + /// MSRV is lower. + ManuallyDrop, +} + +impl NonCopyUnionStyle { + /// Convert an `NonCopyUnionStyle` to its str representation. + pub fn as_str(&self) -> &'static str { + match self { + Self::BindgenWrapper => "bindgen_wrapper", + Self::ManuallyDrop => "manually_drop", + } + } +} + +impl Default for NonCopyUnionStyle { + fn default() -> Self { + Self::BindgenWrapper + } +} + +impl std::str::FromStr for NonCopyUnionStyle { + type Err = std::io::Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "bindgen_wrapper" => Ok(Self::BindgenWrapper), + "manually_drop" => Ok(Self::ManuallyDrop), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + concat!( + "Got an invalid NonCopyUnionStyle. Accepted values ", + "are 'bindgen_wrapper' and 'manually_drop'" + ), + )), + } + } +} + /// Fallible conversion to an opaque blob. /// /// Implementors of this trait should provide the `try_get_layout` method to @@ -66,7 +66,9 @@ doc_mod!(ir, ir_docs); doc_mod!(parse, parse_docs); doc_mod!(regex_set, regex_set_docs); -pub use crate::codegen::{AliasVariation, EnumVariation, MacroTypeVariation}; +pub use crate::codegen::{ + AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle, +}; use crate::features::RustFeatures; pub use crate::features::{ RustTarget, LATEST_STABLE_RUST, RUST_TARGET_STRINGS, @@ -312,6 +314,13 @@ impl Builder { .push(self.options.default_alias_style.as_str().into()); } + if self.options.default_non_copy_union_style != Default::default() { + output_vector.push("--default-non-copy-union-style".into()); + output_vector.push( + self.options.default_non_copy_union_style.as_str().into(), + ); + } + let regex_sets = &[ (&self.options.bitfield_enums, "--bitfield-enum"), (&self.options.newtype_enums, "--newtype-enum"), @@ -329,6 +338,11 @@ impl Builder { (&self.options.type_alias, "--type-alias"), (&self.options.new_type_alias, "--new-type-alias"), (&self.options.new_type_alias_deref, "--new-type-alias-deref"), + ( + &self.options.bindgen_wrapper_union, + "--bindgen-wrapper-union", + ), + (&self.options.manually_drop_union, "--manually-drop-union"), (&self.options.blocklisted_types, "--blocklist-type"), (&self.options.blocklisted_functions, "--blocklist-function"), (&self.options.blocklisted_items, "--blocklist-item"), @@ -1101,6 +1115,32 @@ impl Builder { self } + /// Set the default style of code to generate for unions with a non-Copy member. + pub fn default_non_copy_union_style( + mut self, + arg: codegen::NonCopyUnionStyle, + ) -> Self { + self.options.default_non_copy_union_style = arg; + self + } + + /// Mark the given union (or set of union, if using a pattern) to use + /// a bindgen-generated wrapper for its members if at least one is non-Copy. + pub fn bindgen_wrapper_union<T: AsRef<str>>(mut self, arg: T) -> Self { + self.options.bindgen_wrapper_union.insert(arg); + self + } + + /// Mark the given union (or set of union, if using a pattern) to use + /// [`::core::mem::ManuallyDrop`] for its members if at least one is non-Copy. + /// + /// Note: `ManuallyDrop` was stabilized in Rust 1.20.0, do not use it if your + /// MSRV is lower. + pub fn manually_drop_union<T: AsRef<str>>(mut self, arg: T) -> Self { + self.options.manually_drop_union.insert(arg); + self + } + /// Add a string to prepend to the generated bindings. The string is passed /// through without any modification. pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Self { @@ -1811,6 +1851,18 @@ struct BindgenOptions { /// Deref and Deref to their aliased type. new_type_alias_deref: RegexSet, + /// The default style of code to generate for union containing non-Copy + /// members. + default_non_copy_union_style: codegen::NonCopyUnionStyle, + + /// The union patterns to mark an non-Copy union as using the bindgen + /// generated wrapper. + bindgen_wrapper_union: RegexSet, + + /// The union patterns to mark an non-Copy union as using the + /// `::core::mem::ManuallyDrop` wrapper. + manually_drop_union: RegexSet, + /// Whether we should generate builtins or not. builtins: bool, @@ -2079,6 +2131,8 @@ impl BindgenOptions { &mut self.type_alias, &mut self.new_type_alias, &mut self.new_type_alias_deref, + &mut self.bindgen_wrapper_union, + &mut self.manually_drop_union, &mut self.no_partialeq_types, &mut self.no_copy_types, &mut self.no_debug_types, @@ -2137,6 +2191,9 @@ impl Default for BindgenOptions { type_alias: Default::default(), new_type_alias: Default::default(), new_type_alias_deref: Default::default(), + default_non_copy_union_style: Default::default(), + bindgen_wrapper_union: Default::default(), + manually_drop_union: Default::default(), builtins: false, emit_ast: false, emit_ir: false, diff --git a/src/options.rs b/src/options.rs index 4db3fd57..1e87b6eb 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,7 +1,7 @@ use bindgen::{ builder, AliasVariation, Builder, CodegenConfig, EnumVariation, - MacroTypeVariation, RustTarget, DEFAULT_ANON_FIELDS_PREFIX, - RUST_TARGET_STRINGS, + MacroTypeVariation, NonCopyUnionStyle, RustTarget, + DEFAULT_ANON_FIELDS_PREFIX, RUST_TARGET_STRINGS, }; use clap::{App, Arg}; use std::fs::File; @@ -138,6 +138,42 @@ where .value_name("regex") .multiple_occurrences(true) .number_of_values(1), + Arg::new("default-non-copy-union-style") + .long("default-non-copy-union-style") + .help( + "The default style of code used to generate unions with \ + non-Copy members. Note that ManuallyDrop was first \ + stabilized in Rust 1.20.0.", + ) + .value_name("style") + .default_value("bindgen_wrapper") + .possible_values(&[ + "bindgen_wrapper", + "manually_drop", + ]) + .multiple_occurrences(false), + Arg::new("bindgen-wrapper-union") + .long("bindgen-wrapper-union") + .help( + "Mark any union whose name matches <regex> and who has a \ + non-Copy member to use a bindgen-generated wrapper for \ + fields.", + ) + .value_name("regex") + .takes_value(true) + .multiple_occurrences(true) + .number_of_values(1), + Arg::new("manually-drop-union") + .long("manually-drop-union") + .help( + "Mark any union whose name matches <regex> and who has a \ + non-Copy member to use ManuallyDrop (stabilized in Rust \ + 1.20.0) for fields.", + ) + .value_name("regex") + .takes_value(true) + .multiple_occurrences(true) + .number_of_values(1), Arg::new("blocklist-type") .alias("blacklist-type") .long("blocklist-type") @@ -631,6 +667,27 @@ where } } + if let Some(variant) = matches.value_of("default-non-copy-union-style") { + builder = builder.default_non_copy_union_style( + NonCopyUnionStyle::from_str(variant)?, + ); + } + + if let Some(bindgen_wrapper_union) = + matches.values_of("bindgen-wrapper-union") + { + for regex in bindgen_wrapper_union { + builder = builder.bindgen_wrapper_union(regex); + } + } + + if let Some(manually_drop_union) = matches.values_of("manually-drop-union") + { + for regex in manually_drop_union { + builder = builder.manually_drop_union(regex); + } + } + if let Some(hidden_types) = matches.values_of("blocklist-type") { for ty in hidden_types { builder = builder.blocklist_type(ty); diff --git a/tests/expectations/tests/union_with_non_copy_member.rs b/tests/expectations/tests/union_with_non_copy_member.rs new file mode 100644 index 00000000..3c500b21 --- /dev/null +++ b/tests/expectations/tests/union_with_non_copy_member.rs @@ -0,0 +1,239 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +pub struct __BindgenUnionField<T>(::std::marker::PhantomData<T>); +impl<T> __BindgenUnionField<T> { + #[inline] + pub const fn new() -> Self { + __BindgenUnionField(::std::marker::PhantomData) + } + #[inline] + pub unsafe fn as_ref(&self) -> &T { + ::std::mem::transmute(self) + } + #[inline] + pub unsafe fn as_mut(&mut self) -> &mut T { + ::std::mem::transmute(self) + } +} +impl<T> ::std::default::Default for __BindgenUnionField<T> { + #[inline] + fn default() -> Self { + Self::new() + } +} +impl<T> ::std::clone::Clone for __BindgenUnionField<T> { + #[inline] + fn clone(&self) -> Self { + Self::new() + } +} +impl<T> ::std::marker::Copy for __BindgenUnionField<T> {} +impl<T> ::std::fmt::Debug for __BindgenUnionField<T> { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + fmt.write_str("__BindgenUnionField") + } +} +impl<T> ::std::hash::Hash for __BindgenUnionField<T> { + fn hash<H: ::std::hash::Hasher>(&self, _state: &mut H) {} +} +impl<T> ::std::cmp::PartialEq for __BindgenUnionField<T> { + fn eq(&self, _other: &__BindgenUnionField<T>) -> bool { + true + } +} +impl<T> ::std::cmp::Eq for __BindgenUnionField<T> {} +#[repr(C)] +#[derive(Debug, Default)] +pub struct NonCopyType { + pub foo: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_NonCopyType() { + const UNINIT: ::std::mem::MaybeUninit<NonCopyType> = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::<NonCopyType>(), + 4usize, + concat!("Size of: ", stringify!(NonCopyType)) + ); + assert_eq!( + ::std::mem::align_of::<NonCopyType>(), + 4usize, + concat!("Alignment of ", stringify!(NonCopyType)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).foo) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(NonCopyType), + "::", + stringify!(foo) + ) + ); +} +#[repr(C)] +pub struct WithBindgenGeneratedWrapper { + pub non_copy_type: __BindgenUnionField<NonCopyType>, + pub bar: __BindgenUnionField<::std::os::raw::c_int>, + pub bindgen_union_field: u32, +} +#[test] +fn bindgen_test_layout_WithBindgenGeneratedWrapper() { + const UNINIT: ::std::mem::MaybeUninit<WithBindgenGeneratedWrapper> = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::<WithBindgenGeneratedWrapper>(), + 4usize, + concat!("Size of: ", stringify!(WithBindgenGeneratedWrapper)) + ); + assert_eq!( + ::std::mem::align_of::<WithBindgenGeneratedWrapper>(), + 4usize, + concat!("Alignment of ", stringify!(WithBindgenGeneratedWrapper)) + ); + assert_eq!( + unsafe { + ::std::ptr::addr_of!((*ptr).non_copy_type) as usize - ptr as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(WithBindgenGeneratedWrapper), + "::", + stringify!(non_copy_type) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).bar) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(WithBindgenGeneratedWrapper), + "::", + stringify!(bar) + ) + ); +} +impl Default for WithBindgenGeneratedWrapper { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::<Self>::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[repr(C)] +pub union WithManuallyDrop { + pub non_copy_type: ::std::mem::ManuallyDrop<NonCopyType>, + pub bar: ::std::mem::ManuallyDrop<::std::os::raw::c_int>, +} +#[test] +fn bindgen_test_layout_WithManuallyDrop() { + const UNINIT: ::std::mem::MaybeUninit<WithManuallyDrop> = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::<WithManuallyDrop>(), + 4usize, + concat!("Size of: ", stringify!(WithManuallyDrop)) + ); + assert_eq!( + ::std::mem::align_of::<WithManuallyDrop>(), + 4usize, + concat!("Alignment of ", stringify!(WithManuallyDrop)) + ); + assert_eq!( + unsafe { + ::std::ptr::addr_of!((*ptr).non_copy_type) as usize - ptr as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(WithManuallyDrop), + "::", + stringify!(non_copy_type) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).bar) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(WithManuallyDrop), + "::", + stringify!(bar) + ) + ); +} +impl Default for WithManuallyDrop { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::<Self>::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[repr(C)] +pub struct WithDefaultWrapper { + pub non_copy_type: __BindgenUnionField<NonCopyType>, + pub bar: __BindgenUnionField<::std::os::raw::c_int>, + pub bindgen_union_field: u32, +} +#[test] +fn bindgen_test_layout_WithDefaultWrapper() { + const UNINIT: ::std::mem::MaybeUninit<WithDefaultWrapper> = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::<WithDefaultWrapper>(), + 4usize, + concat!("Size of: ", stringify!(WithDefaultWrapper)) + ); + assert_eq!( + ::std::mem::align_of::<WithDefaultWrapper>(), + 4usize, + concat!("Alignment of ", stringify!(WithDefaultWrapper)) + ); + assert_eq!( + unsafe { + ::std::ptr::addr_of!((*ptr).non_copy_type) as usize - ptr as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(WithDefaultWrapper), + "::", + stringify!(non_copy_type) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).bar) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(WithDefaultWrapper), + "::", + stringify!(bar) + ) + ); +} +impl Default for WithDefaultWrapper { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::<Self>::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} diff --git a/tests/headers/union_with_non_copy_member.h b/tests/headers/union_with_non_copy_member.h new file mode 100644 index 00000000..764820a4 --- /dev/null +++ b/tests/headers/union_with_non_copy_member.h @@ -0,0 +1,20 @@ +// bindgen-flags: --bindgen-wrapper-union 'WithBindgenGeneratedWrapper' --manually-drop-union 'WithManuallyDrop' --no-copy 'NonCopyType' + +struct NonCopyType { + int foo; +}; + +union WithBindgenGeneratedWrapper { + struct NonCopyType non_copy_type; + int bar; +}; + +union WithManuallyDrop { + struct NonCopyType non_copy_type; + int bar; +}; + +union WithDefaultWrapper { + struct NonCopyType non_copy_type; + int bar; +}; |