Skip to content

Commit

Permalink
Auto merge of #919 - photoszzt:has_float, r=fitzgen
Browse files Browse the repository at this point in the history
Can derive Eq

Fix: #880 r? @fitzgen
  • Loading branch information
bors-servo authored Aug 22, 2017
2 parents 723e93d + ff9d000 commit 978b531
Show file tree
Hide file tree
Showing 200 changed files with 803 additions and 414 deletions.
15 changes: 13 additions & 2 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use ir::comp::{Base, Bitfield, BitfieldUnit, CompInfo, CompKind, Field,
FieldData, FieldMethods, Method, MethodKind};
use ir::context::{BindgenContext, ItemId};
use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault,
CanDeriveHash, CanDerivePartialEq};
CanDeriveHash, CanDerivePartialEq, CanDeriveEq};
use ir::dot;
use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
use ir::function::{Abi, Function, FunctionSig};
Expand Down Expand Up @@ -1516,6 +1516,10 @@ impl CodeGenerator for CompInfo {
derives.push("PartialEq");
}

if item.can_derive_eq(ctx) {
derives.push("Eq");
}

if !derives.is_empty() {
attributes.push(attributes::derives(&derives))
}
Expand Down Expand Up @@ -3617,14 +3621,21 @@ mod utils {
}
).unwrap();

let union_field_eq_impl = quote_item!(&ctx.ext_cx(),
impl<T> ::$prefix::cmp::Eq for __BindgenUnionField<T> {
}
)
.unwrap();

let items = vec![union_field_decl,
union_field_impl,
union_field_default_impl,
union_field_clone_impl,
union_field_copy_impl,
union_field_debug_impl,
union_field_hash_impl,
union_field_partialeq_impl];
union_field_partialeq_impl,
union_field_eq_impl];

let old_items = mem::replace(result, items);
result.extend(old_items.into_iter());
Expand Down
239 changes: 239 additions & 0 deletions src/ir/analysis/has_float.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
//! Determining which types has float.
use super::{ConstrainResult, MonotoneFramework, generate_dependencies};
use std::collections::HashSet;
use std::collections::HashMap;
use ir::context::{BindgenContext, ItemId};
use ir::traversal::EdgeKind;
use ir::ty::TypeKind;
use ir::comp::Field;
use ir::comp::FieldMethods;

/// An analysis that finds for each IR item whether it has float or not.
///
/// We use the monotone constraint function `has_float`,
/// defined as follows:
///
/// * If T is float or complex float, T trivially has.
/// * If T is a type alias, a templated alias or an indirection to another type,
/// it has float if the type T refers to has.
/// * If T is a compound type, it has float if any of base memter or field
/// has.
/// * If T is an instantiation of an abstract template definition, T has
/// float if any of the template arguments or template definition
/// has.
#[derive(Debug, Clone)]
pub struct HasFloat<'ctx, 'gen>
where 'gen: 'ctx
{
ctx: &'ctx BindgenContext<'gen>,

// The incremental result of this analysis's computation. Everything in this
// set has float.
has_float: HashSet<ItemId>,

// Dependencies saying that if a key ItemId has been inserted into the
// `has_float` set, then each of the ids in Vec<ItemId> need to be
// considered again.
//
// This is a subset of the natural IR graph with reversed edges, where we
// only include the edges from the IR graph that can affect whether a type
// has float or not.
dependencies: HashMap<ItemId, Vec<ItemId>>,
}

impl<'ctx, 'gen> HasFloat<'ctx, 'gen> {
fn consider_edge(kind: EdgeKind) -> bool {
match kind {
EdgeKind::BaseMember |
EdgeKind::Field |
EdgeKind::TypeReference |
EdgeKind::VarType |
EdgeKind::TemplateArgument |
EdgeKind::TemplateDeclaration |
EdgeKind::TemplateParameterDefinition => true,

EdgeKind::Constructor |
EdgeKind::Destructor |
EdgeKind::FunctionReturn |
EdgeKind::FunctionParameter |
EdgeKind::InnerType |
EdgeKind::InnerVar |
EdgeKind::Method => false,
EdgeKind::Generic => false,
}
}

fn insert(&mut self, id: ItemId) -> ConstrainResult {
trace!("inserting {:?} into the has_float set", id);

let was_not_already_in_set = self.has_float.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
);

ConstrainResult::Changed
}
}

impl<'ctx, 'gen> MonotoneFramework for HasFloat<'ctx, 'gen> {
type Node = ItemId;
type Extra = &'ctx BindgenContext<'gen>;
type Output = HashSet<ItemId>;

fn new(ctx: &'ctx BindgenContext<'gen>) -> HasFloat<'ctx, 'gen> {
let has_float = HashSet::new();
let dependencies = generate_dependencies(ctx, Self::consider_edge);

HasFloat {
ctx,
has_float,
dependencies,
}
}

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.has_float.contains(&id) {
trace!(" already know it do not have float");
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;
}
};

match *ty.kind() {
TypeKind::Void |
TypeKind::NullPtr |
TypeKind::Int(..) |
TypeKind::Function(..) |
TypeKind::Enum(..) |
TypeKind::Reference(..) |
TypeKind::BlockPointer |
TypeKind::TypeParam |
TypeKind::Opaque |
TypeKind::Pointer(..) |
TypeKind::UnresolvedTypeRef(..) |
TypeKind::ObjCInterface(..) |
TypeKind::ObjCId |
TypeKind::ObjCSel => {
trace!(" simple type that do not have float");
ConstrainResult::Same
}

TypeKind::Float(..) |
TypeKind::Complex(..) => {
trace!(" float type has float");
self.insert(id)
}

TypeKind::Array(t, _) => {
if self.has_float.contains(&t) {
trace!(" Array with type T that has float also has float");
return self.insert(id)
}
trace!(" Array with type T that do not have float also do not have float");
ConstrainResult::Same
}

TypeKind::ResolvedTypeRef(t) |
TypeKind::TemplateAlias(t, _) |
TypeKind::Alias(t) => {
if self.has_float.contains(&t) {
trace!(" aliases and type refs to T which have float \
also have float");
self.insert(id)
} else {
trace!(" aliases and type refs to T which do not have float \
also do not have floaarrayt");
ConstrainResult::Same
}
}

TypeKind::Comp(ref info) => {
let bases_have = info.base_members()
.iter()
.any(|base| self.has_float.contains(&base.ty));
if bases_have {
trace!(" bases have float, so we also have");
return self.insert(id);
}
let fields_have = info.fields()
.iter()
.any(|f| {
match *f {
Field::DataMember(ref data) => {
self.has_float.contains(&data.ty())
}
Field::Bitfields(ref bfu) => {
bfu.bitfields()
.iter().any(|b| {
self.has_float.contains(&b.ty())
})
},
}
});
if fields_have {
trace!(" fields have float, so we also have");
return self.insert(id);
}

trace!(" comp doesn't have float");
ConstrainResult::Same
}

TypeKind::TemplateInstantiation(ref template) => {
let args_have = template.template_arguments()
.iter()
.any(|arg| self.has_float.contains(&arg));
if args_have {
trace!(" template args have float, so \
insantiation also has float");
return self.insert(id);
}

let def_has = self.has_float
.contains(&template.template_definition());
if def_has {
trace!(" template definition has float, so \
insantiation also has");
return self.insert(id);
}

trace!(" template instantiation do not have float");
ConstrainResult::Same
}
}
}

fn each_depending_on<F>(&self, id: ItemId, mut f: F)
where F: FnMut(ItemId),
{
if let Some(edges) = self.dependencies.get(&id) {
for item in edges {
trace!("enqueue {:?} into worklist", item);
f(*item);
}
}
}
}

impl<'ctx, 'gen> From<HasFloat<'ctx, 'gen>> for HashSet<ItemId> {
fn from(analysis: HasFloat<'ctx, 'gen>) -> Self {
analysis.has_float
}
}
2 changes: 2 additions & 0 deletions src/ir/analysis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ mod derive_hash;
pub use self::derive_hash::CannotDeriveHash;
mod derive_partial_eq;
pub use self::derive_partial_eq::CannotDerivePartialEq;
mod has_float;
pub use self::has_float::HasFloat;

use ir::context::{BindgenContext, ItemId};
use ir::traversal::{EdgeKind, Trace};
Expand Down
51 changes: 43 additions & 8 deletions src/ir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
use super::analysis::{CannotDeriveCopy, CannotDeriveDebug,
CannotDeriveDefault, CannotDeriveHash,
CannotDerivePartialEq, HasTypeParameterInArray,
HasVtableAnalysis, UsedTemplateParameters, analyze};
HasVtableAnalysis, UsedTemplateParameters, HasFloat,
analyze};
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault,
CanDeriveHash, CanDerivePartialEq};
CanDeriveHash, CanDerivePartialEq, CanDeriveEq};
use super::int::IntKind;
use super::item::{HasTypeParamInArray, IsOpaque, Item, ItemAncestors,
ItemCanonicalPath, ItemSet};
Expand Down Expand Up @@ -76,6 +77,14 @@ impl CanDerivePartialEq for ItemId {
}
}

impl CanDeriveEq for ItemId {
fn can_derive_eq(&self, ctx: &BindgenContext) -> bool {
ctx.options().derive_eq &&
ctx.lookup_item_id_can_derive_partialeq(*self) &&
!ctx.lookup_item_id_has_float(&self)
}
}

/// A key used to index a resolved type, so we only process it once.
///
/// This is almost always a USR string (an unique identifier generated by
Expand Down Expand Up @@ -235,6 +244,12 @@ pub struct BindgenContext<'ctx> {
/// Populated when we enter codegen by `compute_has_type_param_in_array`; always `None`
/// before that and `Some` after.
has_type_param_in_array: Option<HashSet<ItemId>>,

/// The set of (`ItemId's of`) types that has float.
///
/// Populated when we enter codegen by `compute_has_float`; always `None`
/// before that and `Some` after.
has_float: Option<HashSet<ItemId>>,
}

/// A traversal of whitelisted items.
Expand Down Expand Up @@ -376,6 +391,7 @@ impl<'ctx> BindgenContext<'ctx> {
cannot_derive_partialeq: None,
have_vtable: None,
has_type_param_in_array: None,
has_float: None,
};

me.add_item(root_module, None, None);
Expand Down Expand Up @@ -890,8 +906,9 @@ impl<'ctx> BindgenContext<'ctx> {
self.compute_cannot_derive_default();
self.compute_cannot_derive_copy();
self.compute_has_type_param_in_array();
self.compute_has_float();
self.compute_cannot_derive_hash();
self.compute_cannot_derive_partialeq();
self.compute_cannot_derive_partialeq_or_eq();

let ret = cb(self);
self.gen_ctx = None;
Expand Down Expand Up @@ -2018,12 +2035,12 @@ impl<'ctx> BindgenContext<'ctx> {
!self.cannot_derive_hash.as_ref().unwrap().contains(&id)
}

/// Compute whether we can derive partialeq.
fn compute_cannot_derive_partialeq(&mut self) {
/// Compute whether we can derive PartialEq. This method is also used in calculating
/// whether we can derive Eq
fn compute_cannot_derive_partialeq_or_eq(&mut self) {
assert!(self.cannot_derive_partialeq.is_none());
if self.options.derive_partialeq {
self.cannot_derive_partialeq =
Some(analyze::<CannotDerivePartialEq>(self));
if self.options.derive_partialeq || self.options.derive_eq {
self.cannot_derive_partialeq = Some(analyze::<CannotDerivePartialEq>(self));
}
}

Expand Down Expand Up @@ -2072,6 +2089,24 @@ impl<'ctx> BindgenContext<'ctx> {
// type parameter in array or not.
self.has_type_param_in_array.as_ref().unwrap().contains(id)
}

/// Compute whether the type has float.
fn compute_has_float(&mut self) {
assert!(self.has_float.is_none());
if self.options.derive_eq {
self.has_float = Some(analyze::<HasFloat>(self));
}
}

/// Look up whether the item with `id` has array or not.
pub fn lookup_item_id_has_float(&self, id: &ItemId) -> bool {
assert!(self.in_codegen_phase(),
"We only compute has float when we enter codegen");

// Look up the computed value for whether the item with `id` has
// float or not.
self.has_float.as_ref().unwrap().contains(id)
}
}

/// A builder struct for configuring item resolution options.
Expand Down
Loading

0 comments on commit 978b531

Please sign in to comment.