1//! Determining the sizedness of types (as base classes and otherwise).
23use 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};
1213/// The result of the `Sizedness` analysis for an individual item.
14///
15/// This is a chain lattice of the form:
16///
17/// ```ignore
18/// NonZeroSized
19/// |
20/// DependsOnTypeParam
21/// |
22/// ZeroSized
23/// ```
24///
25/// We initially assume that all types are `ZeroSized` and then update our
26/// understanding as we learn more about each type.
27#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
28pub(crate) enum SizednessResult {
29/// The type is zero-sized.
30 ///
31 /// This means that if it is a C++ type, and is not being used as a base
32 /// member, then we must add an `_address` byte to enforce the
33 /// unique-address-per-distinct-object-instance rule.
34#[default]
35ZeroSized,
3637/// Whether this type is zero-sized or not depends on whether a type
38 /// parameter is zero-sized or not.
39 ///
40 /// For example, given these definitions:
41 ///
42 /// ```c++
43 /// template<class T>
44 /// class Flongo : public T {};
45 ///
46 /// class Empty {};
47 ///
48 /// class NonEmpty { int x; };
49 /// ```
50 ///
51 /// Then `Flongo<Empty>` is zero-sized, and needs an `_address` byte
52 /// inserted, while `Flongo<NonEmpty>` is *not* zero-sized, and should *not*
53 /// have an `_address` byte inserted.
54 ///
55 /// We don't properly handle this situation correctly right now:
56 /// <https://github.com/rust-lang/rust-bindgen/issues/586>
57DependsOnTypeParam,
5859/// Has some size that is known to be greater than zero. That doesn't mean
60 /// it has a static size, but it is not zero sized for sure. In other words,
61 /// it might contain an incomplete array or some other dynamically sized
62 /// type.
63NonZeroSized,
64}
6566impl SizednessResult {
67/// Take the least upper bound of `self` and `rhs`.
68pub(crate) fn join(self, rhs: Self) -> Self {
69 cmp::max(self, rhs)
70 }
71}
7273impl ops::BitOr for SizednessResult {
74type Output = Self;
7576fn bitor(self, rhs: SizednessResult) -> Self::Output {
77self.join(rhs)
78 }
79}
8081impl ops::BitOrAssign for SizednessResult {
82fn bitor_assign(&mut self, rhs: SizednessResult) {
83*self = self.join(rhs);
84 }
85}
8687/// An analysis that computes the sizedness of all types.
88///
89/// * For types with known sizes -- for example pointers, scalars, etc... --
90/// they are assigned `NonZeroSized`.
91///
92/// * For compound structure types with one or more fields, they are assigned
93/// `NonZeroSized`.
94///
95/// * For compound structure types without any fields, the results of the bases
96/// are `join`ed.
97///
98/// * For type parameters, `DependsOnTypeParam` is assigned.
99#[derive(Debug)]
100pub(crate) struct SizednessAnalysis<'ctx> {
101 ctx: &'ctx BindgenContext,
102 dependencies: HashMap<TypeId, Vec<TypeId>>,
103// Incremental results of the analysis. Missing entries are implicitly
104 // considered `ZeroSized`.
105sized: HashMap<TypeId, SizednessResult>,
106}
107108impl SizednessAnalysis<'_> {
109fn consider_edge(kind: EdgeKind) -> bool {
110// These are the only edges that can affect whether a type is
111 // zero-sized or not.
112matches!(
113 kind,
114 EdgeKind::TemplateArgument |
115 EdgeKind::TemplateParameterDefinition |
116 EdgeKind::TemplateDeclaration |
117 EdgeKind::TypeReference |
118 EdgeKind::BaseMember |
119 EdgeKind::Field
120 )
121 }
122123/// Insert an incremental result, and return whether this updated our
124 /// knowledge of types and we should continue the analysis.
125fn insert(
126&mut self,
127 id: TypeId,
128 result: SizednessResult,
129 ) -> ConstrainResult {
130trace!("inserting {result:?} for {id:?}");
131132if let SizednessResult::ZeroSized = result {
133return ConstrainResult::Same;
134 }
135136match self.sized.entry(id) {
137 Entry::Occupied(mut entry) => {
138if *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 }
151152fn forward(&mut self, from: TypeId, to: TypeId) -> ConstrainResult {
153match self.sized.get(&from) {
154None => ConstrainResult::Same,
155Some(r) => self.insert(to, *r),
156 }
157 }
158}
159160impl<'ctx> MonotoneFramework for SizednessAnalysis<'ctx> {
161type Node = TypeId;
162type Extra = &'ctx BindgenContext;
163type Output = HashMap<TypeId, SizednessResult>;
164165fn new(ctx: &'ctx BindgenContext) -> SizednessAnalysis<'ctx> {
166let 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();
180181let sized = HashMap::default();
182183 SizednessAnalysis {
184 ctx,
185 dependencies,
186 sized,
187 }
188 }
189190fn initial_worklist(&self) -> Vec<TypeId> {
191self.ctx
192 .allowlisted_items()
193 .iter()
194 .filter_map(|id| id.as_type_id(self.ctx))
195 .collect()
196 }
197198fn constrain(&mut self, id: TypeId) -> ConstrainResult {
199trace!("constrain {id:?}");
200201if let Some(SizednessResult::NonZeroSized) = self.sized.get(&id) {
202trace!(" already know it is not zero-sized");
203return ConstrainResult::Same;
204 }
205206if id.has_vtable_ptr(self.ctx) {
207trace!(" has an explicit vtable pointer, therefore is not zero-sized");
208return self.insert(id, SizednessResult::NonZeroSized);
209 }
210211let ty = self.ctx.resolve_type(id);
212213if id.is_opaque(self.ctx, &()) {
214trace!(" type is opaque; checking layout...");
215let result =
216 ty.layout(self.ctx).map_or(SizednessResult::ZeroSized, |l| {
217if l.size == 0 {
218trace!(" ...layout has size == 0");
219 SizednessResult::ZeroSized
220 } else {
221trace!(" ...layout has size > 0");
222 SizednessResult::NonZeroSized
223 }
224 });
225return self.insert(id, result);
226 }
227228match *ty.kind() {
229 TypeKind::Void => {
230trace!(" void is zero-sized");
231self.insert(id, SizednessResult::ZeroSized)
232 }
233234 TypeKind::TypeParam => {
235trace!(
236" type params sizedness depends on what they're \
237 instantiated as"
238);
239self.insert(id, SizednessResult::DependsOnTypeParam)
240 }
241242 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(..) => {
252trace!(" {:?} is known not to be zero-sized", ty.kind());
253self.insert(id, SizednessResult::NonZeroSized)
254 }
255256 TypeKind::ObjCInterface(..) => {
257trace!(" obj-c interfaces always have at least the `isa` pointer");
258self.insert(id, SizednessResult::NonZeroSized)
259 }
260261 TypeKind::TemplateAlias(t, _) |
262 TypeKind::Alias(t) |
263 TypeKind::BlockPointer(t) |
264 TypeKind::ResolvedTypeRef(t) => {
265trace!(" aliases and type refs forward to their inner type");
266self.forward(t, id)
267 }
268269 TypeKind::TemplateInstantiation(ref inst) => {
270trace!(
271" template instantiations are zero-sized if their \
272 definition is zero-sized"
273);
274self.forward(inst.template_definition(), id)
275 }
276277 TypeKind::Array(_, 0) => {
278trace!(" arrays of zero elements are zero-sized");
279self.insert(id, SizednessResult::ZeroSized)
280 }
281 TypeKind::Array(..) => {
282trace!(" arrays of > 0 elements are not zero-sized");
283self.insert(id, SizednessResult::NonZeroSized)
284 }
285 TypeKind::Vector(..) => {
286trace!(" vectors are not zero-sized");
287self.insert(id, SizednessResult::NonZeroSized)
288 }
289290 TypeKind::Comp(ref info) => {
291trace!(" comp considers its own fields and bases");
292293if !info.fields().is_empty() {
294return self.insert(id, SizednessResult::NonZeroSized);
295 }
296297let 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));
302303self.insert(id, result)
304 }
305306 TypeKind::Opaque => {
307unreachable!("covered by the .is_opaque() check above")
308 }
309310 TypeKind::UnresolvedTypeRef(..) => {
311unreachable!("Should have been resolved after parsing!");
312 }
313 }
314 }
315316fn each_depending_on<F>(&self, id: TypeId, mut f: F)
317where
318F: FnMut(TypeId),
319 {
320if let Some(edges) = self.dependencies.get(&id) {
321for ty in edges {
322trace!("enqueue {ty:?} into worklist");
323 f(*ty);
324 }
325 }
326 }
327}
328329impl<'ctx> From<SizednessAnalysis<'ctx>> for HashMap<TypeId, SizednessResult> {
330fn from(analysis: SizednessAnalysis<'ctx>) -> Self {
331// We let the lack of an entry mean "ZeroSized" to save space.
332extra_assert!(analysis
333 .sized
334 .values()
335 .all(|v| { *v != SizednessResult::ZeroSized }));
336337 analysis.sized
338 }
339}
340341/// A convenience trait for querying whether some type or ID is sized.
342///
343/// This is not for _computing_ whether the thing is sized, it is for looking up
344/// the results of the `Sizedness` analysis's computations for a specific thing.
345pub(crate) trait Sizedness {
346/// Get the sizedness of this type.
347fn sizedness(&self, ctx: &BindgenContext) -> SizednessResult;
348349/// Is the sizedness for this type `SizednessResult::ZeroSized`?
350fn is_zero_sized(&self, ctx: &BindgenContext) -> bool {
351self.sizedness(ctx) == SizednessResult::ZeroSized
352 }
353}