Skip to content

Commit dc8c1ca

Browse files
Force move async-closures that are FnOnce to make their inner coroutines also move
1 parent ca76c7a commit dc8c1ca

File tree

7 files changed

+47
-73
lines changed

7 files changed

+47
-73
lines changed

compiler/rustc_hir_typeck/src/upvar.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
166166
span: Span,
167167
body_id: hir::BodyId,
168168
body: &'tcx hir::Body<'tcx>,
169-
capture_clause: hir::CaptureBy,
169+
mut capture_clause: hir::CaptureBy,
170170
) {
171171
// Extract the type of the closure.
172172
let ty = self.node_ty(closure_hir_id);
@@ -259,6 +259,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
259259
)
260260
.consume_body(body);
261261

262+
if let UpvarArgs::Coroutine(..) = args
263+
&& let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) =
264+
self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind")
265+
&& let parent_hir_id =
266+
self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id))
267+
&& let parent_ty = self.node_ty(parent_hir_id)
268+
&& parent_ty.is_coroutine_closure()
269+
&& let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty)
270+
{
271+
capture_clause = self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause;
272+
}
273+
262274
debug!(
263275
"For closure={:?}, capture_information={:#?}",
264276
closure_def_id, delegate.capture_information

compiler/rustc_mir_transform/src/coroutine/by_move_body.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,17 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
9191
return;
9292
}
9393

94-
let ty::Coroutine(_, coroutine_args) = *coroutine_ty.kind() else { bug!("{body:#?}") };
95-
// We don't need to generate a by-move coroutine if the kind of the coroutine is
96-
// already `FnOnce` -- that means that any upvars that the closure consumes have
97-
// already been taken by-value.
98-
let coroutine_kind = coroutine_args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap();
99-
if coroutine_kind == ty::ClosureKind::FnOnce {
94+
// We don't need to generate a by-move coroutine if the coroutine body was
95+
// produced by the `CoroutineKindShim`, since it's already by-move.
96+
if matches!(body.source.instance, ty::InstanceDef::CoroutineKindShim { .. }) {
10097
return;
10198
}
10299

100+
let ty::Coroutine(_, args) = *coroutine_ty.kind() else { bug!("{body:#?}") };
101+
let args = args.as_coroutine();
102+
103+
let coroutine_kind = args.kind_ty().to_opt_closure_kind().unwrap();
104+
103105
let parent_def_id = tcx.local_parent(coroutine_def_id);
104106
let ty::CoroutineClosure(_, parent_args) =
105107
*tcx.type_of(parent_def_id).instantiate_identity().kind()
@@ -128,6 +130,12 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
128130
// the outer closure body -- we need to change the coroutine to take the
129131
// upvar by value.
130132
if coroutine_capture.is_by_ref() && !parent_capture.is_by_ref() {
133+
assert_ne!(
134+
coroutine_kind,
135+
ty::ClosureKind::FnOnce,
136+
"`async FnOnce` closures return coroutines that capture from \
137+
their body; it will always result in a borrowck error!"
138+
);
131139
by_ref_fields.insert(FieldIdx::from_usize(num_args + idx));
132140
}
133141

src/tools/miri/tests/pass/async-closure-captures.stderr

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Hello(0)
2+
Hello(0)
3+
Hello(1)
4+
Hello(1)
5+
Hello(2)
6+
Hello(3)
7+
Hello(3)
8+
Hello(4)
9+
Hello(4)
10+
Hello(5)
11+
Hello(6)
12+
Hello(7)
13+
Hello(8)
14+
Hello(9)

tests/ui/async-await/async-closures/captures.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//@ aux-build:block-on.rs
22
//@ edition:2021
3-
4-
3+
//@ run-pass
4+
//@ check-run-results
55

66
// Same as miri's `tests/pass/async-closure-captures.rs`, keep in sync
77

@@ -104,14 +104,12 @@ async fn async_main() {
104104
let x = Hello(8);
105105
let c = force_fnonce(async || {
106106
println!("{x:?}");
107-
//~^ ERROR `x` does not live long enough
108107
});
109108
call_once(c).await;
110109

111110
let x = &Hello(9);
112111
let c = force_fnonce(async || {
113112
println!("{x:?}");
114-
//~^ ERROR `x` does not live long enough
115113
});
116114
call_once(c).await;
117115
}

tests/ui/async-await/async-closures/captures.run.stdout

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ Hello(3)
88
Hello(4)
99
Hello(4)
1010
Hello(5)
11+
Hello(6)
12+
Hello(7)
13+
Hello(8)
14+
Hello(9)

tests/ui/async-await/async-closures/captures.stderr

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)