Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 03ef470

Browse files
committedApr 16, 2025··
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

Lines changed: 3 additions & 3 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 28 additions & 2 deletions
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

Lines changed: 18 additions & 3 deletions
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

Lines changed: 13 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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: {

‎tests/mir-opt/coroutine_pinned_fields.async_block-{closure#0}.coroutine_pre-elab.0.mir

Lines changed: 421 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// MIR for `borrow_not_held_across_yield::{closure#0}` 0 coroutine_pre-elab
2+
/* coroutine_layout = CoroutineLayout {
3+
field_tys: {
4+
_0: CoroutineSavedTy {
5+
ty: i32,
6+
source_info: SourceInfo {
7+
span: $DIR/coroutine_pinned_fields.rs:32:13: 32:18 (#0),
8+
scope: scope[0],
9+
},
10+
ignore_for_traits: false,
11+
pinned: true,
12+
},
13+
},
14+
variant_fields: {
15+
Unresumed(0): [],
16+
Returned (1): [],
17+
Panicked (2): [],
18+
Suspend0 (3): [_0],
19+
},
20+
storage_conflicts: BitMatrix(1x1) {
21+
(_0, _0),
22+
},
23+
} */
24+
25+
fn borrow_not_held_across_yield::{closure#0}(_1: {static coroutine@$DIR/coroutine_pinned_fields.rs:30:5: 30:14}, _2: ()) -> CoroutineState<(), i32> {
26+
let mut _0: std::ops::CoroutineState<(), i32>;
27+
let mut _3: i32;
28+
let _4: ();
29+
let _6: ();
30+
let mut _7: ();
31+
let mut _8: i32;
32+
let mut _9: ();
33+
scope 1 {
34+
debug x => ((_1 as variant#3).0: i32);
35+
let _5: &mut i32;
36+
scope 2 {
37+
debug y => _5;
38+
}
39+
}
40+
41+
bb0: {
42+
_9 = move _2;
43+
nop;
44+
((_1 as variant#3).0: i32) = const 9_i32;
45+
StorageLive(_4);
46+
StorageLive(_5);
47+
_5 = &mut ((_1 as variant#3).0: i32);
48+
(*_5) = Add(copy (*_5), const 5_i32);
49+
_4 = const ();
50+
StorageDead(_5);
51+
StorageDead(_4);
52+
StorageLive(_6);
53+
StorageLive(_7);
54+
_7 = ();
55+
_0 = CoroutineState::<(), i32>::Yielded(move _7);
56+
StorageDead(_6);
57+
StorageDead(_7);
58+
discriminant(_1) = 3;
59+
return;
60+
}
61+
62+
bb1: {
63+
StorageDead(_7);
64+
StorageDead(_6);
65+
_8 = copy ((_1 as variant#3).0: i32);
66+
nop;
67+
drop(_1) -> [return: bb2, unwind unreachable];
68+
}
69+
70+
bb2: {
71+
_0 = CoroutineState::<(), i32>::Complete(move _8);
72+
discriminant(_1) = 1;
73+
return;
74+
}
75+
76+
bb3: {
77+
StorageDead(_7);
78+
StorageDead(_6);
79+
nop;
80+
drop(_1) -> [return: bb4, unwind unreachable];
81+
}
82+
83+
bb4: {
84+
coroutine_drop;
85+
}
86+
87+
bb5: {
88+
return;
89+
}
90+
91+
bb6: {
92+
drop(_1) -> [return: bb5, unwind continue];
93+
}
94+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// MIR for `movable_is_never_pinned::{closure#0}` 0 coroutine_pre-elab
2+
/* coroutine_layout = CoroutineLayout {
3+
field_tys: {
4+
_0: CoroutineSavedTy {
5+
ty: i32,
6+
source_info: SourceInfo {
7+
span: $DIR/coroutine_pinned_fields.rs:60:13: 60:20 (#0),
8+
scope: scope[0],
9+
},
10+
ignore_for_traits: false,
11+
pinned: false,
12+
},
13+
},
14+
variant_fields: {
15+
Unresumed(0): [],
16+
Returned (1): [],
17+
Panicked (2): [],
18+
Suspend0 (3): [_0],
19+
Suspend1 (4): [_0],
20+
},
21+
storage_conflicts: BitMatrix(1x1) {
22+
(_0, _0),
23+
},
24+
} */
25+
26+
fn movable_is_never_pinned::{closure#0}(_1: {coroutine@$DIR/coroutine_pinned_fields.rs:59:5: 59:7}, _2: ()) -> CoroutineState<(), i32> {
27+
let mut _0: std::ops::CoroutineState<(), i32>;
28+
let mut _3: i32;
29+
let _4: ();
30+
let mut _5: ();
31+
let _6: ();
32+
let mut _7: ();
33+
let mut _8: i32;
34+
let mut _9: ();
35+
scope 1 {
36+
debug bar => ((_1 as variant#4).0: i32);
37+
}
38+
39+
bb0: {
40+
_9 = move _2;
41+
nop;
42+
((_1 as variant#4).0: i32) = const 29_i32;
43+
StorageLive(_4);
44+
StorageLive(_5);
45+
_5 = ();
46+
_0 = CoroutineState::<(), i32>::Yielded(move _5);
47+
StorageDead(_4);
48+
StorageDead(_5);
49+
discriminant(_1) = 3;
50+
return;
51+
}
52+
53+
bb1: {
54+
StorageDead(_5);
55+
StorageDead(_4);
56+
((_1 as variant#4).0: i32) = Add(copy ((_1 as variant#4).0: i32), const 2_i32);
57+
StorageLive(_6);
58+
StorageLive(_7);
59+
_7 = ();
60+
_0 = CoroutineState::<(), i32>::Yielded(move _7);
61+
StorageDead(_6);
62+
StorageDead(_7);
63+
discriminant(_1) = 4;
64+
return;
65+
}
66+
67+
bb2: {
68+
StorageDead(_7);
69+
StorageDead(_6);
70+
_8 = copy ((_1 as variant#4).0: i32);
71+
nop;
72+
drop(_1) -> [return: bb3, unwind unreachable];
73+
}
74+
75+
bb3: {
76+
_0 = CoroutineState::<(), i32>::Complete(move _8);
77+
discriminant(_1) = 1;
78+
return;
79+
}
80+
81+
bb4: {
82+
StorageDead(_7);
83+
StorageDead(_6);
84+
goto -> bb6;
85+
}
86+
87+
bb5: {
88+
StorageDead(_5);
89+
StorageDead(_4);
90+
goto -> bb6;
91+
}
92+
93+
bb6: {
94+
nop;
95+
drop(_1) -> [return: bb7, unwind unreachable];
96+
}
97+
98+
bb7: {
99+
coroutine_drop;
100+
}
101+
102+
bb8: {
103+
return;
104+
}
105+
106+
bb9: {
107+
drop(_1) -> [return: bb8, unwind continue];
108+
}
109+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// skip-filecheck
2+
3+
//! Ensures pinned coroutine fields are marked correctly
4+
5+
//@ compile-flags: -C panic=abort
6+
//@ edition: 2024
7+
8+
#![crate_type = "lib"]
9+
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
10+
11+
use std::future::Future;
12+
use std::ops::Coroutine;
13+
14+
// EMIT_MIR coroutine_pinned_fields.use_borrow_across_yield-{closure#0}.coroutine_pre-elab.0.mir
15+
fn use_borrow_across_yield() -> impl Coroutine {
16+
#[coroutine]
17+
static || {
18+
let mut a = 19; // pinned
19+
let b = &mut a; // not pinned
20+
yield;
21+
*b = 23;
22+
yield;
23+
a
24+
}
25+
}
26+
27+
// EMIT_MIR coroutine_pinned_fields.borrow_not_held_across_yield-{closure#0}.coroutine_pre-elab.0.mir
28+
fn borrow_not_held_across_yield() -> impl Coroutine {
29+
#[coroutine]
30+
static || {
31+
// NOTE: unfortunately, this field is currently marked as pinned even though it shouldn't be
32+
let mut x = 9; // not pinned
33+
{
34+
let y = &mut x; // not stored
35+
*y += 5;
36+
}
37+
yield;
38+
x
39+
}
40+
}
41+
42+
async fn nop() {}
43+
44+
// EMIT_MIR coroutine_pinned_fields.async_block-{closure#0}.coroutine_pre-elab.0.mir
45+
fn async_block() -> impl Future {
46+
async {
47+
let mut x = 9; // pinned
48+
let y = &mut x; // not pinned
49+
nop().await;
50+
*y += 1;
51+
nop().await;
52+
x
53+
}
54+
}
55+
56+
// EMIT_MIR coroutine_pinned_fields.movable_is_never_pinned-{closure#0}.coroutine_pre-elab.0.mir
57+
fn movable_is_never_pinned() -> impl Coroutine {
58+
#[coroutine]
59+
|| {
60+
let mut bar = 29;
61+
yield;
62+
bar += 2;
63+
yield;
64+
bar
65+
}
66+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// MIR for `use_borrow_across_yield::{closure#0}` 0 coroutine_pre-elab
2+
/* coroutine_layout = CoroutineLayout {
3+
field_tys: {
4+
_0: CoroutineSavedTy {
5+
ty: i32,
6+
source_info: SourceInfo {
7+
span: $DIR/coroutine_pinned_fields.rs:18:13: 18:18 (#0),
8+
scope: scope[0],
9+
},
10+
ignore_for_traits: false,
11+
pinned: true,
12+
},
13+
_1: CoroutineSavedTy {
14+
ty: &'{erased} mut i32,
15+
source_info: SourceInfo {
16+
span: $DIR/coroutine_pinned_fields.rs:19:13: 19:14 (#0),
17+
scope: scope[1],
18+
},
19+
ignore_for_traits: false,
20+
pinned: false,
21+
},
22+
},
23+
variant_fields: {
24+
Unresumed(0): [],
25+
Returned (1): [],
26+
Panicked (2): [],
27+
Suspend0 (3): [_0, _1],
28+
Suspend1 (4): [_0],
29+
},
30+
storage_conflicts: BitMatrix(2x2) {
31+
(_0, _0),
32+
(_0, _1),
33+
(_1, _0),
34+
(_1, _1),
35+
},
36+
} */
37+
38+
fn use_borrow_across_yield::{closure#0}(_1: {static coroutine@$DIR/coroutine_pinned_fields.rs:17:5: 17:14}, _2: ()) -> CoroutineState<(), i32> {
39+
let mut _0: std::ops::CoroutineState<(), i32>;
40+
let mut _3: i32;
41+
let _5: ();
42+
let mut _6: ();
43+
let _7: ();
44+
let mut _8: ();
45+
let mut _9: i32;
46+
let mut _10: ();
47+
scope 1 {
48+
debug a => ((_1 as variant#4).0: i32);
49+
let _4: &mut i32;
50+
scope 2 {
51+
debug b => ((_1 as variant#3).1: &mut i32);
52+
}
53+
}
54+
55+
bb0: {
56+
_10 = move _2;
57+
nop;
58+
((_1 as variant#4).0: i32) = const 19_i32;
59+
nop;
60+
((_1 as variant#3).1: &mut i32) = &mut ((_1 as variant#4).0: i32);
61+
StorageLive(_5);
62+
StorageLive(_6);
63+
_6 = ();
64+
_0 = CoroutineState::<(), i32>::Yielded(move _6);
65+
StorageDead(_5);
66+
StorageDead(_6);
67+
discriminant(_1) = 3;
68+
return;
69+
}
70+
71+
bb1: {
72+
StorageDead(_6);
73+
StorageDead(_5);
74+
(*((_1 as variant#3).1: &mut i32)) = const 23_i32;
75+
StorageLive(_7);
76+
StorageLive(_8);
77+
_8 = ();
78+
_0 = CoroutineState::<(), i32>::Yielded(move _8);
79+
StorageDead(_7);
80+
StorageDead(_8);
81+
discriminant(_1) = 4;
82+
return;
83+
}
84+
85+
bb2: {
86+
StorageDead(_8);
87+
StorageDead(_7);
88+
_9 = copy ((_1 as variant#4).0: i32);
89+
nop;
90+
nop;
91+
drop(_1) -> [return: bb3, unwind unreachable];
92+
}
93+
94+
bb3: {
95+
_0 = CoroutineState::<(), i32>::Complete(move _9);
96+
discriminant(_1) = 1;
97+
return;
98+
}
99+
100+
bb4: {
101+
StorageDead(_8);
102+
StorageDead(_7);
103+
goto -> bb6;
104+
}
105+
106+
bb5: {
107+
StorageDead(_6);
108+
StorageDead(_5);
109+
goto -> bb6;
110+
}
111+
112+
bb6: {
113+
nop;
114+
nop;
115+
drop(_1) -> [return: bb7, unwind unreachable];
116+
}
117+
118+
bb7: {
119+
coroutine_drop;
120+
}
121+
122+
bb8: {
123+
return;
124+
}
125+
126+
bb9: {
127+
drop(_1) -> [return: bb8, unwind continue];
128+
}
129+
}

‎tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
scope: scope[0],
99
},
1010
ignore_for_traits: false,
11+
pinned: false,
1112
},
1213
},
1314
variant_fields: {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//@ run-pass
2+
3+
#![feature(coroutines)]
4+
#![feature(coroutine_trait)]
5+
#![feature(stmt_expr_attributes)]
6+
7+
use std::ops::{Coroutine, CoroutineState};
8+
use std::pin::pin;
9+
10+
fn coro() {
11+
let c = #[coroutine]
12+
static || {
13+
let mut a = 19;
14+
let b = &mut a;
15+
yield;
16+
*b = 23;
17+
yield;
18+
a
19+
};
20+
21+
let mut c = pin!(c);
22+
assert_eq!(c.as_mut().resume(()), CoroutineState::Yielded(()));
23+
assert_eq!(c.as_mut().resume(()), CoroutineState::Yielded(()));
24+
assert_eq!(c.as_mut().resume(()), CoroutineState::Complete(23));
25+
}
26+
27+
fn main() {
28+
coro();
29+
}

0 commit comments

Comments
 (0)
Please sign in to comment.