Skip to content

Commit 04c0cd0

Browse files
authored
Option to wrap union members in ManuallyDrop (#2185)
1 parent 86f059f commit 04c0cd0

File tree

5 files changed

+499
-33
lines changed

5 files changed

+499
-33
lines changed

src/codegen/mod.rs

Lines changed: 123 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,7 +1231,7 @@ trait FieldCodegen<'a> {
12311231
}
12321232

12331233
impl<'a> FieldCodegen<'a> for Field {
1234-
type Extra = ();
1234+
type Extra = &'a str;
12351235

12361236
fn codegen<F, M>(
12371237
&self,
@@ -1244,7 +1244,7 @@ impl<'a> FieldCodegen<'a> for Field {
12441244
struct_layout: &mut StructLayoutTracker,
12451245
fields: &mut F,
12461246
methods: &mut M,
1247-
_: (),
1247+
parent_canonical_name: Self::Extra,
12481248
) where
12491249
F: Extend<proc_macro2::TokenStream>,
12501250
M: Extend<proc_macro2::TokenStream>,
@@ -1261,7 +1261,7 @@ impl<'a> FieldCodegen<'a> for Field {
12611261
struct_layout,
12621262
fields,
12631263
methods,
1264-
(),
1264+
parent_canonical_name,
12651265
);
12661266
}
12671267
Field::Bitfields(ref unit) => {
@@ -1275,15 +1275,15 @@ impl<'a> FieldCodegen<'a> for Field {
12751275
struct_layout,
12761276
fields,
12771277
methods,
1278-
(),
1278+
parent_canonical_name,
12791279
);
12801280
}
12811281
}
12821282
}
12831283
}
12841284

12851285
impl<'a> FieldCodegen<'a> for FieldData {
1286-
type Extra = ();
1286+
type Extra = &'a str;
12871287

12881288
fn codegen<F, M>(
12891289
&self,
@@ -1296,7 +1296,7 @@ impl<'a> FieldCodegen<'a> for FieldData {
12961296
struct_layout: &mut StructLayoutTracker,
12971297
fields: &mut F,
12981298
methods: &mut M,
1299-
_: (),
1299+
parent_canonical_name: Self::Extra,
13001300
) where
13011301
F: Extend<proc_macro2::TokenStream>,
13021302
M: Extend<proc_macro2::TokenStream>,
@@ -1313,16 +1313,7 @@ impl<'a> FieldCodegen<'a> for FieldData {
13131313

13141314
// NB: If supported, we use proper `union` types.
13151315
let ty = if parent.is_union() && !struct_layout.is_rust_union() {
1316-
result.saw_bindgen_union();
1317-
if ctx.options().enable_cxx_namespaces {
1318-
quote! {
1319-
root::__BindgenUnionField<#ty>
1320-
}
1321-
} else {
1322-
quote! {
1323-
__BindgenUnionField<#ty>
1324-
}
1325-
}
1316+
wrap_non_copy_type_for_union(ctx, parent_canonical_name, result, ty)
13261317
} else if let Some(item) = field_ty.is_incomplete_array(ctx) {
13271318
result.saw_incomplete_array();
13281319

@@ -1433,6 +1424,54 @@ impl<'a> FieldCodegen<'a> for FieldData {
14331424
}
14341425
}
14351426

1427+
fn wrap_non_copy_type_for_union(
1428+
ctx: &BindgenContext,
1429+
parent_canonical_name: &str,
1430+
result: &mut CodegenResult,
1431+
field_ty: proc_macro2::TokenStream,
1432+
) -> proc_macro2::TokenStream {
1433+
let union_style = union_style_from_ctx_and_name(ctx, parent_canonical_name);
1434+
1435+
match union_style {
1436+
NonCopyUnionStyle::ManuallyDrop => {
1437+
if ctx.options().use_core {
1438+
quote! {
1439+
::core::mem::ManuallyDrop<#field_ty>
1440+
}
1441+
} else {
1442+
quote! {
1443+
::std::mem::ManuallyDrop<#field_ty>
1444+
}
1445+
}
1446+
}
1447+
NonCopyUnionStyle::BindgenWrapper => {
1448+
result.saw_bindgen_union();
1449+
if ctx.options().enable_cxx_namespaces {
1450+
quote! {
1451+
root::__BindgenUnionField<#field_ty>
1452+
}
1453+
} else {
1454+
quote! {
1455+
__BindgenUnionField<#field_ty>
1456+
}
1457+
}
1458+
}
1459+
}
1460+
}
1461+
1462+
fn union_style_from_ctx_and_name(
1463+
ctx: &BindgenContext,
1464+
canonical_name: &str,
1465+
) -> NonCopyUnionStyle {
1466+
if ctx.options().bindgen_wrapper_union.matches(canonical_name) {
1467+
NonCopyUnionStyle::BindgenWrapper
1468+
} else if ctx.options().manually_drop_union.matches(canonical_name) {
1469+
NonCopyUnionStyle::ManuallyDrop
1470+
} else {
1471+
ctx.options().default_non_copy_union_style
1472+
}
1473+
}
1474+
14361475
impl BitfieldUnit {
14371476
/// Get the constructor name for this bitfield unit.
14381477
fn ctor_name(&self) -> proc_macro2::TokenStream {
@@ -1499,7 +1538,7 @@ fn access_specifier(
14991538
}
15001539

15011540
impl<'a> FieldCodegen<'a> for BitfieldUnit {
1502-
type Extra = ();
1541+
type Extra = &'a str;
15031542

15041543
fn codegen<F, M>(
15051544
&self,
@@ -1512,7 +1551,7 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit {
15121551
struct_layout: &mut StructLayoutTracker,
15131552
fields: &mut F,
15141553
methods: &mut M,
1515-
_: (),
1554+
parent_canonical_name: Self::Extra,
15161555
) where
15171556
F: Extend<proc_macro2::TokenStream>,
15181557
M: Extend<proc_macro2::TokenStream>,
@@ -1525,16 +1564,12 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit {
15251564
let unit_field_ty = helpers::bitfield_unit(ctx, layout);
15261565
let field_ty = {
15271566
if parent.is_union() && !struct_layout.is_rust_union() {
1528-
result.saw_bindgen_union();
1529-
if ctx.options().enable_cxx_namespaces {
1530-
quote! {
1531-
root::__BindgenUnionField<#unit_field_ty>
1532-
}
1533-
} else {
1534-
quote! {
1535-
__BindgenUnionField<#unit_field_ty>
1536-
}
1537-
}
1567+
wrap_non_copy_type_for_union(
1568+
ctx,
1569+
parent_canonical_name,
1570+
result,
1571+
unit_field_ty.clone(),
1572+
)
15381573
} else {
15391574
unit_field_ty.clone()
15401575
}
@@ -1859,7 +1894,7 @@ impl CodeGenerator for CompInfo {
18591894
&mut struct_layout,
18601895
&mut fields,
18611896
&mut methods,
1862-
(),
1897+
&canonical_name,
18631898
);
18641899
}
18651900
// Check whether an explicit padding field is needed
@@ -1965,7 +2000,10 @@ impl CodeGenerator for CompInfo {
19652000
explicit_align = Some(layout.align);
19662001
}
19672002

1968-
if !struct_layout.is_rust_union() {
2003+
if !struct_layout.is_rust_union() &&
2004+
union_style_from_ctx_and_name(ctx, &canonical_name) ==
2005+
NonCopyUnionStyle::BindgenWrapper
2006+
{
19692007
let ty = helpers::blob(ctx, layout);
19702008
fields.push(quote! {
19712009
pub bindgen_union_field: #ty ,
@@ -2092,6 +2130,15 @@ impl CodeGenerator for CompInfo {
20922130
#( #attributes )*
20932131
pub union #canonical_ident
20942132
}
2133+
} else if is_union &&
2134+
!struct_layout.is_rust_union() &&
2135+
union_style_from_ctx_and_name(ctx, &canonical_name) ==
2136+
NonCopyUnionStyle::ManuallyDrop
2137+
{
2138+
quote! {
2139+
#( #attributes )*
2140+
pub union #canonical_ident
2141+
}
20952142
} else {
20962143
quote! {
20972144
#( #attributes )*
@@ -3398,6 +3445,52 @@ impl std::str::FromStr for AliasVariation {
33983445
}
33993446
}
34003447

3448+
/// Enum for how non-Copy unions should be translated.
3449+
#[derive(Copy, Clone, PartialEq, Debug)]
3450+
pub enum NonCopyUnionStyle {
3451+
/// Wrap members in a type generated by bindgen.
3452+
BindgenWrapper,
3453+
/// Wrap members in [`::core::mem::ManuallyDrop`].
3454+
///
3455+
/// Note: `ManuallyDrop` was stabilized in Rust 1.20.0, do not use it if your
3456+
/// MSRV is lower.
3457+
ManuallyDrop,
3458+
}
3459+
3460+
impl NonCopyUnionStyle {
3461+
/// Convert an `NonCopyUnionStyle` to its str representation.
3462+
pub fn as_str(&self) -> &'static str {
3463+
match self {
3464+
Self::BindgenWrapper => "bindgen_wrapper",
3465+
Self::ManuallyDrop => "manually_drop",
3466+
}
3467+
}
3468+
}
3469+
3470+
impl Default for NonCopyUnionStyle {
3471+
fn default() -> Self {
3472+
Self::BindgenWrapper
3473+
}
3474+
}
3475+
3476+
impl std::str::FromStr for NonCopyUnionStyle {
3477+
type Err = std::io::Error;
3478+
3479+
fn from_str(s: &str) -> Result<Self, Self::Err> {
3480+
match s {
3481+
"bindgen_wrapper" => Ok(Self::BindgenWrapper),
3482+
"manually_drop" => Ok(Self::ManuallyDrop),
3483+
_ => Err(std::io::Error::new(
3484+
std::io::ErrorKind::InvalidInput,
3485+
concat!(
3486+
"Got an invalid NonCopyUnionStyle. Accepted values ",
3487+
"are 'bindgen_wrapper' and 'manually_drop'"
3488+
),
3489+
)),
3490+
}
3491+
}
3492+
}
3493+
34013494
/// Fallible conversion to an opaque blob.
34023495
///
34033496
/// Implementors of this trait should provide the `try_get_layout` method to

src/lib.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ doc_mod!(ir, ir_docs);
6666
doc_mod!(parse, parse_docs);
6767
doc_mod!(regex_set, regex_set_docs);
6868

69-
pub use crate::codegen::{AliasVariation, EnumVariation, MacroTypeVariation};
69+
pub use crate::codegen::{
70+
AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle,
71+
};
7072
use crate::features::RustFeatures;
7173
pub use crate::features::{
7274
RustTarget, LATEST_STABLE_RUST, RUST_TARGET_STRINGS,
@@ -312,6 +314,13 @@ impl Builder {
312314
.push(self.options.default_alias_style.as_str().into());
313315
}
314316

317+
if self.options.default_non_copy_union_style != Default::default() {
318+
output_vector.push("--default-non-copy-union-style".into());
319+
output_vector.push(
320+
self.options.default_non_copy_union_style.as_str().into(),
321+
);
322+
}
323+
315324
let regex_sets = &[
316325
(&self.options.bitfield_enums, "--bitfield-enum"),
317326
(&self.options.newtype_enums, "--newtype-enum"),
@@ -329,6 +338,11 @@ impl Builder {
329338
(&self.options.type_alias, "--type-alias"),
330339
(&self.options.new_type_alias, "--new-type-alias"),
331340
(&self.options.new_type_alias_deref, "--new-type-alias-deref"),
341+
(
342+
&self.options.bindgen_wrapper_union,
343+
"--bindgen-wrapper-union",
344+
),
345+
(&self.options.manually_drop_union, "--manually-drop-union"),
332346
(&self.options.blocklisted_types, "--blocklist-type"),
333347
(&self.options.blocklisted_functions, "--blocklist-function"),
334348
(&self.options.blocklisted_items, "--blocklist-item"),
@@ -1101,6 +1115,32 @@ impl Builder {
11011115
self
11021116
}
11031117

1118+
/// Set the default style of code to generate for unions with a non-Copy member.
1119+
pub fn default_non_copy_union_style(
1120+
mut self,
1121+
arg: codegen::NonCopyUnionStyle,
1122+
) -> Self {
1123+
self.options.default_non_copy_union_style = arg;
1124+
self
1125+
}
1126+
1127+
/// Mark the given union (or set of union, if using a pattern) to use
1128+
/// a bindgen-generated wrapper for its members if at least one is non-Copy.
1129+
pub fn bindgen_wrapper_union<T: AsRef<str>>(mut self, arg: T) -> Self {
1130+
self.options.bindgen_wrapper_union.insert(arg);
1131+
self
1132+
}
1133+
1134+
/// Mark the given union (or set of union, if using a pattern) to use
1135+
/// [`::core::mem::ManuallyDrop`] for its members if at least one is non-Copy.
1136+
///
1137+
/// Note: `ManuallyDrop` was stabilized in Rust 1.20.0, do not use it if your
1138+
/// MSRV is lower.
1139+
pub fn manually_drop_union<T: AsRef<str>>(mut self, arg: T) -> Self {
1140+
self.options.manually_drop_union.insert(arg);
1141+
self
1142+
}
1143+
11041144
/// Add a string to prepend to the generated bindings. The string is passed
11051145
/// through without any modification.
11061146
pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Self {
@@ -1811,6 +1851,18 @@ struct BindgenOptions {
18111851
/// Deref and Deref to their aliased type.
18121852
new_type_alias_deref: RegexSet,
18131853

1854+
/// The default style of code to generate for union containing non-Copy
1855+
/// members.
1856+
default_non_copy_union_style: codegen::NonCopyUnionStyle,
1857+
1858+
/// The union patterns to mark an non-Copy union as using the bindgen
1859+
/// generated wrapper.
1860+
bindgen_wrapper_union: RegexSet,
1861+
1862+
/// The union patterns to mark an non-Copy union as using the
1863+
/// `::core::mem::ManuallyDrop` wrapper.
1864+
manually_drop_union: RegexSet,
1865+
18141866
/// Whether we should generate builtins or not.
18151867
builtins: bool,
18161868

@@ -2079,6 +2131,8 @@ impl BindgenOptions {
20792131
&mut self.type_alias,
20802132
&mut self.new_type_alias,
20812133
&mut self.new_type_alias_deref,
2134+
&mut self.bindgen_wrapper_union,
2135+
&mut self.manually_drop_union,
20822136
&mut self.no_partialeq_types,
20832137
&mut self.no_copy_types,
20842138
&mut self.no_debug_types,
@@ -2137,6 +2191,9 @@ impl Default for BindgenOptions {
21372191
type_alias: Default::default(),
21382192
new_type_alias: Default::default(),
21392193
new_type_alias_deref: Default::default(),
2194+
default_non_copy_union_style: Default::default(),
2195+
bindgen_wrapper_union: Default::default(),
2196+
manually_drop_union: Default::default(),
21402197
builtins: false,
21412198
emit_ast: false,
21422199
emit_ir: false,

0 commit comments

Comments
 (0)