• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&current).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