@@ -13,8 +13,10 @@ use borrow_check::error_reporting::UseSpans;
1313use borrow_check:: nll:: region_infer:: Cause ;
1414use borrow_check:: { Context , MirBorrowckCtxt , WriteKind } ;
1515use 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+ } ;
1820use rustc_errors:: DiagnosticBuilder ;
1921use syntax_pos:: Span ;
2022
@@ -34,6 +36,7 @@ pub(in borrow_check) enum BorrowExplanation<'tcx> {
3436
3537#[ derive( Clone , Copy ) ]
3638pub ( in borrow_check) enum LaterUseKind {
39+ TraitCapture ,
3740 ClosureCapture ,
3841 Call ,
3942 FakeLetRead ,
@@ -51,6 +54,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
5154 match * self {
5255 BorrowExplanation :: UsedLater ( later_use_kind, var_or_use_span) => {
5356 let message = match later_use_kind {
57+ LaterUseKind :: TraitCapture => "borrow later captured here by trait object" ,
5458 LaterUseKind :: ClosureCapture => "borrow later captured here by closure" ,
5559 LaterUseKind :: Call => "borrow later used by call" ,
5660 LaterUseKind :: FakeLetRead => "borrow later stored here" ,
@@ -60,9 +64,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
6064 } ,
6165 BorrowExplanation :: UsedLaterInLoop ( later_use_kind, var_or_use_span) => {
6266 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" ,
6671 LaterUseKind :: Call => "borrow used by call, in later iteration of loop" ,
6772 LaterUseKind :: FakeLetRead => "borrow later stored here" ,
6873 LaterUseKind :: Other => "borrow used here, in later iteration of loop" ,
@@ -200,13 +205,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
200205 . or_else ( || self . borrow_spans ( span, location) ) ;
201206
202207 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) ;
204209 BorrowExplanation :: UsedLaterInLoop ( later_use. 0 , later_use. 1 )
205210 } else {
206211 // Check if the location represents a `FakeRead`, and adapt the error
207212 // message to the `FakeReadCause` it is from: in particular,
208213 // 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) ;
210215 BorrowExplanation :: UsedLater ( later_use. 0 , later_use. 1 )
211216 }
212217 }
@@ -316,42 +321,184 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
316321 false
317322 }
318323
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 ) {
323331 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+ } ,
325336 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
330366 }
331367 } 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 ;
350424 } ,
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+ _ => { } ,
352459 }
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+ }
354496 }
497+
498+ debug ! ( "was_captured_by_trait: queue={:?}" , queue) ;
355499 }
500+
501+ // We didn't find anything and ran out of locations to check.
502+ false
356503 }
357504}
0 commit comments