diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/impl_partialeq.rs | 121 | ||||
-rw-r--r-- | src/codegen/mod.rs | 91 | ||||
-rw-r--r-- | src/ir/analysis/derive_partial_eq_or_partial_ord.rs | 455 | ||||
-rw-r--r-- | src/ir/analysis/mod.rs | 2 | ||||
-rw-r--r-- | src/ir/comp.rs | 21 | ||||
-rw-r--r-- | src/ir/context.rs | 18 | ||||
-rw-r--r-- | src/ir/derive.rs | 24 | ||||
-rw-r--r-- | src/ir/function.rs | 13 | ||||
-rw-r--r-- | src/ir/item.rs | 8 | ||||
-rw-r--r-- | src/ir/layout.rs | 12 | ||||
-rw-r--r-- | src/lib.rs | 45 | ||||
-rw-r--r-- | src/options.rs | 16 |
12 files changed, 541 insertions, 285 deletions
diff --git a/src/codegen/impl_partialeq.rs b/src/codegen/impl_partialeq.rs new file mode 100644 index 00000000..15a8953e --- /dev/null +++ b/src/codegen/impl_partialeq.rs @@ -0,0 +1,121 @@ + +use ir::comp::{CompInfo, CompKind, Field, FieldMethods}; +use ir::context::BindgenContext; +use ir::item::{IsOpaque, Item}; +use ir::ty::{TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT}; +use quote; + +/// Generate a manual implementation of `PartialEq` trait for the +/// specified compound type. +pub fn gen_partialeq_impl( + ctx: &BindgenContext, + comp_info: &CompInfo, + item: &Item, + ty_for_impl: "e::Tokens, +) -> Option<quote::Tokens> { + let mut tokens = vec![]; + + if item.is_opaque(ctx, &()) { + tokens.push(quote! { + &self._bindgen_opaque_blob[..] == &other._bindgen_opaque_blob[..] + }); + } else if comp_info.kind() == CompKind::Union { + tokens.push(quote! { + &self.bindgen_union_field[..] == &other.bindgen_union_field[..] + }); + } else { + for base in comp_info.base_members().iter() { + if !base.requires_storage(ctx) { + continue; + } + + let ty_item = ctx.resolve_item(base.ty); + let field_name = &base.field_name; + + if ty_item.is_opaque(ctx, &()) { + let field_name = ctx.rust_ident(field_name); + tokens.push(quote! { + &self. #field_name [..] == &other. #field_name [..] + }); + } else { + tokens.push(gen_field(ctx, ty_item, field_name)); + } + } + + for field in comp_info.fields() { + match *field { + Field::DataMember(ref fd) => { + let ty_item = ctx.resolve_item(fd.ty()); + let name = fd.name().unwrap(); + tokens.push(gen_field(ctx, ty_item, name)); + } + Field::Bitfields(ref bu) => for bitfield in bu.bitfields() { + let name_ident = ctx.rust_ident_raw(bitfield.name()); + tokens.push(quote! { + self.#name_ident () == other.#name_ident () + }); + }, + } + } + } + + Some(quote! { + fn eq(&self, other: & #ty_for_impl) -> bool { + #( #tokens )&&* + } + }) +} + +fn gen_field(ctx: &BindgenContext, ty_item: &Item, name: &str) -> quote::Tokens { + fn quote_equals(name_ident: quote::Ident) -> quote::Tokens { + quote! { self.#name_ident == other.#name_ident } + } + + let name_ident = ctx.rust_ident(name); + let ty = ty_item.expect_type(); + + match *ty.kind() { + TypeKind::Void | + TypeKind::NullPtr | + TypeKind::Int(..) | + TypeKind::Complex(..) | + TypeKind::Float(..) | + TypeKind::Enum(..) | + TypeKind::TypeParam | + TypeKind::UnresolvedTypeRef(..) | + TypeKind::BlockPointer | + TypeKind::Reference(..) | + TypeKind::ObjCInterface(..) | + TypeKind::ObjCId | + TypeKind::ObjCSel | + TypeKind::Comp(..) | + TypeKind::Pointer(_) | + TypeKind::Function(..) | + TypeKind::Opaque => quote_equals(name_ident), + + TypeKind::TemplateInstantiation(ref inst) => { + if inst.is_opaque(ctx, &ty_item) { + quote! { + &self. #name_ident [..] == &other. #name_ident [..] + } + } else { + quote_equals(name_ident) + } + } + + TypeKind::Array(_, len) => if len <= RUST_DERIVE_IN_ARRAY_LIMIT { + quote_equals(name_ident) + } else { + quote! { + &self. #name_ident [..] == &other. #name_ident [..] + } + }, + + TypeKind::ResolvedTypeRef(t) | + TypeKind::TemplateAlias(t, _) | + TypeKind::Alias(t) => { + let inner_item = ctx.resolve_item(t); + gen_field(ctx, inner_item, name) + } + } +} diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 264b701b..9ccd79c1 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1,4 +1,5 @@ mod impl_debug; +mod impl_partialeq; mod error; mod helpers; pub mod struct_layout; @@ -14,7 +15,7 @@ use ir::comp::{Base, Bitfield, BitfieldUnit, CompInfo, CompKind, Field, use ir::context::{BindgenContext, ItemId}; use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveHash, CanDerivePartialOrd, CanDeriveOrd, - CanDerivePartialEq, CanDeriveEq}; + CanDerivePartialEq, CanDeriveEq, CannotDeriveReason}; use ir::dot; use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; use ir::function::{Abi, Function, FunctionSig}; @@ -1420,6 +1421,7 @@ impl CodeGenerator for CompInfo { let mut needs_clone_impl = false; let mut needs_default_impl = false; let mut needs_debug_impl = false; + let mut needs_partialeq_impl = false; if let Some(comment) = item.comment(ctx) { attributes.push(attributes::doc(comment)); } @@ -1475,6 +1477,14 @@ impl CodeGenerator for CompInfo { if item.can_derive_partialeq(ctx) { derives.push("PartialEq"); + } else { + needs_partialeq_impl = + ctx.options().derive_partialeq && + ctx.options().impl_partialeq && + ctx.lookup_can_derive_partialeq_or_partialord(item.id()) + .map_or(true, |x| { + x == CannotDeriveReason::ArrayTooLarge + }); } if item.can_derive_eq(ctx) { @@ -1535,25 +1545,14 @@ impl CodeGenerator for CompInfo { } for base in self.base_members() { - // Virtual bases are already taken into account by the vtable - // pointer. - // - // FIXME(emilio): Is this always right? - if base.is_virtual() { - continue; - } - - let base_ty = ctx.resolve_type(base.ty); - // NB: We won't include unsized types in our base chain because they - // would contribute to our size given the dummy field we insert for - // unsized types. - if base_ty.is_unsized(ctx, base.ty) { + if !base.requires_storage(ctx) { continue; } let inner = base.ty.to_rust_ty_or_opaque(ctx, &()); let field_name = ctx.rust_ident(&base.field_name); + let base_ty = ctx.resolve_type(base.ty); struct_layout.saw_base(base_ty); fields.push(quote! { @@ -1667,33 +1666,34 @@ impl CodeGenerator for CompInfo { } } - let mut generics = quote! {}; + let mut generic_param_names = vec![]; if let Some(ref params) = used_template_params { - if !params.is_empty() { - let mut param_names = vec![]; + for (idx, ty) in params.iter().enumerate() { + let param = ctx.resolve_type(*ty); + let name = param.name().unwrap(); + let ident = ctx.rust_ident(name); + generic_param_names.push(ident.clone()); - for (idx, ty) in params.iter().enumerate() { - let param = ctx.resolve_type(*ty); - let name = param.name().unwrap(); - let ident = ctx.rust_ident(name); - param_names.push(ident.clone()); - - let prefix = ctx.trait_prefix(); - let field_name = ctx.rust_ident(format!("_phantom_{}", idx)); - fields.push(quote! { - pub #field_name : ::#prefix::marker::PhantomData< - ::#prefix::cell::UnsafeCell<#ident> - > , - }); - } - - generics = quote! { - < #( #param_names ),* > - }; + let prefix = ctx.trait_prefix(); + let field_name = ctx.rust_ident(format!("_phantom_{}", idx)); + fields.push(quote! { + pub #field_name : ::#prefix::marker::PhantomData< + ::#prefix::cell::UnsafeCell<#ident> + > , + }); } } + let generics = if !generic_param_names.is_empty() { + let generic_param_names = generic_param_names.clone(); + quote! { + < #( #generic_param_names ),* > + } + } else { + quote! { } + }; + tokens.append(quote! { #generics { #( #fields )* @@ -1896,6 +1896,27 @@ impl CodeGenerator for CompInfo { }); } + if needs_partialeq_impl { + if let Some(impl_) = impl_partialeq::gen_partialeq_impl(ctx, self, item, &ty_for_impl) { + + let partialeq_bounds = if !generic_param_names.is_empty() { + let bounds = generic_param_names.iter().map(|t| { + quote! { #t: PartialEq } + }); + quote! { where #( #bounds ),* } + } else { + quote! { } + }; + + let prefix = ctx.trait_prefix(); + result.push(quote! { + impl #generics ::#prefix::cmp::PartialEq for #ty_for_impl #partialeq_bounds { + #impl_ + } + }); + } + } + if !methods.is_empty() { result.push(quote! { impl #generics #ty_for_impl { 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 7efce6e5..0cca6c6d 100644 --- a/src/ir/analysis/derive_partial_eq_or_partial_ord.rs +++ b/src/ir/analysis/derive_partial_eq_or_partial_ord.rs @@ -3,16 +3,13 @@ use super::{ConstrainResult, MonotoneFramework, generate_dependencies}; use ir::comp::CompKind; -use ir::comp::Field; -use ir::comp::FieldMethods; use ir::context::{BindgenContext, ItemId}; -use ir::derive::CanTriviallyDerivePartialEqOrPartialOrd; -use ir::item::IsOpaque; -use ir::traversal::EdgeKind; +use ir::derive::{CanTriviallyDerivePartialEqOrPartialOrd, CanDerive, CannotDeriveReason}; +use ir::item::{Item, IsOpaque}; +use ir::traversal::{EdgeKind, Trace}; use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT; -use ir::ty::TypeKind; +use ir::ty::{TypeKind, Type}; use std::collections::HashMap; -use std::collections::HashSet; /// An analysis that finds for each IR item whether `PartialEq`/`PartialOrd` /// cannot be derived. @@ -23,9 +20,9 @@ use std::collections::HashSet; /// * If T is Opaque and layout of the type is known, get this layout as opaque /// type and check whether it can be derived using trivial checks. /// -/// * If T is Array type, `PartialEq` or partialord cannot be derived if the -/// length of the array is larger than the limit or the type of data the array -/// contains cannot derive `PartialEq`/`PartialOrd`. +/// * If T is Array type, `PartialEq` or partialord cannot be derived if the array is incomplete, if the length of +/// the array is larger than the limit, or the type of data the array contains cannot derive +/// `PartialEq`/`PartialOrd`. /// /// * If T is a type alias, a templated alias or an indirection to another type, /// `PartialEq`/`PartialOrd` cannot be derived if the type T refers to cannot be @@ -47,7 +44,7 @@ pub struct CannotDerivePartialEqOrPartialOrd<'ctx> { // The incremental result of this analysis's computation. Everything in this // set cannot derive `PartialEq`/`PartialOrd`. - cannot_derive_partialeq_or_partialord: HashSet<ItemId>, + cannot_derive_partialeq_or_partialord: HashMap<ItemId, CannotDeriveReason>, // Dependencies saying that if a key ItemId has been inserted into the // `cannot_derive_partialeq_or_partialord` set, then each of the ids @@ -83,91 +80,66 @@ impl<'ctx> CannotDerivePartialEqOrPartialOrd<'ctx> { } } - fn insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult { + fn insert<Id: Into<ItemId>>( + &mut self, + id: Id, + reason: CannotDeriveReason, + ) -> ConstrainResult { let id = id.into(); - trace!("inserting {:?} into the cannot_derive_partialeq_or_partialord set", id); - - let was_not_already_in_set = self.cannot_derive_partialeq_or_partialord.insert(id); - assert!( - was_not_already_in_set, - "We shouldn't try and insert {:?} twice because if it was \ - already in the set, `constrain` should have exited early.", - id + trace!( + "inserting {:?} into the cannot_derive_partialeq_or_partialord because {:?}", + id, + reason ); - + let existing = self.cannot_derive_partialeq_or_partialord + .insert(id, reason); + assert!(can_supersede(existing, Some(reason))); ConstrainResult::Changed } -} - -impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> { - type Node = ItemId; - type Extra = &'ctx BindgenContext; - type Output = HashSet<ItemId>; - - fn new( - ctx: &'ctx BindgenContext, - ) -> CannotDerivePartialEqOrPartialOrd<'ctx> { - let cannot_derive_partialeq_or_partialord = HashSet::new(); - let dependencies = generate_dependencies(ctx, Self::consider_edge); - CannotDerivePartialEqOrPartialOrd { - ctx, - cannot_derive_partialeq_or_partialord, - dependencies, + fn constrain_type( + &mut self, + item: &Item, + ty: &Type, + ) -> Option<CannotDeriveReason> { + if !self.ctx.whitelisted_items().contains(&item.id()) { + return Some(CannotDeriveReason::Other); } - } - - fn initial_worklist(&self) -> Vec<ItemId> { - self.ctx.whitelisted_items().iter().cloned().collect() - } - - fn constrain(&mut self, id: ItemId) -> ConstrainResult { - trace!("constrain: {:?}", id); - - if self.cannot_derive_partialeq_or_partialord.contains(&id) { - trace!(" already know it cannot derive `PartialEq`/`PartialOrd`"); - return ConstrainResult::Same; - } - - let item = self.ctx.resolve_item(id); - let ty = match item.as_type() { - Some(ty) => ty, - None => { - trace!(" not a type; ignoring"); - return ConstrainResult::Same; - } - }; if self.ctx.no_partialeq_by_name(&item) { - return self.insert(id) + return Some(CannotDeriveReason::Other); } trace!("ty: {:?}", ty); if item.is_opaque(self.ctx, &()) { - let layout_can_derive = ty.layout(self.ctx).map_or(true, |l| { - l.opaque().can_trivially_derive_partialeq_or_partialord() - }); - return if layout_can_derive && - !(ty.is_union() && - self.ctx.options().rust_features().untagged_union()) { - trace!(" we can trivially derive `PartialEq`/`PartialOrd` for the layout"); - ConstrainResult::Same - } else { - trace!(" we cannot derive `PartialEq`/`PartialOrd` for the layout"); - self.insert(id) - }; - } + if ty.is_union() + && self.ctx.options().rust_features().untagged_union() + { + trace!( + " cannot derive `PartialEq`/`PartialOrd` for Rust unions" + ); + return Some(CannotDeriveReason::Other); + } - if ty.layout(self.ctx).map_or(false, |l| { - l.align > RUST_DERIVE_IN_ARRAY_LIMIT - }) - { - // We have to be conservative: the struct *could* have enough - // padding that we emit an array that is longer than - // `RUST_DERIVE_IN_ARRAY_LIMIT`. If we moved padding calculations - // into the IR and computed them before this analysis, then we could - // be precise rather than conservative here. - return self.insert(id); + let layout_can_derive = ty.layout(self.ctx) + .map_or(CanDerive::Yes, |l| { + l.opaque().can_trivially_derive_partialeq_or_partialord() + }); + + return match layout_can_derive { + CanDerive::Yes => { + trace!( + " we can trivially derive `PartialEq`/`PartialOrd` for the layout" + ); + None + } + CanDerive::No(reason) => { + trace!( + " we cannot derive `PartialEq`/`PartialOrd` for the layout" + ); + Some(reason) + } + }; } match *ty.kind() { @@ -186,25 +158,36 @@ impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> { TypeKind::ObjCInterface(..) | TypeKind::ObjCId | TypeKind::ObjCSel => { - trace!(" simple type that can always derive `PartialEq`/`PartialOrd`"); - ConstrainResult::Same + trace!( + " simple type that can always derive `PartialEq`/`PartialOrd`" + ); + return None; } TypeKind::Array(t, len) => { - if self.cannot_derive_partialeq_or_partialord.contains(&t.into()) { + if self.cannot_derive_partialeq_or_partialord.contains_key(&t.into()) { trace!( " arrays of T for which we cannot derive `PartialEq`/`PartialOrd` \ - also cannot derive `PartialEq`/`PartialOrd`" + also cannot derive `PartialEq`/`PartialOrd`" ); - return self.insert(id); + return Some(CannotDeriveReason::Other); } - if len <= RUST_DERIVE_IN_ARRAY_LIMIT { - trace!(" array is small enough to derive `PartialEq`/`PartialOrd`"); - ConstrainResult::Same + if len == 0 { + trace!( + " cannot derive `PartialEq`/`PartialOrd` for incomplete arrays" + ); + return Some(CannotDeriveReason::Other); + } else if len <= RUST_DERIVE_IN_ARRAY_LIMIT { + trace!( + " array is small enough to derive `PartialEq`/`PartialOrd`" + ); + return None; } else { - trace!(" array is too large to derive `PartialEq`/`PartialOrd`"); - self.insert(id) + trace!( + " array is too large to derive `PartialEq`/`PartialOrd`" + ); + return Some(CannotDeriveReason::ArrayTooLarge); } } @@ -212,44 +195,30 @@ impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> { let inner_type = self.ctx.resolve_type(inner).canonical_type(self.ctx); if let TypeKind::Function(ref sig) = *inner_type.kind() { - if !sig.can_trivially_derive_partialeq_or_partialord() { + if let CanDerive::No(_) = + sig.can_trivially_derive_partialeq_or_partialord() + { trace!( " function pointer that can't trivially derive `PartialEq`/`PartialOrd`" ); - return self.insert(id); + return Some(CannotDeriveReason::Other); } } - trace!(" pointers can derive PartialEq"); - ConstrainResult::Same + trace!(" pointers can derive `PartialEq`/`PartialOrd`"); + return None; } TypeKind::Function(ref sig) => { - if !sig.can_trivially_derive_partialeq_or_partialord() { + if let CanDerive::No(_) = + sig.can_trivially_derive_partialeq_or_partialord() + { trace!( " function that can't trivially derive `PartialEq`/`PartialOrd`" ); - return self.insert(id); + return Some(CannotDeriveReason::Other); } trace!(" function can derive `PartialEq`/`PartialOrd`"); - ConstrainResult::Same - } - - TypeKind::ResolvedTypeRef(t) | - TypeKind::TemplateAlias(t, _) | - TypeKind::Alias(t) => { - if self.cannot_derive_partialeq_or_partialord.contains(&t.into()) { - trace!( - " aliases and type refs to T which cannot derive \ - `PartialEq`/`PartialOrd` also cannot derive `PartialEq`/`PartialOrd`" - ); - self.insert(id) - } else { - trace!( - " aliases and type refs to T which can derive \ - `PartialEq`/`PartialOrd` can also derive `PartialEq`/`PartialOrd`" - ); - ConstrainResult::Same - } + return None; } TypeKind::Comp(ref info) => { @@ -260,114 +229,180 @@ impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> { if info.kind() == CompKind::Union { if self.ctx.options().rust_features().untagged_union() { - trace!(" cannot derive `PartialEq`/`PartialOrd` for Rust unions"); - return self.insert(id); - } - - if ty.layout(self.ctx).map_or(true, |l| { - l.opaque().can_trivially_derive_partialeq_or_partialord() - }) - { trace!( - " union layout can trivially derive `PartialEq`/`PartialOrd`" + " cannot derive `PartialEq`/`PartialOrd` for Rust unions" ); - return ConstrainResult::Same; - } else { - trace!(" union layout cannot derive `PartialEq`/`PartialOrd`"); - return self.insert(id); + return Some(CannotDeriveReason::Other); } - } - let bases_cannot_derive = - info.base_members().iter().any(|base| { - !self.ctx.whitelisted_items().contains(&base.ty.into()) || - self.cannot_derive_partialeq_or_partialord.contains(&base.ty.into()) - }); - if bases_cannot_derive { - trace!( - " base members cannot derive `PartialEq`/`PartialOrd`, so we can't \ - either" + let layout_can_derive = ty.layout(self.ctx).map_or( + CanDerive::Yes, + |l| { + l.opaque() + .can_trivially_derive_partialeq_or_partialord() + }, ); - return self.insert(id); - } - - let fields_cannot_derive = - info.fields().iter().any(|f| match *f { - Field::DataMember(ref data) => { - !self.ctx.whitelisted_items().contains( - &data.ty().into(), - ) || - self.cannot_derive_partialeq_or_partialord.contains( - &data.ty().into(), - ) + return match layout_can_derive { + CanDerive::Yes => { + trace!( + " union layout can trivially derive `PartialEq`/`PartialOrd`" + ); + None } - 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().into(), - ) || - self.cannot_derive_partialeq_or_partialord.contains( - &b.ty().into(), - ) - }) + CanDerive::No(reason) => { + trace!( + " union layout cannot derive `PartialEq`/`PartialOrd`" + ); + Some(reason) } - }); - if fields_cannot_derive { - trace!( - " fields cannot derive `PartialEq`/`PartialOrd`, so we can't either" - ); - return self.insert(id); + }; } + return self.constrain_join(item); + } - trace!(" comp can derive PartialEq"); - ConstrainResult::Same + TypeKind::ResolvedTypeRef(..) | + TypeKind::TemplateAlias(..) | + TypeKind::Alias(..) | + TypeKind::TemplateInstantiation(..) => { + return self.constrain_join(item); } - TypeKind::TemplateInstantiation(ref template) => { - let args_cannot_derive = - template.template_arguments().iter().any(|arg| { - self.cannot_derive_partialeq_or_partialord.contains(&arg.into()) - }); - if args_cannot_derive { - trace!( - " template args cannot derive `PartialEq`/`PartialOrd`, so \ - insantiation can't either" - ); - return self.insert(id); + TypeKind::Opaque => unreachable!( + "The early ty.is_opaque check should have handled this case" + ), + } + } + + fn constrain_join(&mut self, item: &Item) -> Option<CannotDeriveReason> { + let mut candidate = None; + + item.trace( + self.ctx, + &mut |sub_id, edge_kind| { + // Ignore ourselves, since union with ourself is a + // no-op. Ignore edges that aren't relevant to the + // analysis. + if sub_id == item.id() || !Self::consider_edge(edge_kind) { + return; } - assert!( - !template.template_definition().is_opaque(self.ctx, &()), - "The early ty.is_opaque check should have handled this case" - ); - let def_cannot_derive = self.cannot_derive_partialeq_or_partialord.contains( - &template.template_definition().into(), - ); - if def_cannot_derive { - trace!( - " template definition cannot derive `PartialEq`/`PartialOrd`, so \ - insantiation can't either" - ); - return self.insert(id); + let reason = self.cannot_derive_partialeq_or_partialord + .get(&sub_id) + .cloned(); + + if can_supersede(candidate, reason) { + candidate = reason; } + }, + &(), + ); - trace!(" template instantiation can derive `PartialEq`/`PartialOrd`"); - ConstrainResult::Same - } + candidate + } +} - TypeKind::Opaque => { - unreachable!( - "The early ty.is_opaque check should have handled this case" - ) +/// Check if the one reason could supersede another. +/// +/// To keep this analysis monotone we should go only in one direction. +/// If the abscence of the reason is at the bottom and `CannotDeriveReason::Other` +/// is at the top, then we can only go upwards. +/// +/// Other +/// ^ +/// | +/// ArrayTooLarge +/// ^ +/// | +/// None +/// +fn can_supersede(from: Option<CannotDeriveReason>, to: Option<CannotDeriveReason>) -> bool { + fn rank(maybe_reason: Option<CannotDeriveReason>) -> usize { + match maybe_reason { + None => 0, + Some(CannotDeriveReason::ArrayTooLarge) => 1, + Some(CannotDeriveReason::Other) => 2, + } + } + rank(from) <= rank(to) +} + +impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> { + type Node = ItemId; + type Extra = &'ctx BindgenContext; + type Output = HashMap<ItemId, CannotDeriveReason>; + + fn new( + ctx: &'ctx BindgenContext, + ) -> CannotDerivePartialEqOrPartialOrd<'ctx> { + let cannot_derive_partialeq_or_partialord = HashMap::new(); + let dependencies = generate_dependencies(ctx, Self::consider_edge); + + CannotDerivePartialEqOrPartialOrd { + ctx, + cannot_derive_partialeq_or_partialord, + dependencies, + } + } + + fn initial_worklist(&self) -> Vec<ItemId> { + // The transitive closure of all whitelisted items, including explicitly + // blacklisted items. + self.ctx + .whitelisted_items() + .iter() + .cloned() + .flat_map(|i| { + let mut reachable = vec![i]; + i.trace( + self.ctx, + &mut |s, _| { + reachable.push(s); + }, + &(), + ); + reachable + }) + .collect() + } + + fn constrain(&mut self, id: ItemId) -> ConstrainResult { + trace!("constrain: {:?}", id); + + if Some(CannotDeriveReason::Other) + == self.cannot_derive_partialeq_or_partialord.get(&id).cloned() + { + trace!( + " already know it cannot derive `PartialEq`/`PartialOrd`" + ); + return ConstrainResult::Same; + } + + let item = self.ctx.resolve_item(id); + let maybe_reason = match item.as_type() { + Some(ty) => { + self.constrain_type(item, ty).or_else(|| { + if ty.layout(self.ctx).map_or(false, |l| { + l.align > RUST_DERIVE_IN_ARRAY_LIMIT + }) + { + // We have to be conservative: the struct *could* have enough + // padding that we emit an array that is longer than + // `RUST_DERIVE_IN_ARRAY_LIMIT`. If we moved padding calculations + // into the IR and computed them before this analysis, then we could + // be precise rather than conservative here. + Some(CannotDeriveReason::ArrayTooLarge) + } else { + None + } + }) } + None => self.constrain_join(item), + }; + + if let Some(reason) = maybe_reason { + self.insert(id, reason) + } else { + ConstrainResult::Same } } @@ -384,7 +419,7 @@ impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> { } } -impl<'ctx> From<CannotDerivePartialEqOrPartialOrd<'ctx>> for HashSet<ItemId> { +impl<'ctx> From<CannotDerivePartialEqOrPartialOrd<'ctx>> for HashMap<ItemId, CannotDeriveReason> { fn from(analysis: CannotDerivePartialEqOrPartialOrd<'ctx>) -> Self { analysis.cannot_derive_partialeq_or_partialord } diff --git a/src/ir/analysis/mod.rs b/src/ir/analysis/mod.rs index 179fa33d..dfc96f0a 100644 --- a/src/ir/analysis/mod.rs +++ b/src/ir/analysis/mod.rs @@ -59,8 +59,8 @@ mod derive_partial_eq_or_partial_ord; pub use self::derive_partial_eq_or_partial_ord::CannotDerivePartialEqOrPartialOrd; mod has_float; pub use self::has_float::HasFloat; - use ir::context::{BindgenContext, ItemId}; + use ir::traversal::{EdgeKind, Trace}; use std::collections::HashMap; use std::fmt; diff --git a/src/ir/comp.rs b/src/ir/comp.rs index c1dd369b..6a90bcbf 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -806,6 +806,27 @@ impl Base { pub fn is_virtual(&self) -> bool { self.kind == BaseKind::Virtual } + + /// Whether this base class should have it's own field for storage. + pub fn requires_storage(&self, ctx: &BindgenContext) -> bool { + // Virtual bases are already taken into account by the vtable + // pointer. + // + // FIXME(emilio): Is this always right? + if self.is_virtual() { + return false; + } + + let base_ty = ctx.resolve_type(self.ty); + // NB: We won't include unsized types in our base chain because they + // would contribute to our size given the dummy field we insert for + // unsized types. + if base_ty.is_unsized(ctx, self.ty) { + return false; + } + + true + } } /// A compound type. diff --git a/src/ir/context.rs b/src/ir/context.rs index 37b71ba1..27a34162 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -7,7 +7,7 @@ use super::analysis::{CannotDeriveCopy, CannotDeriveDebug, HasFloat, analyze}; use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveHash, CanDerivePartialOrd, CanDeriveOrd, - CanDerivePartialEq, CanDeriveEq}; + CanDerivePartialEq, CanDeriveEq, CannotDeriveReason}; use super::int::IntKind; use super::item::{IsOpaque, Item, ItemAncestors, ItemCanonicalPath, ItemSet}; use super::item_kind::ItemKind; @@ -249,7 +249,7 @@ where { fn can_derive_partialord(&self, ctx: &BindgenContext) -> bool { ctx.options().derive_partialord && - ctx.lookup_can_derive_partialeq_or_partialord(*self) + ctx.lookup_can_derive_partialeq_or_partialord(*self).is_none() } } @@ -259,7 +259,7 @@ where { fn can_derive_partialeq(&self, ctx: &BindgenContext) -> bool { ctx.options().derive_partialeq && - ctx.lookup_can_derive_partialeq_or_partialord(*self) + ctx.lookup_can_derive_partialeq_or_partialord(*self).is_none() } } @@ -269,7 +269,7 @@ where { fn can_derive_eq(&self, ctx: &BindgenContext) -> bool { ctx.options().derive_eq && - ctx.lookup_can_derive_partialeq_or_partialord(*self) && + ctx.lookup_can_derive_partialeq_or_partialord(*self).is_none() && !ctx.lookup_has_float(*self) } } @@ -280,7 +280,7 @@ where { fn can_derive_ord(&self, ctx: &BindgenContext) -> bool { ctx.options().derive_ord && - ctx.lookup_can_derive_partialeq_or_partialord(*self) && + ctx.lookup_can_derive_partialeq_or_partialord(*self).is_none() && !ctx.lookup_has_float(*self) } } @@ -423,12 +423,12 @@ pub struct BindgenContext { /// and is always `None` before that and `Some` after. cannot_derive_hash: Option<HashSet<ItemId>>, - /// The set of (`ItemId`s of) types that can't derive hash. + /// The map why specified `ItemId`s of) types that can't derive hash. /// /// This is populated when we enter codegen by /// `compute_cannot_derive_partialord_partialeq_or_eq` and is always `None` /// before that and `Some` after. - cannot_derive_partialeq_or_partialord: Option<HashSet<ItemId>>, + cannot_derive_partialeq_or_partialord: Option<HashMap<ItemId, CannotDeriveReason>>, /// The set of (`ItemId's of`) types that has vtable. /// @@ -2365,7 +2365,7 @@ impl BindgenContext { } /// Look up whether the item with `id` can derive `Partial{Eq,Ord}`. - pub fn lookup_can_derive_partialeq_or_partialord<Id: Into<ItemId>>(&self, id: Id) -> bool { + pub fn lookup_can_derive_partialeq_or_partialord<Id: Into<ItemId>>(&self, id: Id) -> Option<CannotDeriveReason> { let id = id.into(); assert!( self.in_codegen_phase(), @@ -2374,7 +2374,7 @@ impl BindgenContext { // Look up the computed value for whether the item with `id` can // derive partialeq or not. - !self.cannot_derive_partialeq_or_partialord.as_ref().unwrap().contains(&id) + self.cannot_derive_partialeq_or_partialord.as_ref().unwrap().get(&id).cloned() } /// Look up whether the item with `id` can derive `Copy` or not. diff --git a/src/ir/derive.rs b/src/ir/derive.rs index cafbd3b1..52e61133 100644 --- a/src/ir/derive.rs +++ b/src/ir/derive.rs @@ -117,5 +117,27 @@ pub trait CanTriviallyDeriveHash { pub trait CanTriviallyDerivePartialEqOrPartialOrd { /// Return `true` if `PartialEq` or `PartialOrd` can trivially be derived /// for this thing, `false` otherwise. - fn can_trivially_derive_partialeq_or_partialord(&self) -> bool; + fn can_trivially_derive_partialeq_or_partialord(&self) -> CanDerive; +} + +/// Reason why exactly we cannot automatically derive a trait. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum CannotDeriveReason { + /// The only thing that stops us from automatically deriving is that + /// array with more than maximum number of elements is used. + /// + /// This means we probably can "manually" implement such trait. + ArrayTooLarge, + + /// Any other reason. + Other, +} + +/// Whether it is possible or not to derive trait automatically. +pub enum CanDerive { + /// Yes, we can! + Yes, + + /// No, we cannot. Contains reason why exactly we can't derive. + No(CannotDeriveReason) } diff --git a/src/ir/function.rs b/src/ir/function.rs index ccdfc4f3..3a9f337c 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -9,7 +9,7 @@ use super::ty::TypeKind; use clang; use clang_sys::{self, CXCallingConv}; use ir::derive::{CanTriviallyDeriveDebug, CanTriviallyDeriveHash, - CanTriviallyDerivePartialEqOrPartialOrd}; + CanTriviallyDerivePartialEqOrPartialOrd, CanDerive, CannotDeriveReason}; use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; use quote; use std::io; @@ -560,7 +560,14 @@ impl CanTriviallyDeriveHash for FunctionSig { } impl CanTriviallyDerivePartialEqOrPartialOrd for FunctionSig { - fn can_trivially_derive_partialeq_or_partialord(&self) -> bool { - self.function_pointers_can_derive() + fn can_trivially_derive_partialeq_or_partialord(&self) -> CanDerive { + if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT { + return CanDerive::No(CannotDeriveReason::Other); + } + + match self.abi { + Abi::C | Abi::Unknown(..) => CanDerive::Yes, + _ => CanDerive::No(CannotDeriveReason::Other), + } } } diff --git a/src/ir/item.rs b/src/ir/item.rs index 010528b6..dbc352eb 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -347,21 +347,21 @@ impl CanDeriveHash for Item { impl CanDerivePartialOrd for Item { fn can_derive_partialord(&self, ctx: &BindgenContext) -> bool { ctx.options().derive_partialord && - ctx.lookup_can_derive_partialeq_or_partialord(self.id()) + ctx.lookup_can_derive_partialeq_or_partialord(self.id()).is_none() } } impl CanDerivePartialEq for Item { fn can_derive_partialeq(&self, ctx: &BindgenContext) -> bool { ctx.options().derive_partialeq && - ctx.lookup_can_derive_partialeq_or_partialord(self.id()) + ctx.lookup_can_derive_partialeq_or_partialord(self.id()).is_none() } } impl CanDeriveEq for Item { fn can_derive_eq(&self, ctx: &BindgenContext) -> bool { ctx.options().derive_eq && - ctx.lookup_can_derive_partialeq_or_partialord(self.id()) && + ctx.lookup_can_derive_partialeq_or_partialord(self.id()).is_none() && !ctx.lookup_has_float(self.id()) } } @@ -369,7 +369,7 @@ impl CanDeriveEq for Item { impl CanDeriveOrd for Item { fn can_derive_ord(&self, ctx: &BindgenContext) -> bool { ctx.options().derive_ord && - ctx.lookup_can_derive_partialeq_or_partialord(self.id()) && + ctx.lookup_can_derive_partialeq_or_partialord(self.id()).is_none() && !ctx.lookup_has_float(self.id()) } } diff --git a/src/ir/layout.rs b/src/ir/layout.rs index 2df15ef2..0d9c123f 100644 --- a/src/ir/layout.rs +++ b/src/ir/layout.rs @@ -2,7 +2,7 @@ use super::derive::{CanTriviallyDeriveCopy, CanTriviallyDeriveDebug, CanTriviallyDeriveDefault, CanTriviallyDeriveHash, - CanTriviallyDerivePartialEqOrPartialOrd}; + CanTriviallyDerivePartialEqOrPartialOrd, CanDerive, CannotDeriveReason}; use super::ty::{RUST_DERIVE_IN_ARRAY_LIMIT, Type, TypeKind}; use clang; use std::{cmp, mem}; @@ -140,7 +140,13 @@ impl CanTriviallyDeriveHash for Opaque { } impl CanTriviallyDerivePartialEqOrPartialOrd for Opaque { - fn can_trivially_derive_partialeq_or_partialord(&self) -> bool { - self.array_size_within_derive_limit() + fn can_trivially_derive_partialeq_or_partialord(&self) -> CanDerive { + self.array_size().map_or(CanDerive::No(CannotDeriveReason::Other), |size| { + if size <= RUST_DERIVE_IN_ARRAY_LIMIT { + CanDerive::Yes + } else { + CanDerive::No(CannotDeriveReason::ArrayTooLarge) + } + }) } } @@ -239,6 +239,14 @@ impl Builder { output_vector.push("--no-layout-tests".into()); } + if self.options.impl_debug { + output_vector.push("--impl-debug".into()); + } + + if self.options.impl_partialeq { + output_vector.push("--impl-partialeq".into()); + } + if !self.options.derive_copy { output_vector.push("--no-derive-copy".into()); } @@ -247,10 +255,6 @@ impl Builder { output_vector.push("--no-derive-debug".into()); } - if self.options.impl_debug { - output_vector.push("--impl-debug".into()); - } - if !self.options.derive_default { output_vector.push("--no-derive-default".into()); } else { @@ -806,6 +810,18 @@ impl Builder { self } + /// Set whether `Debug` should be implemented, if it can not be derived automatically. + pub fn impl_debug(mut self, doit: bool) -> Self { + self.options.impl_debug = doit; + self + } + + /// Set whether `PartialEq` should be implemented, if it can not be derived automatically. + pub fn impl_partialeq(mut self, doit: bool) -> Self { + self.options.impl_partialeq = doit; + self + } + /// Set whether `Copy` should be derived by default. pub fn derive_copy(mut self, doit: bool) -> Self { self.options.derive_copy = doit; @@ -818,12 +834,6 @@ impl Builder { self } - /// Set whether `Debug` should be implemented, if it can not be derived automatically. - pub fn impl_debug(mut self, doit: bool) -> Self { - self.options.impl_debug = doit; - self - } - /// Set whether `Default` should be derived by default. pub fn derive_default(mut self, doit: bool) -> Self { self.options.derive_default = doit; @@ -1209,6 +1219,14 @@ struct BindgenOptions { /// True if we should generate layout tests for generated structures. layout_tests: bool, + /// True if we should implement the Debug trait for C/C++ structures and types + /// that do not support automatically deriving Debug. + impl_debug: bool, + + /// True if we should implement the PartialEq trait for C/C++ structures and types + /// that do not support autoamically deriving PartialEq. + impl_partialeq: bool, + /// True if we should derive Copy trait implementations for C/C++ structures /// and types. derive_copy: bool, @@ -1217,10 +1235,6 @@ struct BindgenOptions { /// and types. derive_debug: bool, - /// True if we should implement the Debug trait for C/C++ structures and types - /// that do not support automatically deriving Debug. - impl_debug: bool, - /// True if we should derive Default trait implementations for C/C++ structures /// and types. derive_default: bool, @@ -1386,9 +1400,10 @@ impl Default for BindgenOptions { emit_ir: false, emit_ir_graphviz: None, layout_tests: true, + impl_debug: false, + impl_partialeq: false, derive_copy: true, derive_debug: true, - impl_debug: false, derive_default: false, derive_hash: false, derive_partialord: false, diff --git a/src/options.rs b/src/options.rs index aec4a2bd..64ee249e 100644 --- a/src/options.rs +++ b/src/options.rs @@ -66,14 +66,18 @@ where Arg::with_name("no-derive-debug") .long("no-derive-debug") .help("Avoid deriving Debug on any type."), - Arg::with_name("impl-debug") - .long("impl-debug") - .help("Create Debug implementation, if it can not be derived \ - automatically."), Arg::with_name("no-derive-default") .long("no-derive-default") .hidden(true) .help("Avoid deriving Default on any type."), + Arg::with_name("impl-debug") + .long("impl-debug") + .help("Create Debug implementation, if it can not be derived \ + automatically."), + Arg::with_name("impl-partialeq") + .long("impl-partialeq") + .help("Create PartialEq implementation, if it can not be derived \ + automatically."), Arg::with_name("with-derive-default") .long("with-derive-default") .help("Derive Default on any type."), @@ -347,6 +351,10 @@ where builder = builder.impl_debug(true); } + if matches.is_present("impl-partialeq") { + builder = builder.impl_partialeq(true); + } + if matches.is_present("with-derive-default") { builder = builder.derive_default(true); } |