diff options
22 files changed, 370 insertions, 39 deletions
@@ -1,5 +1,7 @@ # `bindgen` +[`impl period`](https://blog.rust-lang.org/2017/09/18/impl-future-for-rust.html) has been started! Join us at [Gitter.im](https://gitter.im/rust-impl-period/WG-dev-tools-bindgen). + **`bindgen` automatically generates Rust FFI bindings to C and C++ libraries.** For example, given the C header `doggo.h`: diff --git a/src/codegen/derive_debug.rs b/src/codegen/impl_debug.rs index 7ef108da..7ef108da 100644 --- a/src/codegen/derive_debug.rs +++ b/src/codegen/impl_debug.rs diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index ad4f65f4..0da01175 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1,4 +1,4 @@ -mod derive_debug; +mod impl_debug; mod error; mod helpers; pub mod struct_layout; @@ -865,24 +865,6 @@ impl CodeGenerator for TemplateInstantiation { } } -/// Generates an infinite number of anonymous field names. -struct AnonFieldNames(usize); - -impl Default for AnonFieldNames { - fn default() -> AnonFieldNames { - AnonFieldNames(0) - } -} - -impl Iterator for AnonFieldNames { - type Item = String; - - fn next(&mut self) -> Option<String> { - self.0 += 1; - Some(format!("__bindgen_anon_{}", self.0)) - } -} - /// Trait for implementing the code generation of a struct or union field. trait FieldCodegen<'a> { type Extra; @@ -894,7 +876,6 @@ trait FieldCodegen<'a> { codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, - anon_field_names: &mut AnonFieldNames, result: &mut CodegenResult, struct_layout: &mut StructLayoutTracker, fields: &mut F, @@ -915,7 +896,6 @@ impl<'a> FieldCodegen<'a> for Field { codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, - anon_field_names: &mut AnonFieldNames, result: &mut CodegenResult, struct_layout: &mut StructLayoutTracker, fields: &mut F, @@ -933,7 +913,6 @@ impl<'a> FieldCodegen<'a> for Field { codegen_depth, accessor_kind, parent, - anon_field_names, result, struct_layout, fields, @@ -948,7 +927,6 @@ impl<'a> FieldCodegen<'a> for Field { codegen_depth, accessor_kind, parent, - anon_field_names, result, struct_layout, fields, @@ -970,7 +948,6 @@ impl<'a> FieldCodegen<'a> for FieldData { codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, - anon_field_names: &mut AnonFieldNames, result: &mut CodegenResult, struct_layout: &mut StructLayoutTracker, fields: &mut F, @@ -1030,7 +1007,7 @@ impl<'a> FieldCodegen<'a> for FieldData { let field_name = self.name() .map(|name| ctx.rust_mangle(name).into_owned()) - .unwrap_or_else(|| anon_field_names.next().unwrap()); + .expect("Each field should have a name in codegen!"); let field_ident = ctx.rust_ident_raw(field_name.as_str()); if !parent.is_union() { @@ -1164,7 +1141,6 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, - anon_field_names: &mut AnonFieldNames, result: &mut CodegenResult, struct_layout: &mut StructLayoutTracker, fields: &mut F, @@ -1213,7 +1189,6 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { codegen_depth, accessor_kind, parent, - anon_field_names, result, struct_layout, fields, @@ -1321,7 +1296,6 @@ impl<'a> FieldCodegen<'a> for Bitfield { _codegen_depth: usize, _accessor_kind: FieldAccessorKind, parent: &CompInfo, - _anon_field_names: &mut AnonFieldNames, _result: &mut CodegenResult, _struct_layout: &mut StructLayoutTracker, _fields: &mut F, @@ -1559,7 +1533,7 @@ impl CodeGenerator for CompInfo { struct_layout.saw_vtable(); } - for (i, base) in self.base_members().iter().enumerate() { + for base in self.base_members() { // Virtual bases are already taken into account by the vtable // pointer. // @@ -1577,11 +1551,7 @@ impl CodeGenerator for CompInfo { } let inner = base.ty.to_rust_ty_or_opaque(ctx, &()); - let field_name = ctx.rust_ident(if i == 0 { - "_base".into() - } else { - format!("_base_{}", i) - }); + let field_name = ctx.rust_ident(&base.field_name); struct_layout.saw_base(base_ty); @@ -1599,7 +1569,6 @@ impl CodeGenerator for CompInfo { let mut methods = vec![]; if !is_opaque { - let mut anon_field_names = AnonFieldNames::default(); let codegen_depth = item.codegen_depth(ctx); let fields_should_be_private = item.annotations().private_fields().unwrap_or(false); @@ -1613,7 +1582,6 @@ impl CodeGenerator for CompInfo { codegen_depth, struct_accessor_kind, self, - &mut anon_field_names, result, &mut struct_layout, &mut fields, @@ -1914,7 +1882,7 @@ impl CodeGenerator for CompInfo { } if needs_debug_impl { - let impl_ = derive_debug::gen_debug_impl( + let impl_ = impl_debug::gen_debug_impl( ctx, self.fields(), item, diff --git a/src/ir/analysis/derive_copy.rs b/src/ir/analysis/derive_copy.rs index ef01c65a..264d227a 100644 --- a/src/ir/analysis/derive_copy.rs +++ b/src/ir/analysis/derive_copy.rs @@ -9,6 +9,7 @@ use ir::derive::CanTriviallyDeriveCopy; use ir::item::IsOpaque; use ir::template::TemplateParameters; use ir::traversal::EdgeKind; +use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT; use ir::ty::TypeKind; use std::collections::HashMap; use std::collections::HashSet; @@ -266,6 +267,14 @@ impl<'ctx> MonotoneFramework for CannotDeriveCopy<'ctx> { self.is_not_copy(data.ty()) } Field::Bitfields(ref bfu) => { + if bfu.layout().align > RUST_DERIVE_IN_ARRAY_LIMIT { + trace!( + " we cannot derive Copy for a bitfield larger then \ + the limit" + ); + return true; + } + bfu.bitfields().iter().any(|b| { self.is_not_copy(b.ty()) }) diff --git a/src/ir/analysis/derive_debug.rs b/src/ir/analysis/derive_debug.rs index 2bfaff71..7df745c9 100644 --- a/src/ir/analysis/derive_debug.rs +++ b/src/ir/analysis/derive_debug.rs @@ -268,6 +268,14 @@ impl<'ctx> MonotoneFramework for CannotDeriveDebug<'ctx> { self.is_not_debug(data.ty()) } Field::Bitfields(ref bfu) => { + if bfu.layout().align > RUST_DERIVE_IN_ARRAY_LIMIT { + trace!( + " we cannot derive Debug for a bitfield larger then \ + the limit" + ); + return true; + } + bfu.bitfields().iter().any(|b| { self.is_not_debug(b.ty()) }) diff --git a/src/ir/analysis/derive_default.rs b/src/ir/analysis/derive_default.rs index 96805863..7acbe04a 100644 --- a/src/ir/analysis/derive_default.rs +++ b/src/ir/analysis/derive_default.rs @@ -308,6 +308,14 @@ impl<'ctx> MonotoneFramework for CannotDeriveDefault<'ctx> { self.is_not_default(data.ty()) } Field::Bitfields(ref bfu) => { + if bfu.layout().align > RUST_DERIVE_IN_ARRAY_LIMIT { + trace!( + " we cannot derive Default for a bitfield larger then \ + the limit" + ); + return true; + } + bfu.bitfields().iter().any(|b| { !self.ctx.whitelisted_items().contains( &b.ty(), diff --git a/src/ir/analysis/derive_hash.rs b/src/ir/analysis/derive_hash.rs index 80ea0abf..5313cae3 100644 --- a/src/ir/analysis/derive_hash.rs +++ b/src/ir/analysis/derive_hash.rs @@ -283,6 +283,14 @@ impl<'ctx> MonotoneFramework for CannotDeriveHash<'ctx> { self.cannot_derive_hash.contains(&data.ty()) } Field::Bitfields(ref bfu) => { + if bfu.layout().align > RUST_DERIVE_IN_ARRAY_LIMIT { + trace!( + " we cannot derive Hash for a bitfield larger then \ + the limit" + ); + return true; + } + bfu.bitfields().iter().any(|b| { !self.ctx.whitelisted_items().contains( &b.ty(), diff --git a/src/ir/analysis/derive_partial_eq_or_partial_ord.rs b/src/ir/analysis/derive_partial_eq_or_partial_ord.rs index f90f1111..f34a256a 100644 --- a/src/ir/analysis/derive_partial_eq_or_partial_ord.rs +++ b/src/ir/analysis/derive_partial_eq_or_partial_ord.rs @@ -137,6 +137,10 @@ impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> { } }; + if self.ctx.no_partialeq_by_name(&item) { + return self.insert(id) + } + trace!("ty: {:?}", ty); if item.is_opaque(self.ctx, &()) { let layout_can_derive = ty.layout(self.ctx).map_or(true, |l| { @@ -297,6 +301,14 @@ impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> { ) } Field::Bitfields(ref bfu) => { + if bfu.layout().align > RUST_DERIVE_IN_ARRAY_LIMIT { + trace!( + " we cannot derive PartialEq for a bitfield larger then \ + the limit" + ); + return true; + } + bfu.bitfields().iter().any(|b| { !self.ctx.whitelisted_items().contains( &b.ty(), diff --git a/src/ir/comp.rs b/src/ir/comp.rs index bbccd06a..fc17ab8f 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -666,6 +666,33 @@ impl CompFields { CompFields::AfterComputingBitfieldUnits(fields_and_units), ); } + + fn deanonymize_fields(&mut self) { + let fields = match *self { + CompFields::AfterComputingBitfieldUnits(ref mut fields) => { + fields + } + CompFields::BeforeComputingBitfieldUnits(_) => { + panic!("Not yet computed bitfield units."); + } + }; + + let mut anon_field_counter = 0; + for field in fields.iter_mut() { + let field_data = match *field { + Field::DataMember(ref mut fd) => fd, + Field::Bitfields(_) => continue, + }; + + if let Some(_) = field_data.name { + continue; + } + + anon_field_counter += 1; + let name = format!("__bindgen_anon_{}", anon_field_counter); + field_data.name = Some(name); + } + } } impl Trace for CompFields { @@ -769,6 +796,8 @@ pub struct Base { pub ty: ItemId, /// The kind of inheritance we're doing. pub kind: BaseKind, + /// Name of the field in which this base should be stored. + pub field_name: String, } impl Base { @@ -1155,11 +1184,16 @@ impl CompInfo { BaseKind::Normal }; + let field_name = match ci.base_members.len() { + 0 => "_base".into(), + n => format!("_base_{}", n), + }; let type_id = Item::from_ty_or_ref(cur.cur_type(), cur, None, ctx); ci.base_members.push(Base { ty: type_id, kind: kind, + field_name: field_name, }); } CXCursor_Constructor | @@ -1344,6 +1378,11 @@ impl CompInfo { self.fields.compute_bitfield_units(ctx); } + /// Assign for each anonymous field a generated name. + pub fn deanonymize_fields(&mut self) { + self.fields.deanonymize_fields(); + } + /// Returns whether the current union can be represented as a Rust `union` /// /// Requirements: diff --git a/src/ir/context.rs b/src/ir/context.rs index 7340d308..b540d152 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -681,7 +681,8 @@ impl BindgenContext { "where" | "while" | "yield" | - "bool" => true, + "bool" | + "_" => true, _ => false, } { @@ -799,6 +800,18 @@ impl BindgenContext { } } + /// Assign a new generated name for each anonymous field. + fn deanonymize_fields(&mut self) { + let _t = self.timer("deanonymize_fields"); + let comp_types = self.items + .values_mut() + .filter_map(|item| item.kind_mut().as_type_mut()) + .filter_map(Type::as_comp_mut); + for comp_info in comp_types { + comp_info.deanonymize_fields(); + } + } + /// Iterate over all items and replace any item that has been named in a /// `replaces="SomeType"` annotation with the replacement type. fn process_replacements(&mut self) { @@ -940,6 +953,8 @@ impl BindgenContext { self.compute_bitfield_units(); self.process_replacements(); } + + self.deanonymize_fields(); // And assert once again, because resolving type refs and processing // replacements both mutate the IR graph. @@ -2212,6 +2227,12 @@ impl BindgenContext { // float or not. self.has_float.as_ref().unwrap().contains(id) } + + /// Check if `--no-partialeq` flag is enabled for this item. + pub fn no_partialeq_by_name(&self, item: &Item) -> bool { + let name = item.canonical_path(self)[1..].join("::"); + self.options().no_partialeq_types.matches(&name) + } } /// A builder struct for configuring item resolution options. @@ -247,7 +247,7 @@ impl Builder { output_vector.push("--no-derive-debug".into()); } - if !self.options.impl_debug { + if self.options.impl_debug { output_vector.push("--impl-debug".into()); } @@ -504,6 +504,20 @@ impl Builder { output_vector.push(path.into()); } + self.options + .no_partialeq_types + .get_items() + .iter() + .map(|item| { + output_vector.push("--no-partialeq".into()); + output_vector.push( + item.trim_left_matches("^") + .trim_right_matches("$") + .into(), + ); + }) + .count(); + output_vector } @@ -1127,6 +1141,13 @@ impl Builder { )) } } + + /// Don't derive `PartialEq` for a given type. Regular + /// expressions are supported. + pub fn no_partialeq(mut self, arg: String) -> Builder { + self.options.no_partialeq_types.insert(arg); + self + } } /// Configuration options for generated bindings. @@ -1305,7 +1326,11 @@ struct BindgenOptions { /// The absolute path to the rustfmt configuration file, if None, the standard rustfmt /// options are used. + rustfmt_configuration_file: Option<PathBuf>, + + /// The set of types that we should not derive `PartialEq` for. + no_partialeq_types: RegexSet, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -1323,6 +1348,7 @@ impl BindgenOptions { self.bitfield_enums.build(); self.constified_enum_modules.build(); self.rustified_enums.build(); + self.no_partialeq_types.build(); } /// Update rust target version @@ -1392,6 +1418,7 @@ impl Default for BindgenOptions { time_phases: false, rustfmt_bindings: false, rustfmt_configuration_file: None, + no_partialeq_types: Default::default(), } } } diff --git a/src/options.rs b/src/options.rs index 6f5ad6c8..d8a98641 100644 --- a/src/options.rs +++ b/src/options.rs @@ -270,6 +270,13 @@ where .takes_value(true) .multiple(false) .number_of_values(1), + Arg::with_name("no-partialeq") + .long("no-partialeq") + .help("Avoid deriving PartialEq for types matching <regex>.") + .value_name("regex") + .takes_value(true) + .multiple(true) + .number_of_values(1), ]) // .args() .get_matches_from(args); @@ -546,6 +553,12 @@ where builder = builder.rustfmt_configuration_file(Some(path)); } + if let Some(no_partialeq) = matches.values_of("no-partialeq") { + for regex in no_partialeq { + builder = builder.no_partialeq(String::from(regex)); + } + } + let verbose = matches.is_present("verbose"); Ok((builder, output, verbose)) diff --git a/tests/expectations/tests/bitfield_large_overflow.rs b/tests/expectations/tests/bitfield_large_overflow.rs new file mode 100644 index 00000000..523570e4 --- /dev/null +++ b/tests/expectations/tests/bitfield_large_overflow.rs @@ -0,0 +1,21 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] + + +#[repr(C)] +pub struct _bindgen_ty_1 { + pub _bitfield_1: [u8; 128usize], + pub __bindgen_align: [u64; 0usize], +} +impl Default for _bindgen_ty_1 { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +extern "C" { + #[link_name = "a"] + pub static mut a: _bindgen_ty_1; +} + diff --git a/tests/expectations/tests/no-partialeq-opaque.rs b/tests/expectations/tests/no-partialeq-opaque.rs new file mode 100644 index 00000000..8fd5451f --- /dev/null +++ b/tests/expectations/tests/no-partialeq-opaque.rs @@ -0,0 +1,29 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] + + +#[repr(C)] +#[derive(Debug, Default, Copy)] +pub struct NoPartialEq { + pub _bindgen_opaque_blob: u32, +} +#[test] +fn bindgen_test_layout_NoPartialEq() { + assert_eq!( + ::std::mem::size_of::<NoPartialEq>(), + 4usize, + concat!("Size of: ", stringify!(NoPartialEq)) + ); + assert_eq!( + ::std::mem::align_of::<NoPartialEq>(), + 4usize, + concat!("Alignment of ", stringify!(NoPartialEq)) + ); +} +impl Clone for NoPartialEq { + fn clone(&self) -> Self { + *self + } +} diff --git a/tests/expectations/tests/no-partialeq-whitelisted.rs b/tests/expectations/tests/no-partialeq-whitelisted.rs new file mode 100644 index 00000000..f07864ce --- /dev/null +++ b/tests/expectations/tests/no-partialeq-whitelisted.rs @@ -0,0 +1,39 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] + + +#[repr(C)] +#[derive(Debug, Default, Copy)] +pub struct NoPartialEq { + pub i: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_NoPartialEq() { + assert_eq!( + ::std::mem::size_of::<NoPartialEq>(), + 4usize, + concat!("Size of: ", stringify!(NoPartialEq)) + ); + assert_eq!( + ::std::mem::align_of::<NoPartialEq>(), + 4usize, + concat!("Alignment of ", stringify!(NoPartialEq)) + ); + assert_eq!( + unsafe { &(*(0 as *const NoPartialEq)).i as *const _ as usize }, + 0usize, + concat!( + "Alignment of field: ", + stringify!(NoPartialEq), + "::", + stringify!(i) + ) + ); +} +impl Clone for NoPartialEq { + fn clone(&self) -> Self { + *self + } +} diff --git a/tests/expectations/tests/underscore.rs b/tests/expectations/tests/underscore.rs new file mode 100644 index 00000000..d1b0ccc2 --- /dev/null +++ b/tests/expectations/tests/underscore.rs @@ -0,0 +1,40 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] + + +pub const __: ::std::os::raw::c_int = 10; +#[repr(C)] +#[derive(Debug, Default, Copy)] +pub struct ptr_t { + pub __: [::std::os::raw::c_uchar; 8usize], +} +#[test] +fn bindgen_test_layout_ptr_t() { + assert_eq!( + ::std::mem::size_of::<ptr_t>(), + 8usize, + concat!("Size of: ", stringify!(ptr_t)) + ); + assert_eq!( + ::std::mem::align_of::<ptr_t>(), + 1usize, + concat!("Alignment of ", stringify!(ptr_t)) + ); + assert_eq!( + unsafe { &(*(0 as *const ptr_t)).__ as *const _ as usize }, + 0usize, + concat!( + "Alignment of field: ", + stringify!(ptr_t), + "::", + stringify!(__) + ) + ); +} +impl Clone for ptr_t { + fn clone(&self) -> Self { + *self + } +} diff --git a/tests/expectations/tests/whitelisted-item-references-no-partialeq.rs b/tests/expectations/tests/whitelisted-item-references-no-partialeq.rs new file mode 100644 index 00000000..a7bbab74 --- /dev/null +++ b/tests/expectations/tests/whitelisted-item-references-no-partialeq.rs @@ -0,0 +1,62 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] + + +#[repr(C)] +#[derive(Debug, Default, Copy)] +pub struct NoPartialEq { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_NoPartialEq() { + assert_eq!( + ::std::mem::size_of::<NoPartialEq>(), + 1usize, + concat!("Size of: ", stringify!(NoPartialEq)) + ); + assert_eq!( + ::std::mem::align_of::<NoPartialEq>(), + 1usize, + concat!("Alignment of ", stringify!(NoPartialEq)) + ); +} +impl Clone for NoPartialEq { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[derive(Debug, Default, Copy)] +pub struct WhitelistMe { + pub a: NoPartialEq, +} +#[test] +fn bindgen_test_layout_WhitelistMe() { + assert_eq!( + ::std::mem::size_of::<WhitelistMe>(), + 1usize, + concat!("Size of: ", stringify!(WhitelistMe)) + ); + assert_eq!( + ::std::mem::align_of::<WhitelistMe>(), + 1usize, + concat!("Alignment of ", stringify!(WhitelistMe)) + ); + assert_eq!( + unsafe { &(*(0 as *const WhitelistMe)).a as *const _ as usize }, + 0usize, + concat!( + "Alignment of field: ", + stringify!(WhitelistMe), + "::", + stringify!(a) + ) + ); +} +impl Clone for WhitelistMe { + fn clone(&self) -> Self { + *self + } +} diff --git a/tests/headers/bitfield_large_overflow.hpp b/tests/headers/bitfield_large_overflow.hpp new file mode 100644 index 00000000..227829b8 --- /dev/null +++ b/tests/headers/bitfield_large_overflow.hpp @@ -0,0 +1,5 @@ +// bindgen-flags: --no-layout-tests + +struct { + unsigned : 632; +} a; diff --git a/tests/headers/no-partialeq-opaque.hpp b/tests/headers/no-partialeq-opaque.hpp new file mode 100644 index 00000000..a5a03cd2 --- /dev/null +++ b/tests/headers/no-partialeq-opaque.hpp @@ -0,0 +1,5 @@ +// bindgen-flags: --with-derive-partialeq --opaque-type "NoPartialEq" --no-partialeq "NoPartialEq" + +class NoPartialEq { + int i; +}; diff --git a/tests/headers/no-partialeq-whitelisted.hpp b/tests/headers/no-partialeq-whitelisted.hpp new file mode 100644 index 00000000..dba4e91f --- /dev/null +++ b/tests/headers/no-partialeq-whitelisted.hpp @@ -0,0 +1,5 @@ +// bindgen-flags: --with-derive-partialeq --whitelist-type "NoPartialEq" --no-partialeq "NoPartialEq" + +class NoPartialEq { + int i; +}; diff --git a/tests/headers/underscore.hpp b/tests/headers/underscore.hpp new file mode 100644 index 00000000..1c9371f1 --- /dev/null +++ b/tests/headers/underscore.hpp @@ -0,0 +1,3 @@ +const int _ = 10; + +typedef struct { unsigned char _[8]; } ptr_t;
\ No newline at end of file diff --git a/tests/headers/whitelisted-item-references-no-partialeq.hpp b/tests/headers/whitelisted-item-references-no-partialeq.hpp new file mode 100644 index 00000000..d9d3d431 --- /dev/null +++ b/tests/headers/whitelisted-item-references-no-partialeq.hpp @@ -0,0 +1,7 @@ +// bindgen-flags: --with-derive-partialeq --whitelist-type "WhitelistMe" --no-partialeq "NoPartialEq" + +struct NoPartialEq {}; + +class WhitelistMe { + NoPartialEq a; +}; |