1 | //! Traversal of the graph of IR items and types. |
2 | |
3 | use super::context::{BindgenContext, ItemId}; |
4 | use super::item::ItemSet; |
5 | use std::collections::{BTreeMap, VecDeque}; |
6 | |
7 | /// An outgoing edge in the IR graph is a reference from some item to another |
8 | /// item: |
9 | /// |
10 | /// from --> to |
11 | /// |
12 | /// The `from` is left implicit: it is the concrete `Trace` implementer which |
13 | /// yielded this outgoing edge. |
14 | #[derive (Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
15 | pub(crate) struct Edge { |
16 | to: ItemId, |
17 | kind: EdgeKind, |
18 | } |
19 | |
20 | impl Edge { |
21 | /// Construct a new edge whose referent is `to` and is of the given `kind`. |
22 | pub(crate) fn new(to: ItemId, kind: EdgeKind) -> Edge { |
23 | Edge { to, kind } |
24 | } |
25 | } |
26 | |
27 | impl From<Edge> for ItemId { |
28 | fn from(val: Edge) -> Self { |
29 | val.to |
30 | } |
31 | } |
32 | |
33 | /// The kind of edge reference. This is useful when we wish to only consider |
34 | /// certain kinds of edges for a particular traversal or analysis. |
35 | #[derive (Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
36 | pub(crate) enum EdgeKind { |
37 | /// A generic, catch-all edge. |
38 | Generic, |
39 | |
40 | /// An edge from a template declaration, to the definition of a named type |
41 | /// parameter. For example, the edge from `Foo<T>` to `T` in the following |
42 | /// snippet: |
43 | /// |
44 | /// ```C++ |
45 | /// template<typename T> |
46 | /// class Foo { }; |
47 | /// ``` |
48 | TemplateParameterDefinition, |
49 | |
50 | /// An edge from a template instantiation to the template declaration that |
51 | /// is being instantiated. For example, the edge from `Foo<int>` to |
52 | /// to `Foo<T>`: |
53 | /// |
54 | /// ```C++ |
55 | /// template<typename T> |
56 | /// class Foo { }; |
57 | /// |
58 | /// using Bar = Foo<ant>; |
59 | /// ``` |
60 | TemplateDeclaration, |
61 | |
62 | /// An edge from a template instantiation to its template argument. For |
63 | /// example, `Foo<Bar>` to `Bar`: |
64 | /// |
65 | /// ```C++ |
66 | /// template<typename T> |
67 | /// class Foo { }; |
68 | /// |
69 | /// class Bar { }; |
70 | /// |
71 | /// using FooBar = Foo<Bar>; |
72 | /// ``` |
73 | TemplateArgument, |
74 | |
75 | /// An edge from a compound type to one of its base member types. For |
76 | /// example, the edge from `Bar` to `Foo`: |
77 | /// |
78 | /// ```C++ |
79 | /// class Foo { }; |
80 | /// |
81 | /// class Bar : public Foo { }; |
82 | /// ``` |
83 | BaseMember, |
84 | |
85 | /// An edge from a compound type to the types of one of its fields. For |
86 | /// example, the edge from `Foo` to `int`: |
87 | /// |
88 | /// ```C++ |
89 | /// class Foo { |
90 | /// int x; |
91 | /// }; |
92 | /// ``` |
93 | Field, |
94 | |
95 | /// An edge from an class or struct type to an inner type member. For |
96 | /// example, the edge from `Foo` to `Foo::Bar` here: |
97 | /// |
98 | /// ```C++ |
99 | /// class Foo { |
100 | /// struct Bar { }; |
101 | /// }; |
102 | /// ``` |
103 | InnerType, |
104 | |
105 | /// An edge from an class or struct type to an inner static variable. For |
106 | /// example, the edge from `Foo` to `Foo::BAR` here: |
107 | /// |
108 | /// ```C++ |
109 | /// class Foo { |
110 | /// static const char* BAR; |
111 | /// }; |
112 | /// ``` |
113 | InnerVar, |
114 | |
115 | /// An edge from a class or struct type to one of its method functions. For |
116 | /// example, the edge from `Foo` to `Foo::bar`: |
117 | /// |
118 | /// ```C++ |
119 | /// class Foo { |
120 | /// bool bar(int x, int y); |
121 | /// }; |
122 | /// ``` |
123 | Method, |
124 | |
125 | /// An edge from a class or struct type to one of its constructor |
126 | /// functions. For example, the edge from `Foo` to `Foo::Foo(int x, int y)`: |
127 | /// |
128 | /// ```C++ |
129 | /// class Foo { |
130 | /// int my_x; |
131 | /// int my_y; |
132 | /// |
133 | /// public: |
134 | /// Foo(int x, int y); |
135 | /// }; |
136 | /// ``` |
137 | Constructor, |
138 | |
139 | /// An edge from a class or struct type to its destructor function. For |
140 | /// example, the edge from `Doggo` to `Doggo::~Doggo()`: |
141 | /// |
142 | /// ```C++ |
143 | /// struct Doggo { |
144 | /// char* wow; |
145 | /// |
146 | /// public: |
147 | /// ~Doggo(); |
148 | /// }; |
149 | /// ``` |
150 | Destructor, |
151 | |
152 | /// An edge from a function declaration to its return type. For example, the |
153 | /// edge from `foo` to `int`: |
154 | /// |
155 | /// ```C++ |
156 | /// int foo(char* string); |
157 | /// ``` |
158 | FunctionReturn, |
159 | |
160 | /// An edge from a function declaration to one of its parameter types. For |
161 | /// example, the edge from `foo` to `char*`: |
162 | /// |
163 | /// ```C++ |
164 | /// int foo(char* string); |
165 | /// ``` |
166 | FunctionParameter, |
167 | |
168 | /// An edge from a static variable to its type. For example, the edge from |
169 | /// `FOO` to `const char*`: |
170 | /// |
171 | /// ```C++ |
172 | /// static const char* FOO; |
173 | /// ``` |
174 | VarType, |
175 | |
176 | /// An edge from a non-templated alias or typedef to the referenced type. |
177 | TypeReference, |
178 | } |
179 | |
180 | /// A predicate to allow visiting only sub-sets of the whole IR graph by |
181 | /// excluding certain edges from being followed by the traversal. |
182 | /// |
183 | /// The predicate must return true if the traversal should follow this edge |
184 | /// and visit everything that is reachable through it. |
185 | pub(crate) type TraversalPredicate = |
186 | for<'a> fn(&'a BindgenContext, Edge) -> bool; |
187 | |
188 | /// A `TraversalPredicate` implementation that follows all edges, and therefore |
189 | /// traversals using this predicate will see the whole IR graph reachable from |
190 | /// the traversal's roots. |
191 | pub(crate) fn all_edges(_: &BindgenContext, _: Edge) -> bool { |
192 | true |
193 | } |
194 | |
195 | /// A `TraversalPredicate` implementation that only follows |
196 | /// `EdgeKind::InnerType` edges, and therefore traversals using this predicate |
197 | /// will only visit the traversal's roots and their inner types. This is used |
198 | /// in no-recursive-allowlist mode, where inner types such as anonymous |
199 | /// structs/unions still need to be processed. |
200 | pub(crate) fn only_inner_type_edges(_: &BindgenContext, edge: Edge) -> bool { |
201 | edge.kind == EdgeKind::InnerType |
202 | } |
203 | |
204 | /// A `TraversalPredicate` implementation that only follows edges to items that |
205 | /// are enabled for code generation. This lets us skip considering items for |
206 | /// which are not reachable from code generation. |
207 | pub(crate) fn codegen_edges(ctx: &BindgenContext, edge: Edge) -> bool { |
208 | let cc = &ctx.options().codegen_config; |
209 | match edge.kind { |
210 | EdgeKind::Generic => { |
211 | ctx.resolve_item(edge.to).is_enabled_for_codegen(ctx) |
212 | } |
213 | |
214 | // We statically know the kind of item that non-generic edges can point |
215 | // to, so we don't need to actually resolve the item and check |
216 | // `Item::is_enabled_for_codegen`. |
217 | EdgeKind::TemplateParameterDefinition | |
218 | EdgeKind::TemplateArgument | |
219 | EdgeKind::TemplateDeclaration | |
220 | EdgeKind::BaseMember | |
221 | EdgeKind::Field | |
222 | EdgeKind::InnerType | |
223 | EdgeKind::FunctionReturn | |
224 | EdgeKind::FunctionParameter | |
225 | EdgeKind::VarType | |
226 | EdgeKind::TypeReference => cc.types(), |
227 | EdgeKind::InnerVar => cc.vars(), |
228 | EdgeKind::Method => cc.methods(), |
229 | EdgeKind::Constructor => cc.constructors(), |
230 | EdgeKind::Destructor => cc.destructors(), |
231 | } |
232 | } |
233 | |
234 | /// The storage for the set of items that have been seen (although their |
235 | /// outgoing edges might not have been fully traversed yet) in an active |
236 | /// traversal. |
237 | pub(crate) trait TraversalStorage<'ctx> { |
238 | /// Construct a new instance of this TraversalStorage, for a new traversal. |
239 | fn new(ctx: &'ctx BindgenContext) -> Self; |
240 | |
241 | /// Add the given item to the storage. If the item has never been seen |
242 | /// before, return `true`. Otherwise, return `false`. |
243 | /// |
244 | /// The `from` item is the item from which we discovered this item, or is |
245 | /// `None` if this item is a root. |
246 | fn add(&mut self, from: Option<ItemId>, item: ItemId) -> bool; |
247 | } |
248 | |
249 | impl<'ctx> TraversalStorage<'ctx> for ItemSet { |
250 | fn new(_: &'ctx BindgenContext) -> Self { |
251 | ItemSet::new() |
252 | } |
253 | |
254 | fn add(&mut self, _: Option<ItemId>, item: ItemId) -> bool { |
255 | self.insert(item) |
256 | } |
257 | } |
258 | |
259 | /// A `TraversalStorage` implementation that keeps track of how we first reached |
260 | /// each item. This is useful for providing debug assertions with meaningful |
261 | /// diagnostic messages about dangling items. |
262 | #[derive (Debug)] |
263 | pub(crate) struct Paths<'ctx>(BTreeMap<ItemId, ItemId>, &'ctx BindgenContext); |
264 | |
265 | impl<'ctx> TraversalStorage<'ctx> for Paths<'ctx> { |
266 | fn new(ctx: &'ctx BindgenContext) -> Self { |
267 | Paths(BTreeMap::new(), ctx) |
268 | } |
269 | |
270 | fn add(&mut self, from: Option<ItemId>, item: ItemId) -> bool { |
271 | let newly_discovered = |
272 | self.0.insert(item, from.unwrap_or(item)).is_none(); |
273 | |
274 | if self.1.resolve_item_fallible(item).is_none() { |
275 | let mut path = vec![]; |
276 | let mut current = item; |
277 | loop { |
278 | let predecessor = *self.0.get(¤t).expect( |
279 | "We know we found this item id, so it must have a \ |
280 | predecessor" , |
281 | ); |
282 | if predecessor == current { |
283 | break; |
284 | } |
285 | path.push(predecessor); |
286 | current = predecessor; |
287 | } |
288 | path.reverse(); |
289 | panic!( |
290 | "Found reference to dangling id = {:?}\nvia path = {:?}" , |
291 | item, path |
292 | ); |
293 | } |
294 | |
295 | newly_discovered |
296 | } |
297 | } |
298 | |
299 | /// The queue of seen-but-not-yet-traversed items. |
300 | /// |
301 | /// Using a FIFO queue with a traversal will yield a breadth-first traversal, |
302 | /// while using a LIFO queue will result in a depth-first traversal of the IR |
303 | /// graph. |
304 | pub(crate) trait TraversalQueue: Default { |
305 | /// Add a newly discovered item to the queue. |
306 | fn push(&mut self, item: ItemId); |
307 | |
308 | /// Pop the next item to traverse, if any. |
309 | fn next(&mut self) -> Option<ItemId>; |
310 | } |
311 | |
312 | impl TraversalQueue for Vec<ItemId> { |
313 | fn push(&mut self, item: ItemId) { |
314 | self.push(item); |
315 | } |
316 | |
317 | fn next(&mut self) -> Option<ItemId> { |
318 | self.pop() |
319 | } |
320 | } |
321 | |
322 | impl TraversalQueue for VecDeque<ItemId> { |
323 | fn push(&mut self, item: ItemId) { |
324 | self.push_back(item); |
325 | } |
326 | |
327 | fn next(&mut self) -> Option<ItemId> { |
328 | self.pop_front() |
329 | } |
330 | } |
331 | |
332 | /// Something that can receive edges from a `Trace` implementation. |
333 | pub(crate) trait Tracer { |
334 | /// Note an edge between items. Called from within a `Trace` implementation. |
335 | fn visit_kind(&mut self, item: ItemId, kind: EdgeKind); |
336 | |
337 | /// A synonym for `tracer.visit_kind(item, EdgeKind::Generic)`. |
338 | fn visit(&mut self, item: ItemId) { |
339 | self.visit_kind(item, kind:EdgeKind::Generic); |
340 | } |
341 | } |
342 | |
343 | impl<F> Tracer for F |
344 | where |
345 | F: FnMut(ItemId, EdgeKind), |
346 | { |
347 | fn visit_kind(&mut self, item: ItemId, kind: EdgeKind) { |
348 | (*self)(item, kind) |
349 | } |
350 | } |
351 | |
352 | /// Trace all of the outgoing edges to other items. Implementations should call |
353 | /// one of `tracer.visit(edge)` or `tracer.visit_kind(edge, EdgeKind::Whatever)` |
354 | /// for each of their outgoing edges. |
355 | pub(crate) trait Trace { |
356 | /// If a particular type needs extra information beyond what it has in |
357 | /// `self` and `context` to find its referenced items, its implementation |
358 | /// can define this associated type, forcing callers to pass the needed |
359 | /// information through. |
360 | type Extra; |
361 | |
362 | /// Trace all of this item's outgoing edges to other items. |
363 | fn trace<T>( |
364 | &self, |
365 | context: &BindgenContext, |
366 | tracer: &mut T, |
367 | extra: &Self::Extra, |
368 | ) where |
369 | T: Tracer; |
370 | } |
371 | |
372 | /// An graph traversal of the transitive closure of references between items. |
373 | /// |
374 | /// See `BindgenContext::allowlisted_items` for more information. |
375 | pub(crate) struct ItemTraversal<'ctx, Storage, Queue> |
376 | where |
377 | Storage: TraversalStorage<'ctx>, |
378 | Queue: TraversalQueue, |
379 | { |
380 | ctx: &'ctx BindgenContext, |
381 | |
382 | /// The set of items we have seen thus far in this traversal. |
383 | seen: Storage, |
384 | |
385 | /// The set of items that we have seen, but have yet to traverse. |
386 | queue: Queue, |
387 | |
388 | /// The predicate that determines which edges this traversal will follow. |
389 | predicate: TraversalPredicate, |
390 | |
391 | /// The item we are currently traversing. |
392 | currently_traversing: Option<ItemId>, |
393 | } |
394 | |
395 | impl<'ctx, Storage, Queue> ItemTraversal<'ctx, Storage, Queue> |
396 | where |
397 | Storage: TraversalStorage<'ctx>, |
398 | Queue: TraversalQueue, |
399 | { |
400 | /// Begin a new traversal, starting from the given roots. |
401 | pub(crate) fn new<R>( |
402 | ctx: &'ctx BindgenContext, |
403 | roots: R, |
404 | predicate: TraversalPredicate, |
405 | ) -> ItemTraversal<'ctx, Storage, Queue> |
406 | where |
407 | R: IntoIterator<Item = ItemId>, |
408 | { |
409 | let mut seen = Storage::new(ctx); |
410 | let mut queue = Queue::default(); |
411 | |
412 | for id in roots { |
413 | seen.add(None, id); |
414 | queue.push(id); |
415 | } |
416 | |
417 | ItemTraversal { |
418 | ctx, |
419 | seen, |
420 | queue, |
421 | predicate, |
422 | currently_traversing: None, |
423 | } |
424 | } |
425 | } |
426 | |
427 | impl<'ctx, Storage, Queue> Tracer for ItemTraversal<'ctx, Storage, Queue> |
428 | where |
429 | Storage: TraversalStorage<'ctx>, |
430 | Queue: TraversalQueue, |
431 | { |
432 | fn visit_kind(&mut self, item: ItemId, kind: EdgeKind) { |
433 | let edge: Edge = Edge::new(to:item, kind); |
434 | if !(self.predicate)(self.ctx, edge) { |
435 | return; |
436 | } |
437 | |
438 | let is_newly_discovered: bool = |
439 | self.seen.add(self.currently_traversing, item); |
440 | if is_newly_discovered { |
441 | self.queue.push(item) |
442 | } |
443 | } |
444 | } |
445 | |
446 | impl<'ctx, Storage, Queue> Iterator for ItemTraversal<'ctx, Storage, Queue> |
447 | where |
448 | Storage: TraversalStorage<'ctx>, |
449 | Queue: TraversalQueue, |
450 | { |
451 | type Item = ItemId; |
452 | |
453 | fn next(&mut self) -> Option<Self::Item> { |
454 | let id: ItemId = self.queue.next()?; |
455 | |
456 | let newly_discovered: bool = self.seen.add(from:None, item:id); |
457 | debug_assert!( |
458 | !newly_discovered, |
459 | "should have already seen anything we get out of our queue" |
460 | ); |
461 | debug_assert!( |
462 | self.ctx.resolve_item_fallible(id).is_some(), |
463 | "should only get IDs of actual items in our context during traversal" |
464 | ); |
465 | |
466 | self.currently_traversing = Some(id); |
467 | id.trace(self.ctx, self, &()); |
468 | self.currently_traversing = None; |
469 | |
470 | Some(id) |
471 | } |
472 | } |
473 | |
474 | /// An iterator to find any dangling items. |
475 | /// |
476 | /// See `BindgenContext::assert_no_dangling_item_traversal` for more |
477 | /// information. |
478 | pub(crate) type AssertNoDanglingItemsTraversal<'ctx> = |
479 | ItemTraversal<'ctx, Paths<'ctx>, VecDeque<ItemId>>; |
480 | |