Skip to content

Commit f44b09f

Browse files
committed
Initial UnsafePinned impl [Part 2: Lowering]
1 parent 40dacd5 commit f44b09f

16 files changed

+899
-13
lines changed

compiler/rustc_ast_ir/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContex
1717
pub mod visit;
1818

1919
/// The movability of a coroutine / closure literal:
20-
/// whether a coroutine contains self-references, causing it to be `!Unpin`.
20+
/// whether a coroutine contains self-references, causing it to be `![Unsafe]Unpin`.
2121
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
2222
#[cfg_attr(
2323
feature = "nightly",
2424
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
2525
)]
2626
pub enum Movability {
27-
/// May contain self-references, `!Unpin`.
27+
/// May contain self-references, `!Unpin + !UnsafeUnpin`.
2828
Static,
29-
/// Must not contain self-references, `Unpin`.
29+
/// Must not contain self-references, `Unpin + UnsafeUnpin`.
3030
Movable,
3131
}
3232

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3385,7 +3385,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
33853385
Some(3)
33863386
} else if string.starts_with("static") {
33873387
// `static` is 6 chars long
3388-
// This is used for `!Unpin` coroutines
3388+
// This is used for immovable (self-referential) coroutines
33893389
Some(6)
33903390
} else {
33913391
None

compiler/rustc_middle/src/mir/query.rs

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ pub struct CoroutineSavedTy<'tcx> {
2929
pub source_info: SourceInfo,
3030
/// Whether the local should be ignored for trait bound computations.
3131
pub ignore_for_traits: bool,
32+
/// If this local is borrowed across a suspension point and thus is
33+
/// "wrapped" in `UnsafePinned`. Always false for movable coroutines.
34+
pub pinned: bool,
3235
}
3336

3437
/// The layout of coroutine state.

compiler/rustc_middle/src/ty/context.rs

+1
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,7 @@ bidirectional_lang_item_map! {
734734
TransmuteTrait,
735735
Tuple,
736736
Unpin,
737+
UnsafeUnpin,
737738
Unsize,
738739
// tidy-alphabetical-end
739740
}

compiler/rustc_mir_transform/src/coroutine.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -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.
@@ -690,6 +699,8 @@ fn locals_live_across_suspend_points<'tcx>(
690699
let mut live_locals_at_suspension_points = Vec::new();
691700
let mut source_info_at_suspension_points = Vec::new();
692701
let mut live_locals_at_any_suspension_point = DenseBitSet::new_empty(body.local_decls.len());
702+
let mut locals_borrowed_across_any_suspension_point =
703+
DenseBitSet::new_empty(body.local_decls.len());
693704

694705
for (block, data) in body.basic_blocks.iter_enumerated() {
695706
if let TerminatorKind::Yield { .. } = data.terminator().kind {
@@ -711,6 +722,7 @@ fn locals_live_across_suspend_points<'tcx>(
711722
// of the local, which happens using the `intersect` operation below.
712723
borrowed_locals_cursor.seek_before_primary_effect(loc);
713724
live_locals.union(borrowed_locals_cursor.get());
725+
locals_borrowed_across_any_suspension_point.union(borrowed_locals_cursor.get());
714726
}
715727

716728
// Store the storage liveness for later use so we can restore the state
@@ -726,6 +738,7 @@ fn locals_live_across_suspend_points<'tcx>(
726738

727739
// The coroutine argument is ignored.
728740
live_locals.remove(SELF_ARG);
741+
locals_borrowed_across_any_suspension_point.remove(SELF_ARG);
729742

730743
debug!("loc = {:?}, live_locals = {:?}", loc, live_locals);
731744

@@ -741,13 +754,18 @@ fn locals_live_across_suspend_points<'tcx>(
741754
debug!("live_locals_anywhere = {:?}", live_locals_at_any_suspension_point);
742755
let saved_locals = CoroutineSavedLocals(live_locals_at_any_suspension_point);
743756

757+
debug!("borrowed_locals = {:?}", locals_borrowed_across_any_suspension_point);
758+
744759
// Renumber our liveness_map bitsets to include only the locals we are
745760
// saving.
746761
let live_locals_at_suspension_points = live_locals_at_suspension_points
747762
.iter()
748763
.map(|live_here| saved_locals.renumber_bitset(live_here))
749764
.collect();
750765

766+
let saved_locals_borrowed_across_suspension_points =
767+
saved_locals.renumber_bitset(&locals_borrowed_across_any_suspension_point);
768+
751769
let storage_conflicts = compute_storage_conflicts(
752770
body,
753771
&saved_locals,
@@ -759,6 +777,7 @@ fn locals_live_across_suspend_points<'tcx>(
759777
saved_locals,
760778
live_locals_at_suspension_points,
761779
source_info_at_suspension_points,
780+
saved_locals_borrowed_across_suspension_points,
762781
storage_conflicts,
763782
storage_liveness: storage_liveness_map,
764783
}
@@ -931,6 +950,7 @@ fn compute_layout<'tcx>(
931950
saved_locals,
932951
live_locals_at_suspension_points,
933952
source_info_at_suspension_points,
953+
saved_locals_borrowed_across_suspension_points,
934954
storage_conflicts,
935955
storage_liveness,
936956
} = liveness;
@@ -960,8 +980,14 @@ fn compute_layout<'tcx>(
960980
ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true,
961981
_ => false,
962982
};
963-
let decl =
964-
CoroutineSavedTy { ty: decl.ty, source_info: decl.source_info, ignore_for_traits };
983+
let pinned = saved_locals_borrowed_across_suspension_points.contains(saved_local);
984+
985+
let decl = CoroutineSavedTy {
986+
ty: decl.ty,
987+
source_info: decl.source_info,
988+
ignore_for_traits,
989+
pinned,
990+
};
965991
debug!(?decl);
966992

967993
tys.push(decl);

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1148,13 +1148,16 @@ where
11481148

11491149
ty::Infer(_) | ty::Bound(_, _) => panic!("unexpected type `{self_ty:?}`"),
11501150

1151-
// Coroutines have one special built-in candidate, `Unpin`, which
1152-
// takes precedence over the structural auto trait candidate being
1153-
// assembled.
1151+
// Coroutines have two special built-in candidates, `Unpin` and `UnsafeUnpin`.
1152+
// These take precedence over the structural auto trait candidate being assembled.
11541153
ty::Coroutine(def_id, _)
1155-
if self.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::Unpin) =>
1154+
if matches!(
1155+
self.cx().as_lang_item(goal.predicate.def_id()),
1156+
Some(TraitSolverLangItem::Unpin | TraitSolverLangItem::UnsafeUnpin)
1157+
) =>
11561158
{
11571159
match self.cx().coroutine_movability(def_id) {
1160+
// immovable coroutines are *never* [Unsafe]Unpin
11581161
Movability::Static => Some(Err(NoSolution)),
11591162
Movability::Movable => Some(
11601163
self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -752,15 +752,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
752752
candidates.ambiguous = true;
753753
}
754754
ty::Coroutine(coroutine_def_id, _)
755-
if self.tcx().is_lang_item(def_id, LangItem::Unpin) =>
755+
if self.tcx().is_lang_item(def_id, LangItem::Unpin)
756+
|| self.tcx().is_lang_item(def_id, LangItem::UnsafeUnpin) =>
756757
{
757758
match self.tcx().coroutine_movability(coroutine_def_id) {
758759
hir::Movability::Static => {
759-
// Immovable coroutines are never `Unpin`, so
760+
// Immovable coroutines are never `[Unsafe]Unpin`, so
760761
// suppress the normal auto-impl candidate for it.
761762
}
762763
hir::Movability::Movable => {
763-
// Movable coroutines are always `Unpin`, so add an
764+
// Movable coroutines are always `[Unsafe]Unpin`, so add an
764765
// unconditional builtin candidate.
765766
candidates.vec.push(BuiltinCandidate { has_nested: false });
766767
}

compiler/rustc_type_ir/src/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub enum TraitSolverLangItem {
3838
TransmuteTrait,
3939
Tuple,
4040
Unpin,
41+
UnsafeUnpin,
4142
Unsize,
4243
// tidy-alphabetical-end
4344
}

tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
scope: scope[0],
2222
},
2323
ignore_for_traits: false,
24+
pinned: true,
2425
},
2526
_1: CoroutineSavedTy {
2627
ty: Coroutine(
@@ -42,6 +43,7 @@
4243
scope: scope[0],
4344
},
4445
ignore_for_traits: false,
46+
pinned: true,
4547
},
4648
},
4749
variant_fields: {

0 commit comments

Comments
 (0)