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}