@@ -8,11 +8,13 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
8
8
use rustc_hir as hir;
9
9
use rustc_hir:: def:: { CtorKind , DefKind , Res } ;
10
10
use rustc_hir:: def_id:: DefId ;
11
+ use rustc_hir:: hir_id:: HirIdSet ;
11
12
use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
12
- use rustc_hir:: { Expr , ExprKind , Pat , PatKind } ;
13
+ use rustc_hir:: { Arm , Expr , ExprKind , Guard , HirId , Pat , PatKind } ;
13
14
use rustc_middle:: middle:: region:: { self , YieldData } ;
14
15
use rustc_middle:: ty:: { self , Ty } ;
15
16
use rustc_span:: Span ;
17
+ use smallvec:: SmallVec ;
16
18
17
19
struct InteriorVisitor < ' a , ' tcx > {
18
20
fcx : & ' a FnCtxt < ' a , ' tcx > ,
@@ -21,6 +23,13 @@ struct InteriorVisitor<'a, 'tcx> {
21
23
expr_count : usize ,
22
24
kind : hir:: GeneratorKind ,
23
25
prev_unresolved_span : Option < Span > ,
26
+ /// Match arm guards have temporary borrows from the pattern bindings.
27
+ /// In case there is a yield point in a guard with a reference to such bindings,
28
+ /// such borrows can span across this yield point.
29
+ /// As such, we need to track these borrows and record them despite of the fact
30
+ /// that they may succeed the said yield point in the post-order.
31
+ guard_bindings : SmallVec < [ SmallVec < [ HirId ; 4 ] > ; 1 ] > ,
32
+ guard_bindings_set : HirIdSet ,
24
33
}
25
34
26
35
impl < ' a , ' tcx > InteriorVisitor < ' a , ' tcx > {
@@ -30,6 +39,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
30
39
scope : Option < region:: Scope > ,
31
40
expr : Option < & ' tcx Expr < ' tcx > > ,
32
41
source_span : Span ,
42
+ guard_borrowing_from_pattern : bool ,
33
43
) {
34
44
use rustc_span:: DUMMY_SP ;
35
45
@@ -53,7 +63,12 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
53
63
yield_data. expr_and_pat_count, self . expr_count, source_span
54
64
) ;
55
65
56
- if yield_data. expr_and_pat_count >= self . expr_count {
66
+ // If it is a borrowing happening in the guard,
67
+ // it needs to be recorded regardless because they
68
+ // do live across this yield point.
69
+ if guard_borrowing_from_pattern
70
+ || yield_data. expr_and_pat_count >= self . expr_count
71
+ {
57
72
Some ( yield_data)
58
73
} else {
59
74
None
@@ -134,6 +149,8 @@ pub fn resolve_interior<'a, 'tcx>(
134
149
expr_count : 0 ,
135
150
kind,
136
151
prev_unresolved_span : None ,
152
+ guard_bindings : <_ >:: default ( ) ,
153
+ guard_bindings_set : <_ >:: default ( ) ,
137
154
} ;
138
155
intravisit:: walk_body ( & mut visitor, body) ;
139
156
@@ -210,6 +227,38 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
210
227
NestedVisitorMap :: None
211
228
}
212
229
230
+ fn visit_arm ( & mut self , arm : & ' tcx Arm < ' tcx > ) {
231
+ let Arm { guard, pat, body, .. } = arm;
232
+ self . visit_pat ( pat) ;
233
+ if let Some ( ref g) = guard {
234
+ self . guard_bindings . push ( <_ >:: default ( ) ) ;
235
+ ArmPatCollector {
236
+ guard_bindings_set : & mut self . guard_bindings_set ,
237
+ guard_bindings : self
238
+ . guard_bindings
239
+ . last_mut ( )
240
+ . expect ( "should have pushed at least one earlier" ) ,
241
+ }
242
+ . visit_pat ( pat) ;
243
+
244
+ match g {
245
+ Guard :: If ( ref e) => {
246
+ self . visit_expr ( e) ;
247
+ }
248
+ }
249
+
250
+ let mut scope_var_ids =
251
+ self . guard_bindings . pop ( ) . expect ( "should have pushed at least one earlier" ) ;
252
+ for var_id in scope_var_ids. drain ( ..) {
253
+ assert ! (
254
+ self . guard_bindings_set. remove( & var_id) ,
255
+ "variable should be placed in scope earlier"
256
+ ) ;
257
+ }
258
+ }
259
+ self . visit_expr ( body) ;
260
+ }
261
+
213
262
fn visit_pat ( & mut self , pat : & ' tcx Pat < ' tcx > ) {
214
263
intravisit:: walk_pat ( self , pat) ;
215
264
@@ -218,11 +267,12 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
218
267
if let PatKind :: Binding ( ..) = pat. kind {
219
268
let scope = self . region_scope_tree . var_scope ( pat. hir_id . local_id ) ;
220
269
let ty = self . fcx . typeck_results . borrow ( ) . pat_ty ( pat) ;
221
- self . record ( ty, Some ( scope) , None , pat. span ) ;
270
+ self . record ( ty, Some ( scope) , None , pat. span , false ) ;
222
271
}
223
272
}
224
273
225
274
fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
275
+ let mut guard_borrowing_from_pattern = false ;
226
276
match & expr. kind {
227
277
ExprKind :: Call ( callee, args) => match & callee. kind {
228
278
ExprKind :: Path ( qpath) => {
@@ -249,6 +299,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
249
299
}
250
300
_ => intravisit:: walk_expr ( self , expr) ,
251
301
} ,
302
+ ExprKind :: Path ( qpath) => {
303
+ intravisit:: walk_expr ( self , expr) ;
304
+ let res = self . fcx . typeck_results . borrow ( ) . qpath_res ( qpath, expr. hir_id ) ;
305
+ match res {
306
+ Res :: Local ( id) if self . guard_bindings_set . contains ( & id) => {
307
+ guard_borrowing_from_pattern = true ;
308
+ }
309
+ _ => { }
310
+ }
311
+ }
252
312
_ => intravisit:: walk_expr ( self , expr) ,
253
313
}
254
314
@@ -259,18 +319,18 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
259
319
// If there are adjustments, then record the final type --
260
320
// this is the actual value that is being produced.
261
321
if let Some ( adjusted_ty) = self . fcx . typeck_results . borrow ( ) . expr_ty_adjusted_opt ( expr) {
262
- self . record ( adjusted_ty, scope, Some ( expr) , expr. span ) ;
322
+ self . record ( adjusted_ty, scope, Some ( expr) , expr. span , guard_borrowing_from_pattern ) ;
263
323
}
264
324
265
325
// Also record the unadjusted type (which is the only type if
266
326
// there are no adjustments). The reason for this is that the
267
327
// unadjusted value is sometimes a "temporary" that would wind
268
328
// up in a MIR temporary.
269
329
//
270
- // As an example, consider an expression like `vec![].push()`.
330
+ // As an example, consider an expression like `vec![].push(x )`.
271
331
// Here, the `vec![]` would wind up MIR stored into a
272
332
// temporary variable `t` which we can borrow to invoke
273
- // `<Vec<_>>::push(&mut t)`.
333
+ // `<Vec<_>>::push(&mut t, x )`.
274
334
//
275
335
// Note that an expression can have many adjustments, and we
276
336
// are just ignoring those intermediate types. This is because
@@ -287,9 +347,30 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
287
347
// The type table might not have information for this expression
288
348
// if it is in a malformed scope. (#66387)
289
349
if let Some ( ty) = self . fcx . typeck_results . borrow ( ) . expr_ty_opt ( expr) {
290
- self . record ( ty, scope, Some ( expr) , expr. span ) ;
350
+ self . record ( ty, scope, Some ( expr) , expr. span , guard_borrowing_from_pattern ) ;
291
351
} else {
292
352
self . fcx . tcx . sess . delay_span_bug ( expr. span , "no type for node" ) ;
293
353
}
294
354
}
295
355
}
356
+
357
+ struct ArmPatCollector < ' a > {
358
+ guard_bindings_set : & ' a mut HirIdSet ,
359
+ guard_bindings : & ' a mut SmallVec < [ HirId ; 4 ] > ,
360
+ }
361
+
362
+ impl < ' a , ' tcx > Visitor < ' tcx > for ArmPatCollector < ' a > {
363
+ type Map = intravisit:: ErasedMap < ' tcx > ;
364
+
365
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
366
+ NestedVisitorMap :: None
367
+ }
368
+
369
+ fn visit_pat ( & mut self , pat : & ' tcx Pat < ' tcx > ) {
370
+ intravisit:: walk_pat ( self , pat) ;
371
+ if let PatKind :: Binding ( _, id, ..) = pat. kind {
372
+ self . guard_bindings . push ( id) ;
373
+ self . guard_bindings_set . insert ( id) ;
374
+ }
375
+ }
376
+ }
0 commit comments