Skip to content

Commit cfe1faa

Browse files
Rollup merge of #123658 - compiler-errors:stop-assuming, r=oli-obk
Stop making any assumption about the projections applied to the upvars in the `ByMoveBody` pass So it turns out that because of subtle optimizations like [`truncate_capture_for_optimization`](https://github.com/rust-lang/rust/blob/ab5bda1aa70f707014e2e691e43bc37a8819252a/compiler/rustc_hir_typeck/src/upvar.rs#L2351), we simply cannot make any assumptions about the shape of the projections applied to the upvar locals in a coroutine body. So stop doing that -- the code is resilient to such projections, so the assertion really existed only to "protect against the unknown". r? oli-obk Fixes #123650
2 parents b3f40e3 + 54a93ab commit cfe1faa

File tree

2 files changed

+34
-19
lines changed

2 files changed

+34
-19
lines changed

compiler/rustc_mir_transform/src/coroutine/by_move_body.rs

+17-19
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,14 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
281281
if place.local == ty::CAPTURE_STRUCT_LOCAL
282282
&& let Some((&mir::ProjectionElem::Field(idx, _), projection)) =
283283
place.projection.split_first()
284-
&& let Some(&(remapped_idx, remapped_ty, needs_deref, additional_projections)) =
284+
&& let Some(&(remapped_idx, remapped_ty, needs_deref, bridging_projections)) =
285285
self.field_remapping.get(&idx)
286286
{
287287
// As noted before, if the parent closure captures a field by value, and
288288
// the child captures a field by ref, then for the by-move body we're
289289
// generating, we also are taking that field by value. Peel off a deref,
290-
// since a layer of reffing has now become redundant.
291-
let final_deref = if needs_deref {
290+
// since a layer of ref'ing has now become redundant.
291+
let final_projections = if needs_deref {
292292
let Some((mir::ProjectionElem::Deref, projection)) = projection.split_first()
293293
else {
294294
bug!(
@@ -302,20 +302,18 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
302302
projection
303303
};
304304

305-
// The only thing that should be left is a deref, if the parent captured
306-
// an upvar by-ref.
307-
std::assert_matches::assert_matches!(final_deref, [] | [mir::ProjectionElem::Deref]);
308-
309-
// For all of the additional projections that come out of precise capturing,
310-
// re-apply these projections.
311-
let additional_projections =
312-
additional_projections.iter().map(|elem| match elem.kind {
313-
ProjectionKind::Deref => mir::ProjectionElem::Deref,
314-
ProjectionKind::Field(idx, VariantIdx::ZERO) => {
315-
mir::ProjectionElem::Field(idx, elem.ty)
316-
}
317-
_ => unreachable!("precise captures only through fields and derefs"),
318-
});
305+
// These projections are applied in order to "bridge" the local that we are
306+
// currently transforming *from* the old upvar that the by-ref coroutine used
307+
// to capture *to* the upvar of the parent coroutine-closure. For example, if
308+
// the parent captures `&s` but the child captures `&(s.field)`, then we will
309+
// apply a field projection.
310+
let bridging_projections = bridging_projections.iter().map(|elem| match elem.kind {
311+
ProjectionKind::Deref => mir::ProjectionElem::Deref,
312+
ProjectionKind::Field(idx, VariantIdx::ZERO) => {
313+
mir::ProjectionElem::Field(idx, elem.ty)
314+
}
315+
_ => unreachable!("precise captures only through fields and derefs"),
316+
});
319317

320318
// We start out with an adjusted field index (and ty), representing the
321319
// upvar that we get from our parent closure. We apply any of the additional
@@ -326,8 +324,8 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
326324
projection: self.tcx.mk_place_elems_from_iter(
327325
[mir::ProjectionElem::Field(remapped_idx, remapped_ty)]
328326
.into_iter()
329-
.chain(additional_projections)
330-
.chain(final_deref.iter().copied()),
327+
.chain(bridging_projections)
328+
.chain(final_projections.iter().copied()),
331329
),
332330
};
333331
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ edition: 2021
2+
//@ check-pass
3+
4+
#![feature(async_closure)]
5+
6+
pub struct Struct {
7+
pub path: String,
8+
}
9+
10+
// In `upvar.rs`, `truncate_capture_for_optimization` means that we don't actually
11+
// capture `&(*s.path)` here, but instead just `&(*s)`, but ONLY when the upvar is
12+
// immutable. This means that the assumption we have in `ByMoveBody` pass is wrong.
13+
pub fn test(s: &Struct) {
14+
let c = async move || { let path = &s.path; };
15+
}
16+
17+
fn main() {}

0 commit comments

Comments
 (0)