Skip to content

Commit 03ef470

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

18 files changed

+932
-9
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

+5
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
335335
self.coroutine_hidden_types(def_id)
336336
}
337337

338+
fn coroutine_has_pinned_fields(self, def_id: DefId) -> Option<bool> {
339+
self.coroutine_has_pinned_fields(def_id)
340+
}
341+
338342
fn fn_sig(self, def_id: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> {
339343
self.fn_sig(def_id)
340344
}
@@ -734,6 +738,7 @@ bidirectional_lang_item_map! {
734738
TransmuteTrait,
735739
Tuple,
736740
Unpin,
741+
UnsafeUnpin,
737742
Unsize,
738743
// tidy-alphabetical-end
739744
}

compiler/rustc_middle/src/ty/util.rs

+7
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,13 @@ impl<'tcx> TyCtxt<'tcx> {
790790
))
791791
}
792792

793+
/// True if the given coroutine has any pinned fields.
794+
/// `None` if the coroutine is tainted by errors.
795+
pub fn coroutine_has_pinned_fields(self, def_id: DefId) -> Option<bool> {
796+
self.mir_coroutine_witnesses(def_id)
797+
.map(|layout| layout.field_tys.iter().any(|ty| ty.pinned))
798+
}
799+
793800
/// Expands the given impl trait type, stopping if the type is recursive.
794801
#[instrument(skip(self), level = "debug", ret)]
795802
pub fn try_expand_impl_trait_type(

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

+18-3
Original file line numberDiff line numberDiff line change
@@ -1148,13 +1148,13 @@ 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, _)
11551154
if self.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::Unpin) =>
11561155
{
11571156
match self.cx().coroutine_movability(def_id) {
1157+
// immovable coroutines are *never* Unpin
11581158
Movability::Static => Some(Err(NoSolution)),
11591159
Movability::Movable => Some(
11601160
self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
@@ -1163,6 +1163,21 @@ where
11631163
),
11641164
}
11651165
}
1166+
ty::Coroutine(def_id, _)
1167+
if self
1168+
.cx()
1169+
.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::UnsafeUnpin) =>
1170+
{
1171+
match self.cx().coroutine_has_pinned_fields(def_id) {
1172+
Some(true) => Some(Err(NoSolution)),
1173+
Some(false) => Some(
1174+
self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
1175+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
1176+
}),
1177+
),
1178+
None => None, // coro tainted by errors
1179+
}
1180+
}
11661181

11671182
// If we still have an alias here, it must be rigid. For opaques, it's always
11681183
// okay to consider auto traits because that'll reveal its hidden type. For

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

+13
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
751751
// The auto impl might apply; we don't know.
752752
candidates.ambiguous = true;
753753
}
754+
754755
ty::Coroutine(coroutine_def_id, _)
755756
if self.tcx().is_lang_item(def_id, LangItem::Unpin) =>
756757
{
@@ -766,6 +767,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
766767
}
767768
}
768769
}
770+
ty::Coroutine(coroutine_def_id, _)
771+
if self.tcx().is_lang_item(def_id, LangItem::UnsafeUnpin) =>
772+
{
773+
match self.tcx().coroutine_has_pinned_fields(coroutine_def_id) {
774+
Some(true) => {}
775+
Some(false) => {
776+
candidates.vec.push(BuiltinCandidate { has_nested: false });
777+
}
778+
// coro tainted by errors
779+
None => candidates.vec.push(AutoImplCandidate),
780+
}
781+
}
769782

770783
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
771784
bug!(

compiler/rustc_type_ir/src/interner.rs

+2
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ pub trait Interner:
205205
def_id: Self::DefId,
206206
) -> ty::EarlyBinder<Self, ty::Binder<Self, Self::Tys>>;
207207

208+
fn coroutine_has_pinned_fields(self, def_id: Self::DefId) -> Option<bool>;
209+
208210
fn fn_sig(
209211
self,
210212
def_id: Self::DefId,

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)