Skip to content

Commit 81ba557

Browse files
authored
Rollup merge of #138514 - compiler-errors:fake-borrow-ref-to-value, r=oli-obk
Remove fake borrows of refs that are converted into non-refs in `MakeByMoveBody` Remove fake borrows of closure captures if that capture has been replaced with a by-move version of that capture. For example, given an async closure that looks like: ``` let f: Foo; let c = async move || { match f { ... } }; ``` ... in this pair of coroutine-closure + coroutine, we capture `Foo` in the parent and `&Foo` in the child. We will emit two fake borrows like: ``` _2 = &fake shallow (*(_1.0: &Foo)); _3 = &fake shallow (_1.0: &Foo); ``` However, since the by-move-body transform is responsible for replacing `_1.0: &Foo` with `_1.0: Foo` (since the `AsyncFnOnce` coroutine will own `Foo` by value), that makes the second fake borrow obsolete since we never have an upvar of type `&Foo`, and we should replace it with a `nop`. As a side-note, we don't actually even care about fake borrows here at all since they're fully a MIR borrowck artifact, and we don't need to borrowck by-move MIR bodies. But it's best to preserve as much as we can between these two bodies :) Fixes #138501 r? oli-obk
2 parents 232ec5c + e54bde6 commit 81ba557

4 files changed

+202
-2
lines changed

Diff for: compiler/rustc_mir_transform/src/coroutine/by_move_body.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -178,17 +178,18 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
178178
),
179179
};
180180

181-
(
181+
Some((
182182
FieldIdx::from_usize(child_field_idx + num_args),
183183
(
184184
FieldIdx::from_usize(parent_field_idx + num_args),
185185
parent_capture_ty,
186186
peel_deref,
187187
child_precise_captures,
188188
),
189-
)
189+
))
190190
},
191191
)
192+
.flatten()
192193
.collect();
193194

194195
if coroutine_kind == ty::ClosureKind::FnOnce {
@@ -312,10 +313,46 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
312313
self.super_place(place, context, location);
313314
}
314315

316+
fn visit_statement(&mut self, statement: &mut mir::Statement<'tcx>, location: mir::Location) {
317+
// Remove fake borrows of closure captures if that capture has been
318+
// replaced with a by-move version of that capture.
319+
//
320+
// For example, imagine we capture `Foo` in the parent and `&Foo`
321+
// in the child. We will emit two fake borrows like:
322+
//
323+
// ```
324+
// _2 = &fake shallow (*(_1.0: &Foo));
325+
// _3 = &fake shallow (_1.0: &Foo);
326+
// ```
327+
//
328+
// However, since this transform is responsible for replacing
329+
// `_1.0: &Foo` with `_1.0: Foo`, that makes the second fake borrow
330+
// obsolete, and we should replace it with a nop.
331+
//
332+
// As a side-note, we don't actually even care about fake borrows
333+
// here at all since they're fully a MIR borrowck artifact, and we
334+
// don't need to borrowck by-move MIR bodies. But it's best to preserve
335+
// as much as we can between these two bodies :)
336+
if let mir::StatementKind::Assign(box (_, rvalue)) = &statement.kind
337+
&& let mir::Rvalue::Ref(_, mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow), place) =
338+
rvalue
339+
&& let mir::PlaceRef {
340+
local: ty::CAPTURE_STRUCT_LOCAL,
341+
projection: [mir::ProjectionElem::Field(idx, _)],
342+
} = place.as_ref()
343+
&& let Some(&(_, _, true, _)) = self.field_remapping.get(&idx)
344+
{
345+
statement.kind = mir::StatementKind::Nop;
346+
}
347+
348+
self.super_statement(statement, location);
349+
}
350+
315351
fn visit_local_decl(&mut self, local: mir::Local, local_decl: &mut mir::LocalDecl<'tcx>) {
316352
// Replace the type of the self arg.
317353
if local == ty::CAPTURE_STRUCT_LOCAL {
318354
local_decl.ty = self.by_move_coroutine_ty;
319355
}
356+
self.super_local_decl(local, local_decl);
320357
}
321358
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// MIR for `foo::{closure#0}::{closure#0}` after built
2+
3+
fn foo::{closure#0}::{closure#0}(_1: {async closure body@$DIR/async_closure_fake_read_for_by_move.rs:12:27: 15:6}, _2: ResumeTy) -> ()
4+
yields ()
5+
{
6+
debug _task_context => _2;
7+
debug f => (*(_1.0: &&Foo));
8+
let mut _0: ();
9+
let mut _3: &Foo;
10+
let mut _4: &&Foo;
11+
let mut _5: &&&Foo;
12+
let mut _6: isize;
13+
let mut _7: bool;
14+
15+
bb0: {
16+
PlaceMention((*(_1.0: &&Foo)));
17+
_6 = discriminant((*(*(_1.0: &&Foo))));
18+
switchInt(move _6) -> [0: bb2, otherwise: bb1];
19+
}
20+
21+
bb1: {
22+
_0 = const ();
23+
goto -> bb10;
24+
}
25+
26+
bb2: {
27+
falseEdge -> [real: bb5, imaginary: bb1];
28+
}
29+
30+
bb3: {
31+
goto -> bb1;
32+
}
33+
34+
bb4: {
35+
FakeRead(ForMatchedPlace(None), (*(_1.0: &&Foo)));
36+
unreachable;
37+
}
38+
39+
bb5: {
40+
_3 = &fake shallow (*(*(_1.0: &&Foo)));
41+
_4 = &fake shallow (*(_1.0: &&Foo));
42+
_5 = &fake shallow (_1.0: &&Foo);
43+
StorageLive(_7);
44+
_7 = const true;
45+
switchInt(move _7) -> [0: bb8, otherwise: bb7];
46+
}
47+
48+
bb6: {
49+
falseEdge -> [real: bb3, imaginary: bb1];
50+
}
51+
52+
bb7: {
53+
StorageDead(_7);
54+
FakeRead(ForMatchGuard, _3);
55+
FakeRead(ForMatchGuard, _4);
56+
FakeRead(ForMatchGuard, _5);
57+
_0 = const ();
58+
goto -> bb10;
59+
}
60+
61+
bb8: {
62+
goto -> bb9;
63+
}
64+
65+
bb9: {
66+
StorageDead(_7);
67+
goto -> bb6;
68+
}
69+
70+
bb10: {
71+
drop(_1) -> [return: bb11, unwind: bb12];
72+
}
73+
74+
bb11: {
75+
return;
76+
}
77+
78+
bb12 (cleanup): {
79+
resume;
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// MIR for `foo::{closure#0}::{closure#1}` after built
2+
3+
fn foo::{closure#0}::{closure#1}(_1: {async closure body@$DIR/async_closure_fake_read_for_by_move.rs:12:27: 15:6}, _2: ResumeTy) -> ()
4+
yields ()
5+
{
6+
debug _task_context => _2;
7+
debug f => (_1.0: &Foo);
8+
let mut _0: ();
9+
let mut _3: &Foo;
10+
let mut _4: &&Foo;
11+
let mut _5: &&&Foo;
12+
let mut _6: isize;
13+
let mut _7: bool;
14+
15+
bb0: {
16+
PlaceMention((_1.0: &Foo));
17+
_6 = discriminant((*(_1.0: &Foo)));
18+
switchInt(move _6) -> [0: bb2, otherwise: bb1];
19+
}
20+
21+
bb1: {
22+
_0 = const ();
23+
goto -> bb6;
24+
}
25+
26+
bb2: {
27+
falseEdge -> [real: bb3, imaginary: bb1];
28+
}
29+
30+
bb3: {
31+
_3 = &fake shallow (*(_1.0: &Foo));
32+
_4 = &fake shallow (_1.0: &Foo);
33+
nop;
34+
StorageLive(_7);
35+
_7 = const true;
36+
switchInt(move _7) -> [0: bb5, otherwise: bb4];
37+
}
38+
39+
bb4: {
40+
StorageDead(_7);
41+
FakeRead(ForMatchGuard, _3);
42+
FakeRead(ForMatchGuard, _4);
43+
FakeRead(ForMatchGuard, _5);
44+
_0 = const ();
45+
goto -> bb6;
46+
}
47+
48+
bb5: {
49+
StorageDead(_7);
50+
falseEdge -> [real: bb1, imaginary: bb1];
51+
}
52+
53+
bb6: {
54+
drop(_1) -> [return: bb7, unwind: bb8];
55+
}
56+
57+
bb7: {
58+
return;
59+
}
60+
61+
bb8 (cleanup): {
62+
resume;
63+
}
64+
}

Diff for: tests/mir-opt/async_closure_fake_read_for_by_move.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ edition:2021
2+
// skip-filecheck
3+
4+
enum Foo {
5+
Bar,
6+
Baz,
7+
}
8+
9+
// EMIT_MIR async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir
10+
// EMIT_MIR async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#1}.built.after.mir
11+
fn foo(f: &Foo) {
12+
let x = async move || match f {
13+
Foo::Bar if true => {}
14+
_ => {}
15+
};
16+
}
17+
18+
fn main() {}

0 commit comments

Comments
 (0)