Skip to content

Commit 568703c

Browse files
Use a helper to zip together parent and child captures for coroutine-closures
1 parent 5974fe8 commit 568703c

File tree

3 files changed

+81
-71
lines changed

3 files changed

+81
-71
lines changed

compiler/rustc_middle/src/ty/closure.rs

+67
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{mir, ty};
66
use std::fmt::Write;
77

88
use crate::query::Providers;
9+
use rustc_data_structures::captures::Captures;
910
use rustc_data_structures::fx::FxIndexMap;
1011
use rustc_hir as hir;
1112
use rustc_hir::def_id::LocalDefId;
@@ -415,6 +416,72 @@ impl BorrowKind {
415416
}
416417
}
417418

419+
pub fn analyze_coroutine_closure_captures<'a, 'tcx: 'a, T>(
420+
parent_captures: impl IntoIterator<Item = &'a CapturedPlace<'tcx>>,
421+
child_captures: impl IntoIterator<Item = &'a CapturedPlace<'tcx>>,
422+
mut for_each: impl FnMut((usize, &'a CapturedPlace<'tcx>), (usize, &'a CapturedPlace<'tcx>)) -> T,
423+
) -> impl Iterator<Item = T> + Captures<'a> + Captures<'tcx> {
424+
std::iter::from_coroutine(move || {
425+
let mut child_captures = child_captures.into_iter().enumerate().peekable();
426+
427+
// One parent capture may correspond to several child captures if we end up
428+
// refining the set of captures via edition-2021 precise captures. We want to
429+
// match up any number of child captures with one parent capture, so we keep
430+
// peeking off this `Peekable` until the child doesn't match anymore.
431+
for (parent_field_idx, parent_capture) in parent_captures.into_iter().enumerate() {
432+
// Make sure we use every field at least once, b/c why are we capturing something
433+
// if it's not used in the inner coroutine.
434+
let mut field_used_at_least_once = false;
435+
436+
// A parent matches a child if they share the same prefix of projections.
437+
// The child may have more, if it is capturing sub-fields out of
438+
// something that is captured by-move in the parent closure.
439+
while child_captures.peek().map_or(false, |(_, child_capture)| {
440+
child_prefix_matches_parent_projections(parent_capture, child_capture)
441+
}) {
442+
let (child_field_idx, child_capture) = child_captures.next().unwrap();
443+
// This analysis only makes sense if the parent capture is a
444+
// prefix of the child capture.
445+
assert!(
446+
child_capture.place.projections.len() >= parent_capture.place.projections.len(),
447+
"parent capture ({parent_capture:#?}) expected to be prefix of \
448+
child capture ({child_capture:#?})"
449+
);
450+
451+
yield for_each(
452+
(parent_field_idx, parent_capture),
453+
(child_field_idx, child_capture),
454+
);
455+
456+
field_used_at_least_once = true;
457+
}
458+
459+
// Make sure the field was used at least once.
460+
assert!(
461+
field_used_at_least_once,
462+
"we captured {parent_capture:#?} but it was not used in the child coroutine?"
463+
);
464+
}
465+
assert_eq!(child_captures.next(), None, "leftover child captures?");
466+
})
467+
}
468+
469+
fn child_prefix_matches_parent_projections(
470+
parent_capture: &ty::CapturedPlace<'_>,
471+
child_capture: &ty::CapturedPlace<'_>,
472+
) -> bool {
473+
let HirPlaceBase::Upvar(parent_base) = parent_capture.place.base else {
474+
bug!("expected capture to be an upvar");
475+
};
476+
let HirPlaceBase::Upvar(child_base) = child_capture.place.base else {
477+
bug!("expected capture to be an upvar");
478+
};
479+
480+
parent_base.var_path.hir_id == child_base.var_path.hir_id
481+
&& std::iter::zip(&child_capture.place.projections, &parent_capture.place.projections)
482+
.all(|(child, parent)| child.kind == parent.kind)
483+
}
484+
418485
pub fn provide(providers: &mut Providers) {
419486
*providers = Providers { closure_typeinfo, ..*providers }
420487
}

compiler/rustc_middle/src/ty/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,10 @@ pub use rustc_type_ir::ConstKind::{
7777
pub use rustc_type_ir::*;
7878

7979
pub use self::closure::{
80-
is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo,
81-
CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList,
82-
RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath, CAPTURE_STRUCT_LOCAL,
80+
analyze_coroutine_closure_captures, is_ancestor_or_same_capture, place_to_string_for_capture,
81+
BorrowKind, CaptureInfo, CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap,
82+
MinCaptureList, RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath,
83+
CAPTURE_STRUCT_LOCAL,
8384
};
8485
pub use self::consts::{
8586
Const, ConstData, ConstInt, ConstKind, Expr, ScalarInt, UnevaluatedConst, ValTree,

compiler/rustc_mir_transform/src/coroutine/by_move_body.rs

+10-68
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
7272
use rustc_data_structures::unord::UnordMap;
7373
use rustc_hir as hir;
74-
use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind};
74+
use rustc_middle::hir::place::{Projection, ProjectionKind};
7575
use rustc_middle::mir::visit::MutVisitor;
7676
use rustc_middle::mir::{self, dump_mir, MirPass};
7777
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt, TypeVisitableExt};
@@ -124,44 +124,10 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
124124
.tuple_fields()
125125
.len();
126126

127-
let mut field_remapping = UnordMap::default();
128-
129-
let mut child_captures = tcx
130-
.closure_captures(coroutine_def_id)
131-
.iter()
132-
.copied()
133-
// By construction we capture all the args first.
134-
.skip(num_args)
135-
.enumerate()
136-
.peekable();
137-
138-
// One parent capture may correspond to several child captures if we end up
139-
// refining the set of captures via edition-2021 precise captures. We want to
140-
// match up any number of child captures with one parent capture, so we keep
141-
// peeking off this `Peekable` until the child doesn't match anymore.
142-
for (parent_field_idx, parent_capture) in
143-
tcx.closure_captures(parent_def_id).iter().copied().enumerate()
144-
{
145-
// Make sure we use every field at least once, b/c why are we capturing something
146-
// if it's not used in the inner coroutine.
147-
let mut field_used_at_least_once = false;
148-
149-
// A parent matches a child if they share the same prefix of projections.
150-
// The child may have more, if it is capturing sub-fields out of
151-
// something that is captured by-move in the parent closure.
152-
while child_captures.peek().map_or(false, |(_, child_capture)| {
153-
child_prefix_matches_parent_projections(parent_capture, child_capture)
154-
}) {
155-
let (child_field_idx, child_capture) = child_captures.next().unwrap();
156-
157-
// This analysis only makes sense if the parent capture is a
158-
// prefix of the child capture.
159-
assert!(
160-
child_capture.place.projections.len() >= parent_capture.place.projections.len(),
161-
"parent capture ({parent_capture:#?}) expected to be prefix of \
162-
child capture ({child_capture:#?})"
163-
);
164-
127+
let field_remapping: UnordMap<_, _> = ty::analyze_coroutine_closure_captures(
128+
tcx.closure_captures(parent_def_id).iter().copied(),
129+
tcx.closure_captures(coroutine_def_id).iter().skip(num_args).copied(),
130+
|(parent_field_idx, parent_capture), (child_field_idx, child_capture)| {
165131
// Store this set of additional projections (fields and derefs).
166132
// We need to re-apply them later.
167133
let child_precise_captures =
@@ -192,26 +158,18 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
192158
),
193159
};
194160

195-
field_remapping.insert(
161+
(
196162
FieldIdx::from_usize(child_field_idx + num_args),
197163
(
198164
FieldIdx::from_usize(parent_field_idx + num_args),
199165
parent_capture_ty,
200166
needs_deref,
201167
child_precise_captures,
202168
),
203-
);
204-
205-
field_used_at_least_once = true;
206-
}
207-
208-
// Make sure the field was used at least once.
209-
assert!(
210-
field_used_at_least_once,
211-
"we captured {parent_capture:#?} but it was not used in the child coroutine?"
212-
);
213-
}
214-
assert_eq!(child_captures.next(), None, "leftover child captures?");
169+
)
170+
},
171+
)
172+
.collect();
215173

216174
if coroutine_kind == ty::ClosureKind::FnOnce {
217175
assert_eq!(field_remapping.len(), tcx.closure_captures(parent_def_id).len());
@@ -241,22 +199,6 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
241199
}
242200
}
243201

244-
fn child_prefix_matches_parent_projections(
245-
parent_capture: &ty::CapturedPlace<'_>,
246-
child_capture: &ty::CapturedPlace<'_>,
247-
) -> bool {
248-
let PlaceBase::Upvar(parent_base) = parent_capture.place.base else {
249-
bug!("expected capture to be an upvar");
250-
};
251-
let PlaceBase::Upvar(child_base) = child_capture.place.base else {
252-
bug!("expected capture to be an upvar");
253-
};
254-
255-
parent_base.var_path.hir_id == child_base.var_path.hir_id
256-
&& std::iter::zip(&child_capture.place.projections, &parent_capture.place.projections)
257-
.all(|(child, parent)| child.kind == parent.kind)
258-
}
259-
260202
struct MakeByMoveBody<'tcx> {
261203
tcx: TyCtxt<'tcx>,
262204
field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, &'tcx [Projection<'tcx>])>,

0 commit comments

Comments
 (0)