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 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`.
new(to: ItemId, kind: EdgeKind) -> Edge22 pub fn new(to: ItemId, kind: EdgeKind) -> Edge {
23 Edge { to, kind }
24 }
25 }
26
27 impl Into<ItemId> for Edge {
into(self) -> ItemId28 fn into(self) -> ItemId {
29 self.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 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 pub trait TraversalPredicate {
183 /// Should the traversal follow this edge, and visit everything that is
184 /// reachable through it?
should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool185 fn should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool;
186 }
187
188 impl TraversalPredicate for for<'a> fn(&'a BindgenContext, Edge) -> bool {
should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool189 fn should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool {
190 (*self)(ctx, edge)
191 }
192 }
193
194 /// A `TraversalPredicate` implementation that follows all edges, and therefore
195 /// traversals using this predicate will see the whole IR graph reachable from
196 /// the traversal's roots.
all_edges(_: &BindgenContext, _: Edge) -> bool197 pub fn all_edges(_: &BindgenContext, _: Edge) -> bool {
198 true
199 }
200
201 /// A `TraversalPredicate` implementation that only follows
202 /// `EdgeKind::InnerType` edges, and therefore traversals using this predicate
203 /// will only visit the traversal's roots and their inner types. This is used
204 /// in no-recursive-allowlist mode, where inner types such as anonymous
205 /// structs/unions still need to be processed.
only_inner_type_edges(_: &BindgenContext, edge: Edge) -> bool206 pub fn only_inner_type_edges(_: &BindgenContext, edge: Edge) -> bool {
207 edge.kind == EdgeKind::InnerType
208 }
209
210 /// A `TraversalPredicate` implementation that only follows edges to items that
211 /// are enabled for code generation. This lets us skip considering items for
212 /// which are not reachable from code generation.
codegen_edges(ctx: &BindgenContext, edge: Edge) -> bool213 pub fn codegen_edges(ctx: &BindgenContext, edge: Edge) -> bool {
214 let cc = &ctx.options().codegen_config;
215 match edge.kind {
216 EdgeKind::Generic => {
217 ctx.resolve_item(edge.to).is_enabled_for_codegen(ctx)
218 }
219
220 // We statically know the kind of item that non-generic edges can point
221 // to, so we don't need to actually resolve the item and check
222 // `Item::is_enabled_for_codegen`.
223 EdgeKind::TemplateParameterDefinition |
224 EdgeKind::TemplateArgument |
225 EdgeKind::TemplateDeclaration |
226 EdgeKind::BaseMember |
227 EdgeKind::Field |
228 EdgeKind::InnerType |
229 EdgeKind::FunctionReturn |
230 EdgeKind::FunctionParameter |
231 EdgeKind::VarType |
232 EdgeKind::TypeReference => cc.types(),
233 EdgeKind::InnerVar => cc.vars(),
234 EdgeKind::Method => cc.methods(),
235 EdgeKind::Constructor => cc.constructors(),
236 EdgeKind::Destructor => cc.destructors(),
237 }
238 }
239
240 /// The storage for the set of items that have been seen (although their
241 /// outgoing edges might not have been fully traversed yet) in an active
242 /// traversal.
243 pub trait TraversalStorage<'ctx> {
244 /// Construct a new instance of this TraversalStorage, for a new traversal.
new(ctx: &'ctx BindgenContext) -> Self245 fn new(ctx: &'ctx BindgenContext) -> Self;
246
247 /// Add the given item to the storage. If the item has never been seen
248 /// before, return `true`. Otherwise, return `false`.
249 ///
250 /// The `from` item is the item from which we discovered this item, or is
251 /// `None` if this item is a root.
add(&mut self, from: Option<ItemId>, item: ItemId) -> bool252 fn add(&mut self, from: Option<ItemId>, item: ItemId) -> bool;
253 }
254
255 impl<'ctx> TraversalStorage<'ctx> for ItemSet {
new(_: &'ctx BindgenContext) -> Self256 fn new(_: &'ctx BindgenContext) -> Self {
257 ItemSet::new()
258 }
259
add(&mut self, _: Option<ItemId>, item: ItemId) -> bool260 fn add(&mut self, _: Option<ItemId>, item: ItemId) -> bool {
261 self.insert(item)
262 }
263 }
264
265 /// A `TraversalStorage` implementation that keeps track of how we first reached
266 /// each item. This is useful for providing debug assertions with meaningful
267 /// diagnostic messages about dangling items.
268 #[derive(Debug)]
269 pub struct Paths<'ctx>(BTreeMap<ItemId, ItemId>, &'ctx BindgenContext);
270
271 impl<'ctx> TraversalStorage<'ctx> for Paths<'ctx> {
new(ctx: &'ctx BindgenContext) -> Self272 fn new(ctx: &'ctx BindgenContext) -> Self {
273 Paths(BTreeMap::new(), ctx)
274 }
275
add(&mut self, from: Option<ItemId>, item: ItemId) -> bool276 fn add(&mut self, from: Option<ItemId>, item: ItemId) -> bool {
277 let newly_discovered =
278 self.0.insert(item, from.unwrap_or(item)).is_none();
279
280 if self.1.resolve_item_fallible(item).is_none() {
281 let mut path = vec![];
282 let mut current = item;
283 loop {
284 let predecessor = *self.0.get(¤t).expect(
285 "We know we found this item id, so it must have a \
286 predecessor",
287 );
288 if predecessor == current {
289 break;
290 }
291 path.push(predecessor);
292 current = predecessor;
293 }
294 path.reverse();
295 panic!(
296 "Found reference to dangling id = {:?}\nvia path = {:?}",
297 item, path
298 );
299 }
300
301 newly_discovered
302 }
303 }
304
305 /// The queue of seen-but-not-yet-traversed items.
306 ///
307 /// Using a FIFO queue with a traversal will yield a breadth-first traversal,
308 /// while using a LIFO queue will result in a depth-first traversal of the IR
309 /// graph.
310 pub trait TraversalQueue: Default {
311 /// Add a newly discovered item to the queue.
push(&mut self, item: ItemId)312 fn push(&mut self, item: ItemId);
313
314 /// Pop the next item to traverse, if any.
next(&mut self) -> Option<ItemId>315 fn next(&mut self) -> Option<ItemId>;
316 }
317
318 impl TraversalQueue for Vec<ItemId> {
push(&mut self, item: ItemId)319 fn push(&mut self, item: ItemId) {
320 self.push(item);
321 }
322
next(&mut self) -> Option<ItemId>323 fn next(&mut self) -> Option<ItemId> {
324 self.pop()
325 }
326 }
327
328 impl TraversalQueue for VecDeque<ItemId> {
push(&mut self, item: ItemId)329 fn push(&mut self, item: ItemId) {
330 self.push_back(item);
331 }
332
next(&mut self) -> Option<ItemId>333 fn next(&mut self) -> Option<ItemId> {
334 self.pop_front()
335 }
336 }
337
338 /// Something that can receive edges from a `Trace` implementation.
339 pub trait Tracer {
340 /// Note an edge between items. Called from within a `Trace` implementation.
visit_kind(&mut self, item: ItemId, kind: EdgeKind)341 fn visit_kind(&mut self, item: ItemId, kind: EdgeKind);
342
343 /// A synonym for `tracer.visit_kind(item, EdgeKind::Generic)`.
visit(&mut self, item: ItemId)344 fn visit(&mut self, item: ItemId) {
345 self.visit_kind(item, EdgeKind::Generic);
346 }
347 }
348
349 impl<F> Tracer for F
350 where
351 F: FnMut(ItemId, EdgeKind),
352 {
visit_kind(&mut self, item: ItemId, kind: EdgeKind)353 fn visit_kind(&mut self, item: ItemId, kind: EdgeKind) {
354 (*self)(item, kind)
355 }
356 }
357
358 /// Trace all of the outgoing edges to other items. Implementations should call
359 /// one of `tracer.visit(edge)` or `tracer.visit_kind(edge, EdgeKind::Whatever)`
360 /// for each of their outgoing edges.
361 pub trait Trace {
362 /// If a particular type needs extra information beyond what it has in
363 /// `self` and `context` to find its referenced items, its implementation
364 /// can define this associated type, forcing callers to pass the needed
365 /// information through.
366 type Extra;
367
368 /// Trace all of this item's outgoing edges to other items.
trace<T>( &self, context: &BindgenContext, tracer: &mut T, extra: &Self::Extra, ) where T: Tracer369 fn trace<T>(
370 &self,
371 context: &BindgenContext,
372 tracer: &mut T,
373 extra: &Self::Extra,
374 ) where
375 T: Tracer;
376 }
377
378 /// An graph traversal of the transitive closure of references between items.
379 ///
380 /// See `BindgenContext::allowlisted_items` for more information.
381 pub struct ItemTraversal<'ctx, Storage, Queue, Predicate>
382 where
383 Storage: TraversalStorage<'ctx>,
384 Queue: TraversalQueue,
385 Predicate: TraversalPredicate,
386 {
387 ctx: &'ctx BindgenContext,
388
389 /// The set of items we have seen thus far in this traversal.
390 seen: Storage,
391
392 /// The set of items that we have seen, but have yet to traverse.
393 queue: Queue,
394
395 /// The predicate that determines which edges this traversal will follow.
396 predicate: Predicate,
397
398 /// The item we are currently traversing.
399 currently_traversing: Option<ItemId>,
400 }
401
402 impl<'ctx, Storage, Queue, Predicate>
403 ItemTraversal<'ctx, Storage, Queue, Predicate>
404 where
405 Storage: TraversalStorage<'ctx>,
406 Queue: TraversalQueue,
407 Predicate: TraversalPredicate,
408 {
409 /// Begin a new traversal, starting from the given roots.
new<R>( ctx: &'ctx BindgenContext, roots: R, predicate: Predicate, ) -> ItemTraversal<'ctx, Storage, Queue, Predicate> where R: IntoIterator<Item = ItemId>,410 pub fn new<R>(
411 ctx: &'ctx BindgenContext,
412 roots: R,
413 predicate: Predicate,
414 ) -> ItemTraversal<'ctx, Storage, Queue, Predicate>
415 where
416 R: IntoIterator<Item = ItemId>,
417 {
418 let mut seen = Storage::new(ctx);
419 let mut queue = Queue::default();
420
421 for id in roots {
422 seen.add(None, id);
423 queue.push(id);
424 }
425
426 ItemTraversal {
427 ctx: ctx,
428 seen: seen,
429 queue: queue,
430 predicate: predicate,
431 currently_traversing: None,
432 }
433 }
434 }
435
436 impl<'ctx, Storage, Queue, Predicate> Tracer
437 for ItemTraversal<'ctx, Storage, Queue, Predicate>
438 where
439 Storage: TraversalStorage<'ctx>,
440 Queue: TraversalQueue,
441 Predicate: TraversalPredicate,
442 {
visit_kind(&mut self, item: ItemId, kind: EdgeKind)443 fn visit_kind(&mut self, item: ItemId, kind: EdgeKind) {
444 let edge = Edge::new(item, kind);
445 if !self.predicate.should_follow(self.ctx, edge) {
446 return;
447 }
448
449 let is_newly_discovered =
450 self.seen.add(self.currently_traversing, item);
451 if is_newly_discovered {
452 self.queue.push(item)
453 }
454 }
455 }
456
457 impl<'ctx, Storage, Queue, Predicate> Iterator
458 for ItemTraversal<'ctx, Storage, Queue, Predicate>
459 where
460 Storage: TraversalStorage<'ctx>,
461 Queue: TraversalQueue,
462 Predicate: TraversalPredicate,
463 {
464 type Item = ItemId;
465
next(&mut self) -> Option<Self::Item>466 fn next(&mut self) -> Option<Self::Item> {
467 let id = self.queue.next()?;
468
469 let newly_discovered = self.seen.add(None, id);
470 debug_assert!(
471 !newly_discovered,
472 "should have already seen anything we get out of our queue"
473 );
474 debug_assert!(
475 self.ctx.resolve_item_fallible(id).is_some(),
476 "should only get IDs of actual items in our context during traversal"
477 );
478
479 self.currently_traversing = Some(id);
480 id.trace(self.ctx, self, &());
481 self.currently_traversing = None;
482
483 Some(id)
484 }
485 }
486
487 /// An iterator to find any dangling items.
488 ///
489 /// See `BindgenContext::assert_no_dangling_item_traversal` for more
490 /// information.
491 pub type AssertNoDanglingItemsTraversal<'ctx> = ItemTraversal<
492 'ctx,
493 Paths<'ctx>,
494 VecDeque<ItemId>,
495 for<'a> fn(&'a BindgenContext, Edge) -> bool,
496 >;
497
498 #[cfg(test)]
499 mod tests {
500 use super::*;
501
502 #[test]
503 #[allow(dead_code)]
traversal_predicate_is_object_safe()504 fn traversal_predicate_is_object_safe() {
505 // This should compile only if TraversalPredicate is object safe.
506 fn takes_by_trait_object(_: &dyn TraversalPredicate) {}
507 }
508 }
509