bindgen/ir/analysis/
sizedness.rs1use super::{
4 generate_dependencies, ConstrainResult, HasVtable, MonotoneFramework,
5};
6use crate::ir::context::{BindgenContext, TypeId};
7use crate::ir::item::IsOpaque;
8use crate::ir::traversal::EdgeKind;
9use crate::ir::ty::TypeKind;
10use crate::{Entry, HashMap};
11use std::{cmp, ops};
12
13#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
28pub(crate) enum SizednessResult {
29 #[default]
35 ZeroSized,
36
37 DependsOnTypeParam,
58
59 NonZeroSized,
64}
65
66impl SizednessResult {
67 pub(crate) fn join(self, rhs: Self) -> Self {
69 cmp::max(self, rhs)
70 }
71}
72
73impl ops::BitOr for SizednessResult {
74 type Output = Self;
75
76 fn bitor(self, rhs: SizednessResult) -> Self::Output {
77 self.join(rhs)
78 }
79}
80
81impl ops::BitOrAssign for SizednessResult {
82 fn bitor_assign(&mut self, rhs: SizednessResult) {
83 *self = self.join(rhs);
84 }
85}
86
87#[derive(Debug)]
100pub(crate) struct SizednessAnalysis<'ctx> {
101 ctx: &'ctx BindgenContext,
102 dependencies: HashMap<TypeId, Vec<TypeId>>,
103 sized: HashMap<TypeId, SizednessResult>,
106}
107
108impl SizednessAnalysis<'_> {
109 fn consider_edge(kind: EdgeKind) -> bool {
110 matches!(
113 kind,
114 EdgeKind::TemplateArgument |
115 EdgeKind::TemplateParameterDefinition |
116 EdgeKind::TemplateDeclaration |
117 EdgeKind::TypeReference |
118 EdgeKind::BaseMember |
119 EdgeKind::Field
120 )
121 }
122
123 fn insert(
126 &mut self,
127 id: TypeId,
128 result: SizednessResult,
129 ) -> ConstrainResult {
130 trace!("inserting {result:?} for {id:?}");
131
132 if let SizednessResult::ZeroSized = result {
133 return ConstrainResult::Same;
134 }
135
136 match self.sized.entry(id) {
137 Entry::Occupied(mut entry) => {
138 if *entry.get() < result {
139 entry.insert(result);
140 ConstrainResult::Changed
141 } else {
142 ConstrainResult::Same
143 }
144 }
145 Entry::Vacant(entry) => {
146 entry.insert(result);
147 ConstrainResult::Changed
148 }
149 }
150 }
151
152 fn forward(&mut self, from: TypeId, to: TypeId) -> ConstrainResult {
153 match self.sized.get(&from) {
154 None => ConstrainResult::Same,
155 Some(r) => self.insert(to, *r),
156 }
157 }
158}
159
160impl<'ctx> MonotoneFramework for SizednessAnalysis<'ctx> {
161 type Node = TypeId;
162 type Extra = &'ctx BindgenContext;
163 type Output = HashMap<TypeId, SizednessResult>;
164
165 fn new(ctx: &'ctx BindgenContext) -> SizednessAnalysis<'ctx> {
166 let dependencies = generate_dependencies(ctx, Self::consider_edge)
167 .into_iter()
168 .filter_map(|(id, sub_ids)| {
169 id.as_type_id(ctx).map(|id| {
170 (
171 id,
172 sub_ids
173 .into_iter()
174 .filter_map(|s| s.as_type_id(ctx))
175 .collect::<Vec<_>>(),
176 )
177 })
178 })
179 .collect();
180
181 let sized = HashMap::default();
182
183 SizednessAnalysis {
184 ctx,
185 dependencies,
186 sized,
187 }
188 }
189
190 fn initial_worklist(&self) -> Vec<TypeId> {
191 self.ctx
192 .allowlisted_items()
193 .iter()
194 .filter_map(|id| id.as_type_id(self.ctx))
195 .collect()
196 }
197
198 fn constrain(&mut self, id: TypeId) -> ConstrainResult {
199 trace!("constrain {id:?}");
200
201 if let Some(SizednessResult::NonZeroSized) = self.sized.get(&id) {
202 trace!(" already know it is not zero-sized");
203 return ConstrainResult::Same;
204 }
205
206 if id.has_vtable_ptr(self.ctx) {
207 trace!(" has an explicit vtable pointer, therefore is not zero-sized");
208 return self.insert(id, SizednessResult::NonZeroSized);
209 }
210
211 let ty = self.ctx.resolve_type(id);
212
213 if id.is_opaque(self.ctx, &()) {
214 trace!(" type is opaque; checking layout...");
215 let result =
216 ty.layout(self.ctx).map_or(SizednessResult::ZeroSized, |l| {
217 if l.size == 0 {
218 trace!(" ...layout has size == 0");
219 SizednessResult::ZeroSized
220 } else {
221 trace!(" ...layout has size > 0");
222 SizednessResult::NonZeroSized
223 }
224 });
225 return self.insert(id, result);
226 }
227
228 match *ty.kind() {
229 TypeKind::Void => {
230 trace!(" void is zero-sized");
231 self.insert(id, SizednessResult::ZeroSized)
232 }
233
234 TypeKind::TypeParam => {
235 trace!(
236 " type params sizedness depends on what they're \
237 instantiated as"
238 );
239 self.insert(id, SizednessResult::DependsOnTypeParam)
240 }
241
242 TypeKind::Int(..) |
243 TypeKind::Float(..) |
244 TypeKind::Complex(..) |
245 TypeKind::Function(..) |
246 TypeKind::Enum(..) |
247 TypeKind::Reference(..) |
248 TypeKind::NullPtr |
249 TypeKind::ObjCId |
250 TypeKind::ObjCSel |
251 TypeKind::Pointer(..) => {
252 trace!(" {:?} is known not to be zero-sized", ty.kind());
253 self.insert(id, SizednessResult::NonZeroSized)
254 }
255
256 TypeKind::ObjCInterface(..) => {
257 trace!(" obj-c interfaces always have at least the `isa` pointer");
258 self.insert(id, SizednessResult::NonZeroSized)
259 }
260
261 TypeKind::TemplateAlias(t, _) |
262 TypeKind::Alias(t) |
263 TypeKind::BlockPointer(t) |
264 TypeKind::ResolvedTypeRef(t) => {
265 trace!(" aliases and type refs forward to their inner type");
266 self.forward(t, id)
267 }
268
269 TypeKind::TemplateInstantiation(ref inst) => {
270 trace!(
271 " template instantiations are zero-sized if their \
272 definition is zero-sized"
273 );
274 self.forward(inst.template_definition(), id)
275 }
276
277 TypeKind::Array(_, 0) => {
278 trace!(" arrays of zero elements are zero-sized");
279 self.insert(id, SizednessResult::ZeroSized)
280 }
281 TypeKind::Array(..) => {
282 trace!(" arrays of > 0 elements are not zero-sized");
283 self.insert(id, SizednessResult::NonZeroSized)
284 }
285 TypeKind::Vector(..) => {
286 trace!(" vectors are not zero-sized");
287 self.insert(id, SizednessResult::NonZeroSized)
288 }
289
290 TypeKind::Comp(ref info) => {
291 trace!(" comp considers its own fields and bases");
292
293 if !info.fields().is_empty() {
294 return self.insert(id, SizednessResult::NonZeroSized);
295 }
296
297 let result = info
298 .base_members()
299 .iter()
300 .filter_map(|base| self.sized.get(&base.ty))
301 .fold(SizednessResult::ZeroSized, |a, b| a.join(*b));
302
303 self.insert(id, result)
304 }
305
306 TypeKind::Opaque => {
307 unreachable!("covered by the .is_opaque() check above")
308 }
309
310 TypeKind::UnresolvedTypeRef(..) => {
311 unreachable!("Should have been resolved after parsing!");
312 }
313 }
314 }
315
316 fn each_depending_on<F>(&self, id: TypeId, mut f: F)
317 where
318 F: FnMut(TypeId),
319 {
320 if let Some(edges) = self.dependencies.get(&id) {
321 for ty in edges {
322 trace!("enqueue {ty:?} into worklist");
323 f(*ty);
324 }
325 }
326 }
327}
328
329impl<'ctx> From<SizednessAnalysis<'ctx>> for HashMap<TypeId, SizednessResult> {
330 fn from(analysis: SizednessAnalysis<'ctx>) -> Self {
331 extra_assert!(analysis
333 .sized
334 .values()
335 .all(|v| { *v != SizednessResult::ZeroSized }));
336
337 analysis.sized
338 }
339}
340
341pub(crate) trait Sizedness {
346 fn sizedness(&self, ctx: &BindgenContext) -> SizednessResult;
348
349 fn is_zero_sized(&self, ctx: &BindgenContext) -> bool {
351 self.sizedness(ctx) == SizednessResult::ZeroSized
352 }
353}