@@ -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
+ FakeReadCause , Local , Location , Mir , Operand , Place , Rvalue , Statement , StatementKind ,
18
+ 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 later 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,136 @@ 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) ;
350
- } ,
351
- _ => Other ,
368
+ LaterUseKind :: Other
369
+ } ;
370
+
371
+ ( kind, span)
372
+ }
373
+ }
374
+ }
375
+
376
+ /// Check if a borrowed value was captured by a trait object.
377
+ fn was_captured_by_trait_object ( & self , borrow : & BorrowData < ' tcx > ) -> bool {
378
+ // In order to check if a value was captured by a trait object, we want to look through
379
+ // statements after the reserve location in the current block. We expect the reserve
380
+ // location to be a statement assigning to a local. We follow that local in the subsequent
381
+ // statements, checking for an assignment of our local (or something intermediate that
382
+ // it was assigned into) that results in a trait object.
383
+ let location = borrow. reserve_location ;
384
+ let block = & self . mir [ location. block ] ;
385
+ let stmt = block. statements . get ( location. statement_index ) ;
386
+ debug ! (
387
+ "was_captured_by_trait_object: location={:?} block={:?} stmt={:?}" ,
388
+ location, block, stmt
389
+ ) ;
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_object: target={:?}" , target) ;
400
+ for stmt in & block. statements [ location. statement_index + 1 ..] {
401
+ debug ! ( "was_captured_by_trait_object: stmt={:?}" , stmt) ;
402
+ // Simple case where our target is assigned into another local, and we start
403
+ // watching that local instead.
404
+ if let StatementKind :: Assign (
405
+ Place :: Local ( into) ,
406
+ box Rvalue :: Use ( operand) ,
407
+ ) = & stmt. kind {
408
+ debug ! ( "was_captured_by_trait_object: target={:?} operand={:?}" , target, operand) ;
409
+ match operand {
410
+ Operand :: Copy ( Place :: Local ( from) ) |
411
+ Operand :: Move ( Place :: Local ( from) ) if * from == target => target = * into,
412
+ _ => { } ,
413
+ }
414
+ }
415
+ }
416
+
417
+ if let Some ( terminator) = & block. terminator {
418
+ if let TerminatorKind :: Call {
419
+ destination : Some ( ( Place :: Local ( dest) , _) ) ,
420
+ args,
421
+ ..
422
+ } = & terminator. kind {
423
+ debug ! (
424
+ "was_captured_by_trait_object: target={:?} dest={:?} args={:?}" ,
425
+ target, dest, args
426
+ ) ;
427
+ let mut found_target = false ;
428
+ for arg in args {
429
+ if let Operand :: Move ( Place :: Local ( potential) ) = arg {
430
+ if * potential == target {
431
+ found_target = true ;
432
+ }
433
+ }
434
+ }
435
+
436
+ if found_target {
437
+ let local_decl_ty = & self . mir . local_decls [ * dest] . ty ;
438
+ debug ! ( "was_captured_by_trait_object: local_decl_ty={:?}" , local_decl_ty) ;
439
+ match local_decl_ty. sty {
440
+ // `&dyn Trait`
441
+ ty:: TyKind :: Ref ( _, ty, _) if ty. is_trait ( ) => return true ,
442
+ // `Box<dyn Trait>`
443
+ _ if local_decl_ty. is_box ( ) && local_decl_ty. boxed_ty ( ) . is_trait ( ) =>
444
+ return true ,
445
+ // `dyn Trait`
446
+ _ if local_decl_ty. is_trait ( ) => return true ,
447
+ // Anything else.
448
+ _ => return false ,
352
449
}
353
- } , span )
450
+ }
354
451
}
355
452
}
453
+
454
+ false
356
455
}
357
456
}
0 commit comments