@@ -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,27 @@ 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 mut pinned_by = pinned_cursor. get ( ) . clone ( ) ;
754+ pinned_by. intersect ( & live_locals) ;
755+
756+ if !pinned_by. is_empty ( ) {
757+ assert ! (
758+ !movable,
759+ "local {live_local:?} of movable coro shouldn't be pinned, yet it is pinned by {pinned_by:?}"
760+ ) ;
761+
762+ debug ! ( "{live_local:?} pinned by {pinned_by:?} in {block:?}" ) ;
763+ pinned_locals. insert ( live_local) ;
764+ }
765+ }
766+
732767 // Add the locals live at this suspension point to the set of locals which live across
733768 // any suspension points
734769 live_locals_at_any_suspension_point. union ( & live_locals) ;
@@ -738,7 +773,8 @@ fn locals_live_across_suspend_points<'tcx>(
738773 }
739774 }
740775
741- debug ! ( "live_locals_anywhere = {:?}" , live_locals_at_any_suspension_point) ;
776+ debug ! ( ?pinned_locals) ;
777+ debug ! ( live_locals_anywhere = ?live_locals_at_any_suspension_point) ;
742778 let saved_locals = CoroutineSavedLocals ( live_locals_at_any_suspension_point) ;
743779
744780 // Renumber our liveness_map bitsets to include only the locals we are
@@ -748,6 +784,9 @@ fn locals_live_across_suspend_points<'tcx>(
748784 . map ( |live_here| saved_locals. renumber_bitset ( live_here) )
749785 . collect ( ) ;
750786
787+ let saved_locals_borrowed_across_suspension_points =
788+ saved_locals. renumber_bitset ( & pinned_locals) ;
789+
751790 let storage_conflicts = compute_storage_conflicts (
752791 body,
753792 & saved_locals,
@@ -759,6 +798,7 @@ fn locals_live_across_suspend_points<'tcx>(
759798 saved_locals,
760799 live_locals_at_suspension_points,
761800 source_info_at_suspension_points,
801+ saved_locals_borrowed_across_suspension_points,
762802 storage_conflicts,
763803 storage_liveness : storage_liveness_map,
764804 }
@@ -931,6 +971,7 @@ fn compute_layout<'tcx>(
931971 saved_locals,
932972 live_locals_at_suspension_points,
933973 source_info_at_suspension_points,
974+ saved_locals_borrowed_across_suspension_points,
934975 storage_conflicts,
935976 storage_liveness,
936977 } = liveness;
@@ -960,8 +1001,14 @@ fn compute_layout<'tcx>(
9601001 ClearCrossCrate :: Set ( box LocalInfo :: FakeBorrow ) => true ,
9611002 _ => false ,
9621003 } ;
963- let decl =
964- CoroutineSavedTy { ty : decl. ty , source_info : decl. source_info , ignore_for_traits } ;
1004+ let pinned = saved_locals_borrowed_across_suspension_points. contains ( saved_local) ;
1005+
1006+ let decl = CoroutineSavedTy {
1007+ ty : decl. ty ,
1008+ source_info : decl. source_info ,
1009+ ignore_for_traits,
1010+ pinned,
1011+ } ;
9651012 debug ! ( ?decl) ;
9661013
9671014 tys. push ( decl) ;
0 commit comments