diff options
Diffstat (limited to 'src/ir/analysis/derive_debug.rs')
-rw-r--r-- | src/ir/analysis/derive_debug.rs | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/src/ir/analysis/derive_debug.rs b/src/ir/analysis/derive_debug.rs new file mode 100644 index 00000000..4169485f --- /dev/null +++ b/src/ir/analysis/derive_debug.rs @@ -0,0 +1,304 @@ +//! Determining which types for which we can emit `#[derive(Debug)]`. +use super::MonotoneFramework; +use ir::context::{BindgenContext, ItemId}; +use ir::item::ItemSet; +use std::collections::HashSet; +use std::collections::HashMap; +use ir::traversal::EdgeKind; +use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT; +use ir::ty::TypeKind; +use ir::comp::Field; +use ir::traversal::Trace; +use ir::comp::FieldMethods; +use ir::layout::Layout; +use ir::derive::CanTriviallyDeriveDebug; +use ir::comp::CompKind; + +/// An analysis that finds for each IR item whether debug cannot be derived. +/// +/// We use the monotone constraint function `cannot_derive_debug`, defined as +/// follows: +/// +/// * 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, debug cannot be derived if the length of the array is +/// larger than the limit or the type of data the array contains cannot derive +/// debug. +/// * If T is a type alias, a templated alias or an indirection to another type, +/// debug cannot be derived if the type T refers to cannot be derived debug. +/// * If T is a compound type, debug cannot be derived if any of its base member +/// or field cannot be derived debug. +/// * If T is a pointer, T cannot be derived debug if T is a function pointer +/// and the function signature cannot be derived debug. +/// * If T is an instantiation of an abstract template definition, T cannot be +/// derived debug if any of the template arguments or template definition +/// cannot derive debug. +#[derive(Debug, Clone)] +pub struct CannotDeriveDebugAnalysis<'ctx, 'gen> + where 'gen: 'ctx +{ + ctx: &'ctx BindgenContext<'gen>, + + // The incremental result of this analysis's computation. Everything in this + // set cannot derive debug. + cannot_derive_debug: HashSet<ItemId>, + + // Dependencies saying that if a key ItemId has been inserted into the + // `cannot_derive_debug` 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 + // can derive debug or not. + dependencies: HashMap<ItemId, Vec<ItemId>>, +} + +impl<'ctx, 'gen> CannotDeriveDebugAnalysis<'ctx, 'gen> { + fn consider_edge(kind: EdgeKind) -> bool { + match kind { + // These are the only edges that can affect whether a type can derive + // debug or not. + 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) -> bool { + let was_already_in = self.cannot_derive_debug.insert(id); + assert!( + was_already_in, + format!("We shouldn't try and insert twice because if it was already in the set, \ + `constrain` would have exited early.: {:?}", id) + ); + true + } +} + +impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDebugAnalysis<'ctx, 'gen> { + type Node = ItemId; + type Extra = &'ctx BindgenContext<'gen>; + type Output = HashSet<ItemId>; + + fn new(ctx: &'ctx BindgenContext<'gen>) -> CannotDeriveDebugAnalysis<'ctx, 'gen> { + let cannot_derive_debug = HashSet::new(); + let mut dependencies = HashMap::new(); + let whitelisted_items: HashSet<_> = ctx.whitelisted_items().collect(); + + let whitelisted_and_blacklisted_items: ItemSet = whitelisted_items.iter() + .cloned() + .flat_map(|i| { + let mut reachable = vec![i]; + i.trace(ctx, &mut |s, _| { + reachable.push(s); + }, &()); + reachable + }) + .collect(); + + for item in whitelisted_and_blacklisted_items { + dependencies.entry(item).or_insert(vec![]); + + { + // We reverse our natural IR graph edges to find dependencies + // between nodes. + item.trace(ctx, &mut |sub_item: ItemId, edge_kind| { + if Self::consider_edge(edge_kind) { + dependencies.entry(sub_item) + .or_insert(vec![]) + .push(item); + } + }, &()); + } + } + + CannotDeriveDebugAnalysis { + ctx: ctx, + cannot_derive_debug: cannot_derive_debug, + dependencies: dependencies, + } + } + + fn initial_worklist(&self) -> Vec<ItemId> { + self.ctx.whitelisted_items().collect() + } + + fn constrain(&mut self, id: ItemId) -> bool { + if self.cannot_derive_debug.contains(&id) { + return false; + } + + let item = self.ctx.resolve_item(id); + let ty = match item.as_type() { + None => return false, + Some(ty) => ty + }; + + match *ty.kind() { + // handle the simple case + // These can derive debug without further information + TypeKind::Void | + TypeKind::NullPtr | + TypeKind::Int(..) | + TypeKind::Float(..) | + TypeKind::Complex(..) | + TypeKind::Function(..) | + TypeKind::Enum(..) | + TypeKind::Reference(..) | + TypeKind::BlockPointer | + TypeKind::Named | + TypeKind::UnresolvedTypeRef(..) | + TypeKind::ObjCInterface(..) | + TypeKind::ObjCId | + TypeKind::ObjCSel => { + return false; + }, + TypeKind::Opaque => { + if ty.layout(self.ctx) + .map_or(true, |l| l.opaque().can_trivially_derive_debug(self.ctx, ())) { + return false; + } else { + return self.insert(id); + } + }, + TypeKind::Array(t, len) => { + if len <= RUST_DERIVE_IN_ARRAY_LIMIT { + if self.cannot_derive_debug.contains(&t) { + return self.insert(id); + } + return false; + } else { + return self.insert(id); + } + }, + TypeKind::ResolvedTypeRef(t) | + TypeKind::TemplateAlias(t, _) | + TypeKind::Alias(t) => { + if self.cannot_derive_debug.contains(&t) { + return self.insert(id); + } + return false; + }, + TypeKind::Comp(ref info) => { + if info.has_non_type_template_params() { + if ty.layout(self.ctx).map_or(true, + |l| l.opaque().can_trivially_derive_debug(self.ctx, ())) { + return false; + } else { + return self.insert(id); + } + } + if info.kind() == CompKind::Union { + if self.ctx.options().unstable_rust { + return self.insert(id); + } + + if ty.layout(self.ctx).map_or(true, + |l| l.opaque().can_trivially_derive_debug(self.ctx, ())) { + return false; + } else { + return self.insert(id); + } + } + let bases_cannot_derive = info.base_members() + .iter() + .any(|base| self.cannot_derive_debug.contains(&base.ty)); + if bases_cannot_derive { + return self.insert(id); + } + let fields_cannot_derive = info.fields() + .iter() + .any(|f| { + match f { + &Field::DataMember(ref data) => self.cannot_derive_debug.contains(&data.ty()), + &Field::Bitfields(ref bfu) => bfu.bitfields() + .iter().any(|b| { + self.cannot_derive_debug.contains(&b.ty()) + }) + } + }); + if fields_cannot_derive { + return self.insert(id); + } + false + }, + TypeKind::Pointer(inner) => { + let inner_type = self.ctx.resolve_type(inner); + if let TypeKind::Function(ref sig) = + *inner_type.canonical_type(self.ctx).kind() { + if sig.can_trivially_derive_debug(&self.ctx, ()) { + return false; + } else { + return self.insert(id); + } + } + false + }, + TypeKind::TemplateInstantiation(ref template) => { + let args_cannot_derive = template.template_arguments() + .iter() + .any(|arg| self.cannot_derive_debug.contains(&arg)); + if args_cannot_derive { + return self.insert(id); + } + let ty_cannot_derive = template.template_definition() + .into_resolver() + .through_type_refs() + .through_type_aliases() + .resolve(self.ctx) + .as_type() + .expect("Instantiations of a non-type?") + .as_comp() + .and_then(|c| { + // For non-type template parameters, we generate an opaque + // blob, and in this case the instantiation has a better + // idea of the layout than the definition does. + if c.has_non_type_template_params() { + let opaque = ty.layout(self.ctx) + .or_else(|| self.ctx.resolve_type(template.template_definition()).layout(self.ctx)) + .unwrap_or(Layout::zero()) + .opaque(); + Some(!opaque.can_trivially_derive_debug(&self.ctx, ())) + } else { + None + } + }) + .unwrap_or_else(|| self.cannot_derive_debug.contains(&template.template_definition())); + if ty_cannot_derive { + return self.insert(id); + } + false + }, + } + } + + 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<CannotDeriveDebugAnalysis<'ctx, 'gen>> for HashSet<ItemId> { + fn from(analysis: CannotDeriveDebugAnalysis<'ctx, 'gen>) -> Self { + analysis.cannot_derive_debug + } +} |