@@ -69,8 +69,8 @@ use rustc_middle::ty::{
69
69
} ;
70
70
use rustc_middle:: { bug, span_bug} ;
71
71
use 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,
74
74
} ;
75
75
use rustc_mir_dataflow:: { Analysis , Results , ResultsVisitor } ;
76
76
use rustc_span:: def_id:: { DefId , LocalDefId } ;
@@ -639,6 +639,15 @@ struct LivenessInfo {
639
639
/// Parallel vec to the above with SourceInfo for each yield terminator.
640
640
source_info_at_suspension_points : Vec < SourceInfo > ,
641
641
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
+
642
651
/// For every saved local, the set of other saved locals that are
643
652
/// storage-live at the same time as this local. We cannot overlap locals in
644
653
/// the layout which have conflicting storage.
@@ -657,6 +666,9 @@ struct LivenessInfo {
657
666
/// case none exist, the local is considered to be always live.
658
667
/// - a local has to be stored if it is either directly used after the
659
668
/// 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.
660
672
fn locals_live_across_suspend_points < ' tcx > (
661
673
tcx : TyCtxt < ' tcx > ,
662
674
body : & Body < ' tcx > ,
@@ -686,10 +698,12 @@ fn locals_live_across_suspend_points<'tcx>(
686
698
let mut liveness =
687
699
MaybeLiveLocals . iterate_to_fixpoint ( tcx, body, Some ( "coroutine" ) ) . into_results_cursor ( body) ;
688
700
701
+ let mut pinned_locals_cache = IndexVec :: from_fn_n ( |_| None , body. local_decls . len ( ) ) ;
689
702
let mut storage_liveness_map = IndexVec :: from_elem ( None , & body. basic_blocks ) ;
690
703
let mut live_locals_at_suspension_points = Vec :: new ( ) ;
691
704
let mut source_info_at_suspension_points = Vec :: new ( ) ;
692
705
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 ( ) ) ;
693
707
694
708
for ( block, data) in body. basic_blocks . iter_enumerated ( ) {
695
709
if let TerminatorKind :: Yield { .. } = data. terminator ( ) . kind {
@@ -729,6 +743,26 @@ fn locals_live_across_suspend_points<'tcx>(
729
743
730
744
debug ! ( "loc = {:?}, live_locals = {:?}" , loc, live_locals) ;
731
745
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
+
732
766
// Add the locals live at this suspension point to the set of locals which live across
733
767
// any suspension points
734
768
live_locals_at_any_suspension_point. union ( & live_locals) ;
@@ -738,7 +772,8 @@ fn locals_live_across_suspend_points<'tcx>(
738
772
}
739
773
}
740
774
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) ;
742
777
let saved_locals = CoroutineSavedLocals ( live_locals_at_any_suspension_point) ;
743
778
744
779
// Renumber our liveness_map bitsets to include only the locals we are
@@ -748,6 +783,9 @@ fn locals_live_across_suspend_points<'tcx>(
748
783
. map ( |live_here| saved_locals. renumber_bitset ( live_here) )
749
784
. collect ( ) ;
750
785
786
+ let saved_locals_borrowed_across_suspension_points =
787
+ saved_locals. renumber_bitset ( & pinned_locals) ;
788
+
751
789
let storage_conflicts = compute_storage_conflicts (
752
790
body,
753
791
& saved_locals,
@@ -759,6 +797,7 @@ fn locals_live_across_suspend_points<'tcx>(
759
797
saved_locals,
760
798
live_locals_at_suspension_points,
761
799
source_info_at_suspension_points,
800
+ saved_locals_borrowed_across_suspension_points,
762
801
storage_conflicts,
763
802
storage_liveness : storage_liveness_map,
764
803
}
@@ -931,6 +970,7 @@ fn compute_layout<'tcx>(
931
970
saved_locals,
932
971
live_locals_at_suspension_points,
933
972
source_info_at_suspension_points,
973
+ saved_locals_borrowed_across_suspension_points,
934
974
storage_conflicts,
935
975
storage_liveness,
936
976
} = liveness;
@@ -960,8 +1000,14 @@ fn compute_layout<'tcx>(
960
1000
ClearCrossCrate :: Set ( box LocalInfo :: FakeBorrow ) => true ,
961
1001
_ => false ,
962
1002
} ;
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
+ } ;
965
1011
debug ! ( ?decl) ;
966
1012
967
1013
tys. push ( decl) ;
0 commit comments