rustc_middle/ty/
walk.rs

1//! An iterator over the type substructure.
2//! WARNING: this does not keep track of the region depth.
3
4use rustc_data_structures::sso::SsoHashSet;
5use smallvec::{SmallVec, smallvec};
6use tracing::debug;
7
8use crate::ty::{self, GenericArg, GenericArgKind, Ty};
9
10// The TypeWalker's stack is hot enough that it's worth going to some effort to
11// avoid heap allocations.
12type TypeWalkerStack<'tcx> = SmallVec<[GenericArg<'tcx>; 8]>;
13
14pub struct TypeWalker<'tcx> {
15    stack: TypeWalkerStack<'tcx>,
16    last_subtree: usize,
17    pub visited: SsoHashSet<GenericArg<'tcx>>,
18}
19
20/// An iterator for walking the type tree.
21///
22/// It's very easy to produce a deeply
23/// nested type tree with a lot of
24/// identical subtrees. In order to work efficiently
25/// in this situation walker only visits each type once.
26/// It maintains a set of visited types and
27/// skips any types that are already there.
28impl<'tcx> TypeWalker<'tcx> {
29    pub fn new(root: GenericArg<'tcx>) -> Self {
30        Self { stack: smallvec![root], last_subtree: 1, visited: SsoHashSet::new() }
31    }
32
33    /// Skips the subtree corresponding to the last type
34    /// returned by `next()`.
35    ///
36    /// Example: Imagine you are walking `Foo<Bar<i32>, usize>`.
37    ///
38    /// ```ignore (illustrative)
39    /// let mut iter: TypeWalker = ...;
40    /// iter.next(); // yields Foo
41    /// iter.next(); // yields Bar<i32>
42    /// iter.skip_current_subtree(); // skips i32
43    /// iter.next(); // yields usize
44    /// ```
45    pub fn skip_current_subtree(&mut self) {
46        self.stack.truncate(self.last_subtree);
47    }
48}
49
50impl<'tcx> Iterator for TypeWalker<'tcx> {
51    type Item = GenericArg<'tcx>;
52
53    fn next(&mut self) -> Option<GenericArg<'tcx>> {
54        debug!("next(): stack={:?}", self.stack);
55        loop {
56            let next = self.stack.pop()?;
57            self.last_subtree = self.stack.len();
58            if self.visited.insert(next) {
59                push_inner(&mut self.stack, next);
60                debug!("next: stack={:?}", self.stack);
61                return Some(next);
62            }
63        }
64    }
65}
66
67impl<'tcx> GenericArg<'tcx> {
68    /// Iterator that walks `self` and any types reachable from
69    /// `self`, in depth-first order. Note that just walks the types
70    /// that appear in `self`, it does not descend into the fields of
71    /// structs or variants. For example:
72    ///
73    /// ```text
74    /// isize => { isize }
75    /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize }
76    /// [isize] => { [isize], isize }
77    /// ```
78    pub fn walk(self) -> TypeWalker<'tcx> {
79        TypeWalker::new(self)
80    }
81}
82
83impl<'tcx> Ty<'tcx> {
84    /// Iterator that walks `self` and any types reachable from
85    /// `self`, in depth-first order. Note that just walks the types
86    /// that appear in `self`, it does not descend into the fields of
87    /// structs or variants. For example:
88    ///
89    /// ```text
90    /// isize => { isize }
91    /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize }
92    /// [isize] => { [isize], isize }
93    /// ```
94    pub fn walk(self) -> TypeWalker<'tcx> {
95        TypeWalker::new(self.into())
96    }
97}
98
99impl<'tcx> ty::Const<'tcx> {
100    /// Iterator that walks `self` and any types reachable from
101    /// `self`, in depth-first order. Note that just walks the types
102    /// that appear in `self`, it does not descend into the fields of
103    /// structs or variants. For example:
104    ///
105    /// ```text
106    /// isize => { isize }
107    /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize }
108    /// [isize] => { [isize], isize }
109    /// ```
110    pub fn walk(self) -> TypeWalker<'tcx> {
111        TypeWalker::new(self.into())
112    }
113}
114
115/// We push `GenericArg`s on the stack in reverse order so as to
116/// maintain a pre-order traversal. As of the time of this
117/// writing, the fact that the traversal is pre-order is not
118/// known to be significant to any code, but it seems like the
119/// natural order one would expect (basically, the order of the
120/// types as they are written).
121fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) {
122    match parent.unpack() {
123        GenericArgKind::Type(parent_ty) => match *parent_ty.kind() {
124            ty::Bool
125            | ty::Char
126            | ty::Int(_)
127            | ty::Uint(_)
128            | ty::Float(_)
129            | ty::Str
130            | ty::Infer(_)
131            | ty::Param(_)
132            | ty::Never
133            | ty::Error(_)
134            | ty::Placeholder(..)
135            | ty::Bound(..)
136            | ty::Foreign(..) => {}
137
138            ty::Pat(ty, pat) => {
139                match *pat {
140                    ty::PatternKind::Range { start, end, include_end: _ } => {
141                        stack.extend(end.map(Into::into));
142                        stack.extend(start.map(Into::into));
143                    }
144                }
145                stack.push(ty.into());
146            }
147            ty::Array(ty, len) => {
148                stack.push(len.into());
149                stack.push(ty.into());
150            }
151            ty::Slice(ty) => {
152                stack.push(ty.into());
153            }
154            ty::RawPtr(ty, _) => {
155                stack.push(ty.into());
156            }
157            ty::Ref(lt, ty, _) => {
158                stack.push(ty.into());
159                stack.push(lt.into());
160            }
161            ty::Alias(_, data) => {
162                stack.extend(data.args.iter().rev());
163            }
164            ty::Dynamic(obj, lt, _) => {
165                stack.push(lt.into());
166                stack.extend(obj.iter().rev().flat_map(|predicate| {
167                    let (args, opt_ty) = match predicate.skip_binder() {
168                        ty::ExistentialPredicate::Trait(tr) => (tr.args, None),
169                        ty::ExistentialPredicate::Projection(p) => (p.args, Some(p.term)),
170                        ty::ExistentialPredicate::AutoTrait(_) =>
171                        // Empty iterator
172                        {
173                            (ty::GenericArgs::empty(), None)
174                        }
175                    };
176
177                    args.iter().rev().chain(opt_ty.map(|term| match term.unpack() {
178                        ty::TermKind::Ty(ty) => ty.into(),
179                        ty::TermKind::Const(ct) => ct.into(),
180                    }))
181                }));
182            }
183            ty::Adt(_, args)
184            | ty::Closure(_, args)
185            | ty::CoroutineClosure(_, args)
186            | ty::Coroutine(_, args)
187            | ty::CoroutineWitness(_, args)
188            | ty::FnDef(_, args) => {
189                stack.extend(args.iter().rev());
190            }
191            ty::Tuple(ts) => stack.extend(ts.iter().rev().map(GenericArg::from)),
192            ty::FnPtr(sig_tys, _hdr) => {
193                stack.extend(
194                    sig_tys.skip_binder().inputs_and_output.iter().rev().map(|ty| ty.into()),
195                );
196            }
197            ty::UnsafeBinder(bound_ty) => {
198                stack.push(bound_ty.skip_binder().into());
199            }
200        },
201        GenericArgKind::Lifetime(_) => {}
202        GenericArgKind::Const(parent_ct) => match parent_ct.kind() {
203            ty::ConstKind::Infer(_)
204            | ty::ConstKind::Param(_)
205            | ty::ConstKind::Placeholder(_)
206            | ty::ConstKind::Bound(..)
207            | ty::ConstKind::Error(_) => {}
208
209            ty::ConstKind::Value(cv) => stack.push(cv.ty.into()),
210
211            ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()),
212            ty::ConstKind::Unevaluated(ct) => {
213                stack.extend(ct.args.iter().rev());
214            }
215        },
216    }
217}