bindgen/ir/analysis/
has_destructor.rs

1//! Determining which types have destructors
2
3use super::{generate_dependencies, ConstrainResult, MonotoneFramework};
4use crate::ir::comp::{CompKind, Field, FieldMethods};
5use crate::ir::context::{BindgenContext, ItemId};
6use crate::ir::traversal::EdgeKind;
7use crate::ir::ty::TypeKind;
8use crate::{HashMap, HashSet};
9
10/// An analysis that finds for each IR item whether it has a destructor or not
11///
12/// We use the monotone function `has destructor`, defined as follows:
13///
14/// * If T is a type alias, a templated alias, or an indirection to another type,
15///   T has a destructor if the type T refers to has a destructor.
16/// * If T is a compound type, T has a destructor if we saw a destructor when parsing it,
17///   or if it's a struct, T has a destructor if any of its base members has a destructor,
18///   or if any of its fields have a destructor.
19/// * If T is an instantiation of an abstract template definition, T has
20///   a destructor if its template definition has a destructor,
21///   or if any of the template arguments has a destructor.
22/// * If T is the type of a field, that field has a destructor if it's not a bitfield,
23///   and if T has a destructor.
24#[derive(Debug, Clone)]
25pub(crate) struct HasDestructorAnalysis<'ctx> {
26    ctx: &'ctx BindgenContext,
27
28    // The incremental result of this analysis's computation. Everything in this
29    // set definitely has a destructor.
30    have_destructor: HashSet<ItemId>,
31
32    // Dependencies saying that if a key ItemId has been inserted into the
33    // `have_destructor` set, then each of the ids in Vec<ItemId> need to be
34    // considered again.
35    //
36    // This is a subset of the natural IR graph with reversed edges, where we
37    // only include the edges from the IR graph that can affect whether a type
38    // has a destructor or not.
39    dependencies: HashMap<ItemId, Vec<ItemId>>,
40}
41
42impl HasDestructorAnalysis<'_> {
43    fn consider_edge(kind: EdgeKind) -> bool {
44        // These are the only edges that can affect whether a type has a
45        // destructor or not.
46        matches!(
47            kind,
48            EdgeKind::TypeReference |
49                EdgeKind::BaseMember |
50                EdgeKind::Field |
51                EdgeKind::TemplateArgument |
52                EdgeKind::TemplateDeclaration
53        )
54    }
55
56    fn insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult {
57        let id = id.into();
58        let was_not_already_in_set = self.have_destructor.insert(id);
59        assert!(
60            was_not_already_in_set,
61            "We shouldn't try and insert {id:?} twice because if it was \
62             already in the set, `constrain` should have exited early."
63        );
64        ConstrainResult::Changed
65    }
66}
67
68impl<'ctx> MonotoneFramework for HasDestructorAnalysis<'ctx> {
69    type Node = ItemId;
70    type Extra = &'ctx BindgenContext;
71    type Output = HashSet<ItemId>;
72
73    fn new(ctx: &'ctx BindgenContext) -> Self {
74        let have_destructor = HashSet::default();
75        let dependencies = generate_dependencies(ctx, Self::consider_edge);
76
77        HasDestructorAnalysis {
78            ctx,
79            have_destructor,
80            dependencies,
81        }
82    }
83
84    fn initial_worklist(&self) -> Vec<ItemId> {
85        self.ctx.allowlisted_items().iter().copied().collect()
86    }
87
88    fn constrain(&mut self, id: ItemId) -> ConstrainResult {
89        if self.have_destructor.contains(&id) {
90            // We've already computed that this type has a destructor and that can't
91            // change.
92            return ConstrainResult::Same;
93        }
94
95        let item = self.ctx.resolve_item(id);
96        let ty = match item.as_type() {
97            None => return ConstrainResult::Same,
98            Some(ty) => ty,
99        };
100
101        match *ty.kind() {
102            TypeKind::TemplateAlias(t, _) |
103            TypeKind::Alias(t) |
104            TypeKind::ResolvedTypeRef(t) => {
105                if self.have_destructor.contains(&t.into()) {
106                    self.insert(id)
107                } else {
108                    ConstrainResult::Same
109                }
110            }
111
112            TypeKind::Comp(ref info) => {
113                if info.has_own_destructor() {
114                    return self.insert(id);
115                }
116
117                match info.kind() {
118                    CompKind::Union => ConstrainResult::Same,
119                    CompKind::Struct => {
120                        let base_or_field_destructor =
121                            info.base_members().iter().any(|base| {
122                                self.have_destructor.contains(&base.ty.into())
123                            }) || info.fields().iter().any(
124                                |field| match *field {
125                                    Field::DataMember(ref data) => self
126                                        .have_destructor
127                                        .contains(&data.ty().into()),
128                                    Field::Bitfields(_) => false,
129                                },
130                            );
131                        if base_or_field_destructor {
132                            self.insert(id)
133                        } else {
134                            ConstrainResult::Same
135                        }
136                    }
137                }
138            }
139
140            TypeKind::TemplateInstantiation(ref inst) => {
141                let definition_or_arg_destructor = self
142                    .have_destructor
143                    .contains(&inst.template_definition().into()) ||
144                    inst.template_arguments().iter().any(|arg| {
145                        self.have_destructor.contains(&arg.into())
146                    });
147                if definition_or_arg_destructor {
148                    self.insert(id)
149                } else {
150                    ConstrainResult::Same
151                }
152            }
153
154            _ => ConstrainResult::Same,
155        }
156    }
157
158    fn each_depending_on<F>(&self, id: ItemId, mut f: F)
159    where
160        F: FnMut(ItemId),
161    {
162        if let Some(edges) = self.dependencies.get(&id) {
163            for item in edges {
164                trace!("enqueue {item:?} into worklist");
165                f(*item);
166            }
167        }
168    }
169}
170
171impl<'ctx> From<HasDestructorAnalysis<'ctx>> for HashSet<ItemId> {
172    fn from(analysis: HasDestructorAnalysis<'ctx>) -> Self {
173        analysis.have_destructor
174    }
175}