@@ -14,7 +14,9 @@ use rustc_infer::traits::Normalized;
14
14
use rustc_middle:: mir;
15
15
use rustc_middle:: ty:: fold:: { TypeFoldable , TypeFolder } ;
16
16
use rustc_middle:: ty:: subst:: Subst ;
17
- use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
17
+ use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeVisitor } ;
18
+
19
+ use std:: ops:: ControlFlow ;
18
20
19
21
use super :: NoSolution ;
20
22
@@ -65,6 +67,14 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
65
67
universes : vec ! [ ] ,
66
68
} ;
67
69
70
+ if value. has_escaping_bound_vars ( ) {
71
+ let mut max_visitor =
72
+ MaxEscapingBoundVarVisitor { outer_index : ty:: INNERMOST , escaping : 0 } ;
73
+ value. visit_with ( & mut max_visitor) ;
74
+ if max_visitor. escaping > 0 {
75
+ normalizer. universes . extend ( ( 0 ..max_visitor. escaping ) . map ( |_| None ) ) ;
76
+ }
77
+ }
68
78
let result = value. fold_with ( & mut normalizer) ;
69
79
info ! (
70
80
"normalize::<{}>: result={:?} with {} obligations" ,
@@ -85,6 +95,58 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
85
95
}
86
96
}
87
97
98
+ /// Visitor to find the maximum escaping bound var
99
+ struct MaxEscapingBoundVarVisitor {
100
+ // The index which would count as escaping
101
+ outer_index : ty:: DebruijnIndex ,
102
+ escaping : usize ,
103
+ }
104
+
105
+ impl < ' tcx > TypeVisitor < ' tcx > for MaxEscapingBoundVarVisitor {
106
+ fn visit_binder < T : TypeFoldable < ' tcx > > (
107
+ & mut self ,
108
+ t : & ty:: Binder < ' tcx , T > ,
109
+ ) -> ControlFlow < Self :: BreakTy > {
110
+ self . outer_index . shift_in ( 1 ) ;
111
+ let result = t. super_visit_with ( self ) ;
112
+ self . outer_index . shift_out ( 1 ) ;
113
+ result
114
+ }
115
+
116
+ #[ inline]
117
+ fn visit_ty ( & mut self , t : Ty < ' tcx > ) -> ControlFlow < Self :: BreakTy > {
118
+ if t. outer_exclusive_binder ( ) > self . outer_index {
119
+ self . escaping = self
120
+ . escaping
121
+ . max ( t. outer_exclusive_binder ( ) . as_usize ( ) - self . outer_index . as_usize ( ) ) ;
122
+ }
123
+ ControlFlow :: CONTINUE
124
+ }
125
+
126
+ #[ inline]
127
+ fn visit_region ( & mut self , r : ty:: Region < ' tcx > ) -> ControlFlow < Self :: BreakTy > {
128
+ match * r {
129
+ ty:: ReLateBound ( debruijn, _) if debruijn > self . outer_index => {
130
+ self . escaping =
131
+ self . escaping . max ( debruijn. as_usize ( ) - self . outer_index . as_usize ( ) ) ;
132
+ }
133
+ _ => { }
134
+ }
135
+ ControlFlow :: CONTINUE
136
+ }
137
+
138
+ fn visit_const ( & mut self , ct : & ' tcx ty:: Const < ' tcx > ) -> ControlFlow < Self :: BreakTy > {
139
+ match ct. val {
140
+ ty:: ConstKind :: Bound ( debruijn, _) if debruijn >= self . outer_index => {
141
+ self . escaping =
142
+ self . escaping . max ( debruijn. as_usize ( ) - self . outer_index . as_usize ( ) ) ;
143
+ ControlFlow :: CONTINUE
144
+ }
145
+ _ => ct. super_visit_with ( self ) ,
146
+ }
147
+ }
148
+ }
149
+
88
150
struct QueryNormalizer < ' cx , ' tcx > {
89
151
infcx : & ' cx InferCtxt < ' cx , ' tcx > ,
90
152
cause : & ' cx ObligationCause < ' tcx > ,
@@ -121,14 +183,25 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
121
183
return ty;
122
184
}
123
185
124
- let ty = ty. super_fold_with ( self ) ;
186
+ // N.b. while we want to call `super_fold_with(self)` on `ty` before
187
+ // normalization, we wait until we know whether we need to normalize the
188
+ // current type. If we do, then we only fold the ty *after* replacing bound
189
+ // vars with placeholders. This means that nested types don't need to replace
190
+ // bound vars at the current binder level or above. A key assumption here is
191
+ // that folding the type can't introduce new bound vars.
192
+
193
+ // Wrap this in a closure so we don't accidentally return from the outer function
125
194
let res = ( || match * ty. kind ( ) {
126
- ty:: Opaque ( def_id, substs) if !substs . has_escaping_bound_vars ( ) => {
195
+ ty:: Opaque ( def_id, substs) => {
127
196
// Only normalize `impl Trait` after type-checking, usually in codegen.
128
197
match self . param_env . reveal ( ) {
129
- Reveal :: UserFacing => ty,
198
+ Reveal :: UserFacing => ty. super_fold_with ( self ) ,
130
199
131
200
Reveal :: All => {
201
+ // N.b. there is an assumption here all this code can handle
202
+ // escaping bound vars.
203
+
204
+ let substs = substs. super_fold_with ( self ) ;
132
205
let recursion_limit = self . tcx ( ) . recursion_limit ( ) ;
133
206
if !recursion_limit. value_within_limit ( self . anon_depth ) {
134
207
let obligation = Obligation :: with_depth (
@@ -161,19 +234,11 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
161
234
}
162
235
163
236
ty:: Projection ( data) if !data. has_escaping_bound_vars ( ) => {
164
- // This is kind of hacky -- we need to be able to
165
- // handle normalization within binders because
166
- // otherwise we wind up a need to normalize when doing
167
- // trait matching (since you can have a trait
168
- // obligation like `for<'a> T::B: Fn(&'a i32)`), but
169
- // we can't normalize with bound regions in scope. So
170
- // far now we just ignore binders but only normalize
171
- // if all bound regions are gone (and then we still
172
- // have to renormalize whenever we instantiate a
173
- // binder). It would be better to normalize in a
174
- // binding-aware fashion.
237
+ // This branch is just an optimization: when we don't have escaping bound vars,
238
+ // we don't need to replace them with placeholders (see branch below).
175
239
176
240
let tcx = self . infcx . tcx ;
241
+ let data = data. super_fold_with ( self ) ;
177
242
178
243
let mut orig_values = OriginalQueryValues :: default ( ) ;
179
244
// HACK(matthewjasper) `'static` is special-cased in selection,
@@ -217,22 +282,9 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
217
282
}
218
283
}
219
284
}
220
- ty:: Projection ( data) if !data. trait_ref ( self . infcx . tcx ) . has_escaping_bound_vars ( ) => {
221
- // See note in `rustc_trait_selection::traits::project`
222
-
223
- // One other point mentioning: In `traits::project`, if a
224
- // projection can't be normalized, we return an inference variable
225
- // and register an obligation to later resolve that. Here, the query
226
- // will just return ambiguity. In both cases, the effect is the same: we only want
227
- // to return `ty` because there are bound vars that we aren't yet handling in a more
228
- // complete way.
229
285
230
- // `BoundVarReplacer` can't handle escaping bound vars. Ideally, we want this before even calling
231
- // `QueryNormalizer`, but some const-generics tests pass escaping bound vars.
232
- // Also, use `ty` so we get that sweet `outer_exclusive_binder` optimization
233
- assert ! ( !ty. has_vars_bound_at_or_above( ty:: DebruijnIndex :: from_usize(
234
- self . universes. len( )
235
- ) ) ) ;
286
+ ty:: Projection ( data) => {
287
+ // See note in `rustc_trait_selection::traits::project`
236
288
237
289
let tcx = self . infcx . tcx ;
238
290
let infcx = self . infcx ;
@@ -292,7 +344,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
292
344
)
293
345
}
294
346
295
- _ => ty,
347
+ _ => ty. super_fold_with ( self ) ,
296
348
} ) ( ) ;
297
349
self . cache . insert ( ty, res) ;
298
350
res
0 commit comments