| //! Determining which types have destructors |
| |
| use super::{generate_dependencies, ConstrainResult, MonotoneFramework}; |
| use crate::ir::comp::{CompKind, Field, FieldMethods}; |
| use crate::ir::context::{BindgenContext, ItemId}; |
| use crate::ir::traversal::EdgeKind; |
| use crate::ir::ty::TypeKind; |
| use crate::{HashMap, HashSet}; |
| |
| /// An analysis that finds for each IR item whether it has a destructor or not |
| /// |
| /// We use the monotone function `has destructor`, defined as follows: |
| /// |
| /// * If T is a type alias, a templated alias, or an indirection to another type, |
| /// T has a destructor if the type T refers to has a destructor. |
| /// * If T is a compound type, T has a destructor if we saw a destructor when parsing it, |
| /// or if it's a struct, T has a destructor if any of its base members has a destructor, |
| /// or if any of its fields have a destructor. |
| /// * If T is an instantiation of an abstract template definition, T has |
| /// a destructor if its template definition has a destructor, |
| /// or if any of the template arguments has a destructor. |
| /// * If T is the type of a field, that field has a destructor if it's not a bitfield, |
| /// and if T has a destructor. |
| #[derive(Debug, Clone)] |
| pub struct HasDestructorAnalysis<'ctx> { |
| ctx: &'ctx BindgenContext, |
| |
| // The incremental result of this analysis's computation. Everything in this |
| // set definitely has a destructor. |
| have_destructor: HashSet<ItemId>, |
| |
| // Dependencies saying that if a key ItemId has been inserted into the |
| // `have_destructor` 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 a destructor or not. |
| dependencies: HashMap<ItemId, Vec<ItemId>>, |
| } |
| |
| impl<'ctx> HasDestructorAnalysis<'ctx> { |
| fn consider_edge(kind: EdgeKind) -> bool { |
| // These are the only edges that can affect whether a type has a |
| // destructor or not. |
| matches!( |
| kind, |
| EdgeKind::TypeReference | |
| EdgeKind::BaseMember | |
| EdgeKind::Field | |
| EdgeKind::TemplateArgument | |
| EdgeKind::TemplateDeclaration |
| ) |
| } |
| |
| fn insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult { |
| let id = id.into(); |
| let was_not_already_in_set = self.have_destructor.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> MonotoneFramework for HasDestructorAnalysis<'ctx> { |
| type Node = ItemId; |
| type Extra = &'ctx BindgenContext; |
| type Output = HashSet<ItemId>; |
| |
| fn new(ctx: &'ctx BindgenContext) -> Self { |
| let have_destructor = HashSet::default(); |
| let dependencies = generate_dependencies(ctx, Self::consider_edge); |
| |
| HasDestructorAnalysis { |
| ctx, |
| have_destructor, |
| dependencies, |
| } |
| } |
| |
| fn initial_worklist(&self) -> Vec<ItemId> { |
| self.ctx.allowlisted_items().iter().cloned().collect() |
| } |
| |
| fn constrain(&mut self, id: ItemId) -> ConstrainResult { |
| if self.have_destructor.contains(&id) { |
| // We've already computed that this type has a destructor and that can't |
| // change. |
| return ConstrainResult::Same; |
| } |
| |
| let item = self.ctx.resolve_item(id); |
| let ty = match item.as_type() { |
| None => return ConstrainResult::Same, |
| Some(ty) => ty, |
| }; |
| |
| match *ty.kind() { |
| TypeKind::TemplateAlias(t, _) | |
| TypeKind::Alias(t) | |
| TypeKind::ResolvedTypeRef(t) => { |
| if self.have_destructor.contains(&t.into()) { |
| self.insert(id) |
| } else { |
| ConstrainResult::Same |
| } |
| } |
| |
| TypeKind::Comp(ref info) => { |
| if info.has_own_destructor() { |
| return self.insert(id); |
| } |
| |
| match info.kind() { |
| CompKind::Union => ConstrainResult::Same, |
| CompKind::Struct => { |
| let base_or_field_destructor = |
| info.base_members().iter().any(|base| { |
| self.have_destructor.contains(&base.ty.into()) |
| }) || info.fields().iter().any( |
| |field| match *field { |
| Field::DataMember(ref data) => self |
| .have_destructor |
| .contains(&data.ty().into()), |
| Field::Bitfields(_) => false, |
| }, |
| ); |
| if base_or_field_destructor { |
| self.insert(id) |
| } else { |
| ConstrainResult::Same |
| } |
| } |
| } |
| } |
| |
| TypeKind::TemplateInstantiation(ref inst) => { |
| let definition_or_arg_destructor = self |
| .have_destructor |
| .contains(&inst.template_definition().into()) || |
| inst.template_arguments().iter().any(|arg| { |
| self.have_destructor.contains(&arg.into()) |
| }); |
| if definition_or_arg_destructor { |
| self.insert(id) |
| } else { |
| ConstrainResult::Same |
| } |
| } |
| |
| _ => 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> From<HasDestructorAnalysis<'ctx>> for HashSet<ItemId> { |
| fn from(analysis: HasDestructorAnalysis<'ctx>) -> Self { |
| analysis.have_destructor |
| } |
| } |