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`](ab5bda1aa7/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
This commit is contained in:
commit
cfe1faa75d
@ -281,14 +281,14 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
|
|||||||
if place.local == ty::CAPTURE_STRUCT_LOCAL
|
if place.local == ty::CAPTURE_STRUCT_LOCAL
|
||||||
&& let Some((&mir::ProjectionElem::Field(idx, _), projection)) =
|
&& let Some((&mir::ProjectionElem::Field(idx, _), projection)) =
|
||||||
place.projection.split_first()
|
place.projection.split_first()
|
||||||
&& let Some(&(remapped_idx, remapped_ty, needs_deref, additional_projections)) =
|
&& let Some(&(remapped_idx, remapped_ty, needs_deref, bridging_projections)) =
|
||||||
self.field_remapping.get(&idx)
|
self.field_remapping.get(&idx)
|
||||||
{
|
{
|
||||||
// As noted before, if the parent closure captures a field by value, and
|
// As noted before, if the parent closure captures a field by value, and
|
||||||
// the child captures a field by ref, then for the by-move body we're
|
// the child captures a field by ref, then for the by-move body we're
|
||||||
// generating, we also are taking that field by value. Peel off a deref,
|
// generating, we also are taking that field by value. Peel off a deref,
|
||||||
// since a layer of reffing has now become redundant.
|
// since a layer of ref'ing has now become redundant.
|
||||||
let final_deref = if needs_deref {
|
let final_projections = if needs_deref {
|
||||||
let Some((mir::ProjectionElem::Deref, projection)) = projection.split_first()
|
let Some((mir::ProjectionElem::Deref, projection)) = projection.split_first()
|
||||||
else {
|
else {
|
||||||
bug!(
|
bug!(
|
||||||
@ -302,20 +302,18 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
|
|||||||
projection
|
projection
|
||||||
};
|
};
|
||||||
|
|
||||||
// The only thing that should be left is a deref, if the parent captured
|
// These projections are applied in order to "bridge" the local that we are
|
||||||
// an upvar by-ref.
|
// currently transforming *from* the old upvar that the by-ref coroutine used
|
||||||
std::assert_matches::assert_matches!(final_deref, [] | [mir::ProjectionElem::Deref]);
|
// to capture *to* the upvar of the parent coroutine-closure. For example, if
|
||||||
|
// the parent captures `&s` but the child captures `&(s.field)`, then we will
|
||||||
// For all of the additional projections that come out of precise capturing,
|
// apply a field projection.
|
||||||
// re-apply these projections.
|
let bridging_projections = bridging_projections.iter().map(|elem| match elem.kind {
|
||||||
let additional_projections =
|
ProjectionKind::Deref => mir::ProjectionElem::Deref,
|
||||||
additional_projections.iter().map(|elem| match elem.kind {
|
ProjectionKind::Field(idx, VariantIdx::ZERO) => {
|
||||||
ProjectionKind::Deref => mir::ProjectionElem::Deref,
|
mir::ProjectionElem::Field(idx, elem.ty)
|
||||||
ProjectionKind::Field(idx, VariantIdx::ZERO) => {
|
}
|
||||||
mir::ProjectionElem::Field(idx, elem.ty)
|
_ => unreachable!("precise captures only through fields and derefs"),
|
||||||
}
|
});
|
||||||
_ => unreachable!("precise captures only through fields and derefs"),
|
|
||||||
});
|
|
||||||
|
|
||||||
// We start out with an adjusted field index (and ty), representing the
|
// We start out with an adjusted field index (and ty), representing the
|
||||||
// upvar that we get from our parent closure. We apply any of the additional
|
// 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> {
|
|||||||
projection: self.tcx.mk_place_elems_from_iter(
|
projection: self.tcx.mk_place_elems_from_iter(
|
||||||
[mir::ProjectionElem::Field(remapped_idx, remapped_ty)]
|
[mir::ProjectionElem::Field(remapped_idx, remapped_ty)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(additional_projections)
|
.chain(bridging_projections)
|
||||||
.chain(final_deref.iter().copied()),
|
.chain(final_projections.iter().copied()),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
//@ edition: 2021
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
#![feature(async_closure)]
|
||||||
|
|
||||||
|
pub struct Struct {
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// In `upvar.rs`, `truncate_capture_for_optimization` means that we don't actually
|
||||||
|
// capture `&(*s.path)` here, but instead just `&(*s)`, but ONLY when the upvar is
|
||||||
|
// immutable. This means that the assumption we have in `ByMoveBody` pass is wrong.
|
||||||
|
pub fn test(s: &Struct) {
|
||||||
|
let c = async move || { let path = &s.path; };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
x
Reference in New Issue
Block a user