bindgen/ir/analysis/
has_destructor.rs1use 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#[derive(Debug, Clone)]
25pub(crate) struct HasDestructorAnalysis<'ctx> {
26 ctx: &'ctx BindgenContext,
27
28 have_destructor: HashSet<ItemId>,
31
32 dependencies: HashMap<ItemId, Vec<ItemId>>,
40}
41
42impl HasDestructorAnalysis<'_> {
43 fn consider_edge(kind: EdgeKind) -> bool {
44 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 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}