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 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<'ctx> HasDestructorAnalysis<'ctx> {
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 {:?} twice because if it was \
62 already in the set, `constrain` should have exited early.",
63 id
64 );
65 ConstrainResult::Changed
66 }
67}
68
69impl<'ctx> MonotoneFramework for HasDestructorAnalysis<'ctx> {
70 type Node = ItemId;
71 type Extra = &'ctx BindgenContext;
72 type Output = HashSet<ItemId>;
73
74 fn new(ctx: &'ctx BindgenContext) -> Self {
75 let have_destructor = HashSet::default();
76 let dependencies = generate_dependencies(ctx, Self::consider_edge);
77
78 HasDestructorAnalysis {
79 ctx,
80 have_destructor,
81 dependencies,
82 }
83 }
84
85 fn initial_worklist(&self) -> Vec<ItemId> {
86 self.ctx.allowlisted_items().iter().cloned().collect()
87 }
88
89 fn constrain(&mut self, id: ItemId) -> ConstrainResult {
90 if self.have_destructor.contains(&id) {
91 // We've already computed that this type has a destructor and that can't
92 // change.
93 return ConstrainResult::Same;
94 }
95
96 let item = self.ctx.resolve_item(id);
97 let ty = match item.as_type() {
98 None => return ConstrainResult::Same,
99 Some(ty) => ty,
100 };
101
102 match *ty.kind() {
103 TypeKind::TemplateAlias(t, _) |
104 TypeKind::Alias(t) |
105 TypeKind::ResolvedTypeRef(t) => {
106 if self.have_destructor.contains(&t.into()) {
107 self.insert(id)
108 } else {
109 ConstrainResult::Same
110 }
111 }
112
113 TypeKind::Comp(ref info) => {
114 if info.has_own_destructor() {
115 return self.insert(id);
116 }
117
118 match info.kind() {
119 CompKind::Union => ConstrainResult::Same,
120 CompKind::Struct => {
121 let base_or_field_destructor =
122 info.base_members().iter().any(|base| {
123 self.have_destructor.contains(&base.ty.into())
124 }) || info.fields().iter().any(
125 |field| match *field {
126 Field::DataMember(ref data) => self
127 .have_destructor
128 .contains(&data.ty().into()),
129 Field::Bitfields(_) => false,
130 },
131 );
132 if base_or_field_destructor {
133 self.insert(id)
134 } else {
135 ConstrainResult::Same
136 }
137 }
138 }
139 }
140
141 TypeKind::TemplateInstantiation(ref inst) => {
142 let definition_or_arg_destructor = self
143 .have_destructor
144 .contains(&inst.template_definition().into()) ||
145 inst.template_arguments().iter().any(|arg| {
146 self.have_destructor.contains(&arg.into())
147 });
148 if definition_or_arg_destructor {
149 self.insert(id)
150 } else {
151 ConstrainResult::Same
152 }
153 }
154
155 _ => ConstrainResult::Same,
156 }
157 }
158
159 fn each_depending_on<F>(&self, id: ItemId, mut f: F)
160 where
161 F: FnMut(ItemId),
162 {
163 if let Some(edges) = self.dependencies.get(&id) {
164 for item in edges {
165 trace!("enqueue {:?} into worklist", item);
166 f(*item);
167 }
168 }
169 }
170}
171
172impl<'ctx> From<HasDestructorAnalysis<'ctx>> for HashSet<ItemId> {
173 fn from(analysis: HasDestructorAnalysis<'ctx>) -> Self {
174 analysis.have_destructor
175 }
176}
177