@@ -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,27 @@ 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 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
+
732
767
// Add the locals live at this suspension point to the set of locals which live across
733
768
// any suspension points
734
769
live_locals_at_any_suspension_point. union ( & live_locals) ;
@@ -738,7 +773,8 @@ fn locals_live_across_suspend_points<'tcx>(
738
773
}
739
774
}
740
775
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) ;
742
778
let saved_locals = CoroutineSavedLocals ( live_locals_at_any_suspension_point) ;
743
779
744
780
// Renumber our liveness_map bitsets to include only the locals we are
@@ -748,6 +784,9 @@ fn locals_live_across_suspend_points<'tcx>(
748
784
. map ( |live_here| saved_locals. renumber_bitset ( live_here) )
749
785
. collect ( ) ;
750
786
787
+ let saved_locals_borrowed_across_suspension_points =
788
+ saved_locals. renumber_bitset ( & pinned_locals) ;
789
+
751
790
let storage_conflicts = compute_storage_conflicts (
752
791
body,
753
792
& saved_locals,
@@ -759,6 +798,7 @@ fn locals_live_across_suspend_points<'tcx>(
759
798
saved_locals,
760
799
live_locals_at_suspension_points,
761
800
source_info_at_suspension_points,
801
+ saved_locals_borrowed_across_suspension_points,
762
802
storage_conflicts,
763
803
storage_liveness : storage_liveness_map,
764
804
}
@@ -931,6 +971,7 @@ fn compute_layout<'tcx>(
931
971
saved_locals,
932
972
live_locals_at_suspension_points,
933
973
source_info_at_suspension_points,
974
+ saved_locals_borrowed_across_suspension_points,
934
975
storage_conflicts,
935
976
storage_liveness,
936
977
} = liveness;
@@ -960,8 +1001,14 @@ fn compute_layout<'tcx>(
960
1001
ClearCrossCrate :: Set ( box LocalInfo :: FakeBorrow ) => true ,
961
1002
_ => false ,
962
1003
} ;
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
+ } ;
965
1012
debug ! ( ?decl) ;
966
1013
967
1014
tys. push ( decl) ;
0 commit comments