@@ -13,8 +13,10 @@ use borrow_check::error_reporting::UseSpans;
13
13
use borrow_check:: nll:: region_infer:: Cause ;
14
14
use borrow_check:: { Context , MirBorrowckCtxt , WriteKind } ;
15
15
use rustc:: ty:: { self , Region , TyCtxt } ;
16
- use rustc:: mir:: { FakeReadCause , Local , Location , Mir , Operand } ;
17
- use rustc:: mir:: { Place , StatementKind , TerminatorKind } ;
16
+ use rustc:: mir:: {
17
+ CastKind , FakeReadCause , Local , Location , Mir , Operand , Place , Projection , ProjectionElem ,
18
+ Rvalue , Statement , StatementKind , TerminatorKind
19
+ } ;
18
20
use rustc_errors:: DiagnosticBuilder ;
19
21
use syntax_pos:: Span ;
20
22
@@ -34,6 +36,7 @@ pub(in borrow_check) enum BorrowExplanation<'tcx> {
34
36
35
37
#[ derive( Clone , Copy ) ]
36
38
pub ( in borrow_check) enum LaterUseKind {
39
+ TraitCapture ,
37
40
ClosureCapture ,
38
41
Call ,
39
42
FakeLetRead ,
@@ -51,6 +54,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
51
54
match * self {
52
55
BorrowExplanation :: UsedLater ( later_use_kind, var_or_use_span) => {
53
56
let message = match later_use_kind {
57
+ LaterUseKind :: TraitCapture => "borrow later captured here by trait object" ,
54
58
LaterUseKind :: ClosureCapture => "borrow later captured here by closure" ,
55
59
LaterUseKind :: Call => "borrow later used by call" ,
56
60
LaterUseKind :: FakeLetRead => "borrow later stored here" ,
@@ -60,9 +64,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
60
64
} ,
61
65
BorrowExplanation :: UsedLaterInLoop ( later_use_kind, var_or_use_span) => {
62
66
let message = match later_use_kind {
63
- LaterUseKind :: ClosureCapture => {
64
- "borrow captured here by closure, in later iteration of loop"
65
- } ,
67
+ LaterUseKind :: TraitCapture =>
68
+ "borrow captured here by trait object, in later iteration of loop" ,
69
+ LaterUseKind :: ClosureCapture =>
70
+ "borrow captured here by closure, in later iteration of loop" ,
66
71
LaterUseKind :: Call => "borrow used by call, in later iteration of loop" ,
67
72
LaterUseKind :: FakeLetRead => "borrow later stored here" ,
68
73
LaterUseKind :: Other => "borrow used here, in later iteration of loop" ,
@@ -200,13 +205,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
200
205
. or_else ( || self . borrow_spans ( span, location) ) ;
201
206
202
207
if self . is_borrow_location_in_loop ( context. loc ) {
203
- let later_use = self . later_use_kind ( spans, location) ;
208
+ let later_use = self . later_use_kind ( borrow , spans, location) ;
204
209
BorrowExplanation :: UsedLaterInLoop ( later_use. 0 , later_use. 1 )
205
210
} else {
206
211
// Check if the location represents a `FakeRead`, and adapt the error
207
212
// message to the `FakeReadCause` it is from: in particular,
208
213
// the ones inserted in optimized `let var = <expr>` patterns.
209
- let later_use = self . later_use_kind ( spans, location) ;
214
+ let later_use = self . later_use_kind ( borrow , spans, location) ;
210
215
BorrowExplanation :: UsedLater ( later_use. 0 , later_use. 1 )
211
216
}
212
217
}
@@ -316,42 +321,184 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
316
321
false
317
322
}
318
323
319
- fn later_use_kind ( & self , use_spans : UseSpans , location : Location ) -> ( LaterUseKind , Span ) {
320
- use self :: LaterUseKind :: * ;
321
-
322
- let block = & self . mir . basic_blocks ( ) [ location. block ] ;
324
+ /// Determine how the borrow was later used.
325
+ fn later_use_kind (
326
+ & self ,
327
+ borrow : & BorrowData < ' tcx > ,
328
+ use_spans : UseSpans ,
329
+ location : Location
330
+ ) -> ( LaterUseKind , Span ) {
323
331
match use_spans {
324
- UseSpans :: ClosureUse { var_span, .. } => ( LaterUseKind :: ClosureCapture , var_span) ,
332
+ UseSpans :: ClosureUse { var_span, .. } => {
333
+ // Used in a closure.
334
+ ( LaterUseKind :: ClosureCapture , var_span)
335
+ } ,
325
336
UseSpans :: OtherUse ( span) => {
326
- ( if let Some ( stmt) = block. statements . get ( location. statement_index ) {
327
- match stmt. kind {
328
- StatementKind :: FakeRead ( FakeReadCause :: ForLet , _) => FakeLetRead ,
329
- _ => Other ,
337
+ let block = & self . mir . basic_blocks ( ) [ location. block ] ;
338
+
339
+ let kind = if let Some ( & Statement {
340
+ kind : StatementKind :: FakeRead ( FakeReadCause :: ForLet , _) ,
341
+ ..
342
+ } ) = block. statements . get ( location. statement_index ) {
343
+ LaterUseKind :: FakeLetRead
344
+ } else if self . was_captured_by_trait_object ( borrow) {
345
+ LaterUseKind :: TraitCapture
346
+ } else if location. statement_index == block. statements . len ( ) {
347
+ if let TerminatorKind :: Call {
348
+ ref func, from_hir_call : true , ..
349
+ } = block. terminator ( ) . kind {
350
+ // Just point to the function, to reduce the chance of overlapping spans.
351
+ let function_span = match func {
352
+ Operand :: Constant ( c) => c. span ,
353
+ Operand :: Copy ( Place :: Local ( l) ) | Operand :: Move ( Place :: Local ( l) ) => {
354
+ let local_decl = & self . mir . local_decls [ * l] ;
355
+ if local_decl. name . is_none ( ) {
356
+ local_decl. source_info . span
357
+ } else {
358
+ span
359
+ }
360
+ } ,
361
+ _ => span,
362
+ } ;
363
+ return ( LaterUseKind :: Call , function_span) ;
364
+ } else {
365
+ LaterUseKind :: Other
330
366
}
331
367
} else {
332
- assert_eq ! ( location. statement_index, block. statements. len( ) ) ;
333
- match block. terminator ( ) . kind {
334
- TerminatorKind :: Call { ref func, from_hir_call : true , .. } => {
335
- // Just point to the function, to reduce the chance
336
- // of overlapping spans.
337
- let function_span = match func {
338
- Operand :: Constant ( c) => c. span ,
339
- Operand :: Copy ( Place :: Local ( l) ) | Operand :: Move ( Place :: Local ( l) ) => {
340
- let local_decl = & self . mir . local_decls [ * l] ;
341
- if local_decl. name . is_none ( ) {
342
- local_decl. source_info . span
343
- } else {
344
- span
345
- }
346
- } ,
347
- _ => span,
348
- } ;
349
- return ( Call , function_span) ;
368
+ LaterUseKind :: Other
369
+ } ;
370
+
371
+ ( kind, span)
372
+ }
373
+ }
374
+ }
375
+
376
+ /// Check if a borrowed value was captured by a trait object. We do this by
377
+ /// looking forward in the MIR from the reserve location and checking if we see
378
+ /// a unsized cast to a trait object on our data.
379
+ fn was_captured_by_trait_object ( & self , borrow : & BorrowData < ' tcx > ) -> bool {
380
+ // Start at the reserve location, find the place that we want to see cast to a trait object.
381
+ let location = borrow. reserve_location ;
382
+ let block = & self . mir [ location. block ] ;
383
+ let stmt = block. statements . get ( location. statement_index ) ;
384
+ debug ! ( "was_captured_by_trait_object: location={:?} stmt={:?}" , location, stmt) ;
385
+
386
+ // We make a `queue` vector that has the locations we want to visit. As of writing, this
387
+ // will only ever have one item at any given time, but by using a vector, we can pop from
388
+ // it which simplifies the termination logic.
389
+ let mut queue = vec ! [ location] ;
390
+ let mut target = if let Some ( & Statement {
391
+ kind : StatementKind :: Assign ( Place :: Local ( local) , _) ,
392
+ ..
393
+ } ) = stmt {
394
+ local
395
+ } else {
396
+ return false ;
397
+ } ;
398
+
399
+ debug ! ( "was_captured_by_trait: target={:?} queue={:?}" , target, queue) ;
400
+ while let Some ( current_location) = queue. pop ( ) {
401
+ debug ! ( "was_captured_by_trait: target={:?}" , target) ;
402
+ let block = & self . mir [ current_location. block ] ;
403
+ // We need to check the current location to find out if it is a terminator.
404
+ let is_terminator = current_location. statement_index == block. statements . len ( ) ;
405
+ if !is_terminator {
406
+ let stmt = & block. statements [ current_location. statement_index ] ;
407
+ debug ! ( "was_captured_by_trait_object: stmt={:?}" , stmt) ;
408
+
409
+ // The only kind of statement that we care about is assignments...
410
+ if let StatementKind :: Assign (
411
+ place,
412
+ box rvalue,
413
+ ) = & stmt. kind {
414
+ let into = match place {
415
+ Place :: Local ( into) => into,
416
+ Place :: Projection ( box Projection {
417
+ base : Place :: Local ( into) ,
418
+ elem : ProjectionElem :: Deref ,
419
+ } ) => into,
420
+ _ => {
421
+ // Continue at the next location.
422
+ queue. push ( current_location. successor_within_block ( ) ) ;
423
+ continue ;
350
424
} ,
351
- _ => Other ,
425
+ } ;
426
+
427
+ match rvalue {
428
+ // If we see a use, we should check whether it is our data, and if so
429
+ // update the place that we're looking for to that new place.
430
+ Rvalue :: Use ( operand) => match operand {
431
+ Operand :: Copy ( Place :: Local ( from) ) |
432
+ Operand :: Move ( Place :: Local ( from) ) if * from == target => {
433
+ target = * into;
434
+ } ,
435
+ _ => { } ,
436
+ } ,
437
+ // If we see a unsized cast, then if it is our data we should check
438
+ // whether it is being cast to a trait object.
439
+ Rvalue :: Cast ( CastKind :: Unsize , operand, ty) => match operand {
440
+ Operand :: Copy ( Place :: Local ( from) ) |
441
+ Operand :: Move ( Place :: Local ( from) ) if * from == target => {
442
+ debug ! ( "was_captured_by_trait_object: ty={:?}" , ty) ;
443
+ // Check the type for a trait object.
444
+ match ty. sty {
445
+ // `&dyn Trait`
446
+ ty:: TyKind :: Ref ( _, ty, _) if ty. is_trait ( ) => return true ,
447
+ // `Box<dyn Trait>`
448
+ _ if ty. is_box ( ) && ty. boxed_ty ( ) . is_trait ( ) =>
449
+ return true ,
450
+ // `dyn Trait`
451
+ _ if ty. is_trait ( ) => return true ,
452
+ // Anything else.
453
+ _ => return false ,
454
+ }
455
+ } ,
456
+ _ => return false ,
457
+ } ,
458
+ _ => { } ,
352
459
}
353
- } , span)
460
+ }
461
+
462
+ // Continue at the next location.
463
+ queue. push ( current_location. successor_within_block ( ) ) ;
464
+ } else {
465
+ // The only thing we need to do for terminators is progress to the next block.
466
+ let terminator = block. terminator ( ) ;
467
+ debug ! ( "was_captured_by_trait_object: terminator={:?}" , terminator) ;
468
+
469
+ match & terminator. kind {
470
+ TerminatorKind :: Call {
471
+ destination : Some ( ( Place :: Local ( dest) , block) ) ,
472
+ args,
473
+ ..
474
+ } => {
475
+ debug ! (
476
+ "was_captured_by_trait_object: target={:?} dest={:?} args={:?}" ,
477
+ target, dest, args
478
+ ) ;
479
+ // Check if one of the arguments to this function is the target place.
480
+ let found_target = args. iter ( ) . any ( |arg| {
481
+ if let Operand :: Move ( Place :: Local ( potential) ) = arg {
482
+ * potential == target
483
+ } else {
484
+ false
485
+ }
486
+ } ) ;
487
+
488
+ // If it is, follow this to the next block and update the target.
489
+ if found_target {
490
+ target = * dest;
491
+ queue. push ( block. start_location ( ) ) ;
492
+ }
493
+ } ,
494
+ _ => { } ,
495
+ }
354
496
}
497
+
498
+ debug ! ( "was_captured_by_trait: queue={:?}" , queue) ;
355
499
}
500
+
501
+ // We didn't find anything and ran out of locations to check.
502
+ false
356
503
}
357
504
}
0 commit comments