@@ -69,8 +69,8 @@ use rustc_middle::ty::{
6969} ;
7070use rustc_middle:: { bug, span_bug} ;
7171use rustc_mir_dataflow:: impls:: {
72- MaybeBorrowedLocals , MaybeLiveLocals , MaybeRequiresStorage , MaybeStorageLive ,
73- always_storage_live_locals,
72+ CoroutinePinnedLocals , MaybeBorrowedLocals , MaybeLiveLocals , MaybeRequiresStorage ,
73+ MaybeStorageLive , always_storage_live_locals,
7474} ;
7575use rustc_mir_dataflow:: { Analysis , Results , ResultsVisitor } ;
7676use rustc_span:: def_id:: { DefId , LocalDefId } ;
@@ -639,6 +639,15 @@ struct LivenessInfo {
639639 /// Parallel vec to the above with SourceInfo for each yield terminator.
640640 source_info_at_suspension_points : Vec < SourceInfo > ,
641641
642+ /// Coroutine saved locals that are borrowed across a suspension point.
643+ /// This corresponds to locals that are "wrapped" with `UnsafePinned`.
644+ ///
645+ /// Note that movable coroutines do not allow borrowing locals across
646+ /// suspension points and thus will always have this set empty.
647+ ///
648+ /// For more information, see [RFC 3467](https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html).
649+ saved_locals_borrowed_across_suspension_points : DenseBitSet < CoroutineSavedLocal > ,
650+
642651 /// For every saved local, the set of other saved locals that are
643652 /// storage-live at the same time as this local. We cannot overlap locals in
644653 /// the layout which have conflicting storage.
@@ -657,6 +666,9 @@ struct LivenessInfo {
657666/// case none exist, the local is considered to be always live.
658667/// - a local has to be stored if it is either directly used after the
659668/// the suspend point, or if it is live and has been previously borrowed.
669+ ///
670+ /// We also compute locals which are "pinned" (borrowed across a suspension point).
671+ /// These are "wrapped" in `UnsafePinned` and have their niche opts disabled.
660672fn locals_live_across_suspend_points < ' tcx > (
661673 tcx : TyCtxt < ' tcx > ,
662674 body : & Body < ' tcx > ,
@@ -686,10 +698,12 @@ fn locals_live_across_suspend_points<'tcx>(
686698 let mut liveness =
687699 MaybeLiveLocals . iterate_to_fixpoint ( tcx, body, Some ( "coroutine" ) ) . into_results_cursor ( body) ;
688700
701+ let mut pinned_locals_cache = IndexVec :: from_fn_n ( |_| None , body. local_decls . len ( ) ) ;
689702 let mut storage_liveness_map = IndexVec :: from_elem ( None , & body. basic_blocks ) ;
690703 let mut live_locals_at_suspension_points = Vec :: new ( ) ;
691704 let mut source_info_at_suspension_points = Vec :: new ( ) ;
692705 let mut live_locals_at_any_suspension_point = DenseBitSet :: new_empty ( body. local_decls . len ( ) ) ;
706+ let mut pinned_locals = DenseBitSet :: new_empty ( body. local_decls . len ( ) ) ;
693707
694708 for ( block, data) in body. basic_blocks . iter_enumerated ( ) {
695709 if let TerminatorKind :: Yield { .. } = data. terminator ( ) . kind {
@@ -729,6 +743,26 @@ fn locals_live_across_suspend_points<'tcx>(
729743
730744 debug ! ( "loc = {:?}, live_locals = {:?}" , loc, live_locals) ;
731745
746+ for live_local in live_locals. iter ( ) {
747+ let pinned_cursor = pinned_locals_cache[ live_local] . get_or_insert_with ( || {
748+ CoroutinePinnedLocals ( live_local)
749+ . iterate_to_fixpoint ( tcx, body, None )
750+ . into_results_cursor ( body)
751+ } ) ;
752+ pinned_cursor. seek_to_block_end ( block) ;
753+ let pinned_by = pinned_cursor. get ( ) ;
754+
755+ if !pinned_by. is_empty ( ) {
756+ assert ! (
757+ !movable,
758+ "local {live_local:?} of movable coro shouldn't be pinned, yet it is pinned by {pinned_by:?}"
759+ ) ;
760+
761+ debug ! ( "{live_local:?} pinned by {pinned_by:?} in {block:?}" ) ;
762+ pinned_locals. insert ( live_local) ;
763+ }
764+ }
765+
732766 // Add the locals live at this suspension point to the set of locals which live across
733767 // any suspension points
734768 live_locals_at_any_suspension_point. union ( & live_locals) ;
@@ -738,7 +772,8 @@ fn locals_live_across_suspend_points<'tcx>(
738772 }
739773 }
740774
741- debug ! ( "live_locals_anywhere = {:?}" , live_locals_at_any_suspension_point) ;
775+ debug ! ( ?pinned_locals) ;
776+ debug ! ( live_locals_anywhere = ?live_locals_at_any_suspension_point) ;
742777 let saved_locals = CoroutineSavedLocals ( live_locals_at_any_suspension_point) ;
743778
744779 // Renumber our liveness_map bitsets to include only the locals we are
@@ -748,6 +783,9 @@ fn locals_live_across_suspend_points<'tcx>(
748783 . map ( |live_here| saved_locals. renumber_bitset ( live_here) )
749784 . collect ( ) ;
750785
786+ let saved_locals_borrowed_across_suspension_points =
787+ saved_locals. renumber_bitset ( & pinned_locals) ;
788+
751789 let storage_conflicts = compute_storage_conflicts (
752790 body,
753791 & saved_locals,
@@ -759,6 +797,7 @@ fn locals_live_across_suspend_points<'tcx>(
759797 saved_locals,
760798 live_locals_at_suspension_points,
761799 source_info_at_suspension_points,
800+ saved_locals_borrowed_across_suspension_points,
762801 storage_conflicts,
763802 storage_liveness : storage_liveness_map,
764803 }
@@ -931,6 +970,7 @@ fn compute_layout<'tcx>(
931970 saved_locals,
932971 live_locals_at_suspension_points,
933972 source_info_at_suspension_points,
973+ saved_locals_borrowed_across_suspension_points,
934974 storage_conflicts,
935975 storage_liveness,
936976 } = liveness;
@@ -960,8 +1000,14 @@ fn compute_layout<'tcx>(
9601000 ClearCrossCrate :: Set ( box LocalInfo :: FakeBorrow ) => true ,
9611001 _ => false ,
9621002 } ;
963- let decl =
964- CoroutineSavedTy { ty : decl. ty , source_info : decl. source_info , ignore_for_traits } ;
1003+ let pinned = saved_locals_borrowed_across_suspension_points. contains ( saved_local) ;
1004+
1005+ let decl = CoroutineSavedTy {
1006+ ty : decl. ty ,
1007+ source_info : decl. source_info ,
1008+ ignore_for_traits,
1009+ pinned,
1010+ } ;
9651011 debug ! ( ?decl) ;
9661012
9671013 tys. push ( decl) ;
0 commit comments