Skip to content

Commit 6706033

Browse files
arora-amanroxelo
andcommitted
Move handling UpvarRef to PlaceBuilder
- This allows us to delay figuring out the index of a cpature in the closure structure when all projections to atleast form a capture have been applied to the builder Co-authored-by: Roxane Fruytier <[email protected]>
1 parent b245d04 commit 6706033

File tree

1 file changed

+166
-81
lines changed

1 file changed

+166
-81
lines changed

compiler/rustc_mir_build/src/build/expr/as_place.rs

Lines changed: 166 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use crate::build::expr::category::Category;
44
use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
55
use crate::build::{BlockAnd, BlockAndExtension, Builder};
66
use crate::thir::*;
7+
use rustc_hir::def_id::DefId;
8+
use rustc_hir::HirId;
79
use rustc_middle::middle::region;
810
use rustc_middle::mir::AssertKind::BoundsCheck;
911
use rustc_middle::mir::*;
@@ -12,6 +14,12 @@ use rustc_span::Span;
1214

1315
use rustc_index::vec::Idx;
1416

17+
#[derive(Copy, Clone)]
18+
pub enum PlaceBase {
19+
Local(Local),
20+
Upvar { var_hir_id: HirId, closure_def_id: DefId, closure_kind: ty::ClosureKind },
21+
}
22+
1523
/// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a
1624
/// place by pushing more and more projections onto the end, and then convert the final set into a
1725
/// place using the `into_place` method.
@@ -20,13 +28,132 @@ use rustc_index::vec::Idx;
2028
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
2129
#[derive(Clone)]
2230
struct PlaceBuilder<'tcx> {
23-
local: Local,
31+
base: PlaceBase,
2432
projection: Vec<PlaceElem<'tcx>>,
2533
}
2634

35+
fn capture_matching_projections<'a, 'tcx>(
36+
tcx: TyCtxt<'tcx>,
37+
typeck_results: &'a ty::TypeckResults<'tcx>,
38+
var_hir_id: HirId,
39+
closure_def_id: DefId,
40+
projections: &Vec<PlaceElem<'tcx>>,
41+
) -> Option<(usize, Ty<'tcx>, ty::UpvarCapture<'tcx>)> {
42+
let capture = typeck_results
43+
.closure_captures
44+
.get(&closure_def_id)
45+
.and_then(|captures| captures.get_full(&var_hir_id));
46+
47+
if capture.is_none() {
48+
if !tcx.features().capture_disjoint_fields {
49+
bug!(
50+
"No associated capture found for {:?}[{:#?}] even though \
51+
capture_disjoint_fields isn't enabled",
52+
var_hir_id,
53+
projections
54+
)
55+
}
56+
}
57+
58+
// Unwrap until the FIXME has been resolved
59+
let (capture_index, _, _) = capture.unwrap();
60+
61+
let closure_ty =
62+
typeck_results.node_type(tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()));
63+
64+
let substs = match closure_ty.kind() {
65+
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
66+
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
67+
_ => bug!("Lowering capture for non-closure type {:?}", closure_ty),
68+
};
69+
70+
// Access the capture by accessing the field within the Closure struct.
71+
//
72+
// We must have inferred the capture types since we are building MIR, therefore
73+
// it's safe to call `upvar_tys` and we can unwrap here because
74+
// we know that the capture exists and is the `capture_index`-th capture.
75+
let var_ty = substs.upvar_tys().nth(capture_index).unwrap();
76+
77+
let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id.expect_local());
78+
let capture_kind = typeck_results.upvar_capture(upvar_id);
79+
80+
Some((capture_index, var_ty, capture_kind))
81+
}
82+
83+
/// Takes a PlaceBuilder and resolves the upvar (if any) within it,
84+
/// so that the PlaceBuilder now starts from PlaceBase::Local.
85+
///
86+
/// Returns a Result with the error being the HirId of the
87+
/// Upvar that was not found.
88+
fn to_upvars_resolved_place_builder<'a, 'tcx>(
89+
from_builder: PlaceBuilder<'tcx>,
90+
tcx: TyCtxt<'tcx>,
91+
typeck_results: &'a ty::TypeckResults<'tcx>,
92+
) -> Result<PlaceBuilder<'tcx>, HirId> {
93+
match from_builder.base {
94+
PlaceBase::Local(_) => Ok(from_builder),
95+
PlaceBase::Upvar { var_hir_id, closure_def_id, closure_kind } => {
96+
// Captures are represented using fields inside a structure.
97+
// This represents accessing self in the closure structure
98+
let local = PlaceBase::Local(Local::new(1));
99+
let mut place_builder = PlaceBuilder::from(local);
100+
match closure_kind {
101+
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
102+
place_builder = place_builder.deref();
103+
}
104+
ty::ClosureKind::FnOnce => {}
105+
}
106+
107+
let (capture_index, var_ty, capture_kind) =
108+
if let Some(capture_details) = capture_matching_projections(
109+
tcx,
110+
typeck_results,
111+
var_hir_id,
112+
closure_def_id,
113+
&from_builder.projection,
114+
) {
115+
capture_details
116+
} else {
117+
return Err(var_hir_id);
118+
};
119+
120+
place_builder = place_builder.field(Field::new(capture_index), var_ty);
121+
122+
// If the variable is captured via ByRef(Immutable/Mutable) Borrow,
123+
// we need to deref it
124+
place_builder = match capture_kind {
125+
ty::UpvarCapture::ByRef(_) => place_builder.deref(),
126+
ty::UpvarCapture::ByValue(_) => place_builder,
127+
};
128+
129+
let next_projection = 0;
130+
let mut curr_projections = from_builder.projection;
131+
place_builder.projection.extend(curr_projections.drain(next_projection..));
132+
133+
Ok(place_builder)
134+
}
135+
}
136+
}
137+
27138
impl<'tcx> PlaceBuilder<'tcx> {
28-
fn into_place(self, tcx: TyCtxt<'tcx>) -> Place<'tcx> {
29-
Place { local: self.local, projection: tcx.intern_place_elems(&self.projection) }
139+
fn into_place<'a>(
140+
self,
141+
tcx: TyCtxt<'tcx>,
142+
typeck_results: &'a ty::TypeckResults<'tcx>,
143+
) -> Place<'tcx> {
144+
if let PlaceBase::Local(local) = self.base {
145+
Place { local, projection: tcx.intern_place_elems(&self.projection) }
146+
} else {
147+
self.expect_upvars_resolved(tcx, typeck_results).into_place(tcx, typeck_results)
148+
}
149+
}
150+
151+
fn expect_upvars_resolved<'a>(
152+
self,
153+
tcx: TyCtxt<'tcx>,
154+
typeck_results: &'a ty::TypeckResults<'tcx>,
155+
) -> PlaceBuilder<'tcx> {
156+
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
30157
}
31158

32159
fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
@@ -47,9 +174,9 @@ impl<'tcx> PlaceBuilder<'tcx> {
47174
}
48175
}
49176

50-
impl<'tcx> From<Local> for PlaceBuilder<'tcx> {
51-
fn from(local: Local) -> Self {
52-
Self { local, projection: Vec::new() }
177+
impl<'tcx> From<PlaceBase> for PlaceBuilder<'tcx> {
178+
fn from(base: PlaceBase) -> Self {
179+
Self { base, projection: Vec::new() }
53180
}
54181
}
55182

@@ -71,7 +198,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
71198
M: Mirror<'tcx, Output = Expr<'tcx>>,
72199
{
73200
let place_builder = unpack!(block = self.as_place_builder(block, expr));
74-
block.and(place_builder.into_place(self.hir.tcx()))
201+
block.and(place_builder.into_place(self.hir.tcx(), self.hir.typeck_results()))
75202
}
76203

77204
/// This is used when constructing a compound `Place`, so that we can avoid creating
@@ -98,7 +225,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
98225
M: Mirror<'tcx, Output = Expr<'tcx>>,
99226
{
100227
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
101-
block.and(place_builder.into_place(self.hir.tcx()))
228+
block.and(place_builder.into_place(self.hir.tcx(), self.hir.typeck_results()))
102229
}
103230

104231
/// This is used when constructing a compound `Place`, so that we can avoid creating
@@ -161,36 +288,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
161288
source_info,
162289
),
163290
ExprKind::UpvarRef { closure_def_id, var_hir_id } => {
164-
let capture = this
165-
.hir
166-
.typeck_results
167-
.closure_captures
168-
.get(&closure_def_id)
169-
.and_then(|captures| captures.get_full(&var_hir_id));
170-
171-
if capture.is_none() {
172-
if !this.hir.tcx().features().capture_disjoint_fields {
173-
bug!(
174-
"No associated capture found for {:?} even though \
175-
capture_disjoint_fields isn't enabled",
176-
expr.kind
177-
)
178-
}
179-
// FIXME(project-rfc-2229#24): Handle this case properly
180-
}
181-
182-
// Unwrap until the FIXME has been resolved
183-
let (capture_index, _, upvar_id) = capture.unwrap();
184-
this.lower_closure_capture(block, capture_index, *upvar_id)
291+
let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id.expect_local());
292+
this.lower_closure_capture(block, upvar_id)
185293
}
186294

187295
ExprKind::VarRef { id } => {
188296
let place_builder = if this.is_bound_var_in_guard(id) {
189297
let index = this.var_local_id(id, RefWithinGuard);
190-
PlaceBuilder::from(index).deref()
298+
PlaceBuilder::from(PlaceBase::Local(index)).deref()
191299
} else {
192300
let index = this.var_local_id(id, OutsideGuard);
193-
PlaceBuilder::from(index)
301+
PlaceBuilder::from(PlaceBase::Local(index))
194302
};
195303
block.and(place_builder)
196304
}
@@ -208,7 +316,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
208316
inferred_ty: expr.ty,
209317
});
210318

211-
let place = place_builder.clone().into_place(this.hir.tcx());
319+
let place =
320+
place_builder.clone().into_place(this.hir.tcx(), this.hir.typeck_results());
212321
this.cfg.push(
213322
block,
214323
Statement {
@@ -250,7 +359,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
250359
},
251360
);
252361
}
253-
block.and(PlaceBuilder::from(temp))
362+
block.and(PlaceBuilder::from(PlaceBase::Local(temp)))
254363
}
255364

256365
ExprKind::Array { .. }
@@ -288,64 +397,35 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
288397
debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place)));
289398
let temp =
290399
unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability));
291-
block.and(PlaceBuilder::from(temp))
400+
block.and(PlaceBuilder::from(PlaceBase::Local(temp)))
292401
}
293402
}
294403
}
295404

296405
/// Lower a closure/generator capture by representing it as a field
297406
/// access within the desugared closure/generator.
298-
///
299-
/// `capture_index` is the index of the capture within the desugared
300-
/// closure/generator.
301407
fn lower_closure_capture(
302408
&mut self,
303409
block: BasicBlock,
304-
capture_index: usize,
305410
upvar_id: ty::UpvarId,
306-
) -> BlockAnd<PlaceBuilder<'tcx>> {
411+
) -> BlockAnd<PlaceBuilder<'tcx>> {
307412
let closure_ty = self
308413
.hir
309414
.typeck_results()
310415
.node_type(self.hir.tcx().hir().local_def_id_to_hir_id(upvar_id.closure_expr_id));
311416

312-
// Captures are represented using fields inside a structure.
313-
// This represents accessing self in the closure structure
314-
let mut place_builder = PlaceBuilder::from(Local::new(1));
315-
316-
// In case of Fn/FnMut closures we must deref to access the fields
317-
// Generators are considered FnOnce, so we ignore this step for them.
318-
if let ty::Closure(_, closure_substs) = closure_ty.kind() {
319-
match self.hir.infcx().closure_kind(closure_substs).unwrap() {
320-
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
321-
place_builder = place_builder.deref();
322-
}
323-
ty::ClosureKind::FnOnce => {}
324-
}
325-
}
326-
327-
let substs = match closure_ty.kind() {
328-
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
329-
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
330-
_ => bug!("Lowering capture for non-closure type {:?}", closure_ty)
417+
let closure_kind = if let ty::Closure(_, closure_substs) = closure_ty.kind() {
418+
self.hir.infcx().closure_kind(closure_substs).unwrap()
419+
} else {
420+
// Generators are considered FnOnce.
421+
ty::ClosureKind::FnOnce
331422
};
332423

333-
// Access the capture by accessing the field within the Closure struct.
334-
//
335-
// We must have inferred the capture types since we are building MIR, therefore
336-
// it's safe to call `upvar_tys` and we can unwrap here because
337-
// we know that the capture exists and is the `capture_index`-th capture.
338-
let var_ty = substs.upvar_tys().nth(capture_index).unwrap();
339-
place_builder = place_builder.field(Field::new(capture_index), var_ty);
340-
341-
// If the variable is captured via ByRef(Immutable/Mutable) Borrow,
342-
// we need to deref it
343-
match self.hir.typeck_results.upvar_capture(upvar_id) {
344-
ty::UpvarCapture::ByRef(_) => {
345-
block.and(place_builder.deref())
346-
}
347-
ty::UpvarCapture::ByValue(_) => block.and(place_builder),
348-
}
424+
block.and(PlaceBuilder::from(PlaceBase::Upvar {
425+
var_hir_id: upvar_id.var_path.hir_id,
426+
closure_def_id: upvar_id.closure_expr_id.to_def_id(),
427+
closure_kind,
428+
}))
349429
}
350430

351431
/// Lower an index expression
@@ -373,7 +453,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
373453
let is_outermost_index = fake_borrow_temps.is_none();
374454
let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps);
375455

376-
let base_place =
456+
let mut base_place =
377457
unpack!(block = self.expr_as_place(block, lhs, mutability, Some(fake_borrow_temps),));
378458

379459
// Making this a *fresh* temporary means we do not have to worry about
@@ -383,7 +463,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
383463

384464
block = self.bounds_check(
385465
block,
386-
base_place.clone().into_place(self.hir.tcx()),
466+
base_place.clone().into_place(self.hir.tcx(), self.hir.typeck_results()),
387467
idx,
388468
expr_span,
389469
source_info,
@@ -392,6 +472,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
392472
if is_outermost_index {
393473
self.read_fake_borrows(block, fake_borrow_temps, source_info)
394474
} else {
475+
base_place = base_place.expect_upvars_resolved(self.hir.tcx(), self.hir.typeck_results());
395476
self.add_fake_borrows_of_base(
396477
&base_place,
397478
block,
@@ -441,8 +522,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
441522
source_info: SourceInfo,
442523
) {
443524
let tcx = self.hir.tcx();
444-
let place_ty =
445-
Place::ty_from(base_place.local, &base_place.projection, &self.local_decls, tcx);
525+
let local = match base_place.base {
526+
PlaceBase::Local(local) => local,
527+
PlaceBase::Upvar { .. } => bug!("Expected PlacseBase::Local found Upvar")
528+
};
529+
530+
let place_ty = Place::ty_from(local, &base_place.projection, &self.local_decls, tcx);
446531
if let ty::Slice(_) = place_ty.ty.kind() {
447532
// We need to create fake borrows to ensure that the bounds
448533
// check that we just did stays valid. Since we can't assign to
@@ -452,7 +537,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
452537
match elem {
453538
ProjectionElem::Deref => {
454539
let fake_borrow_deref_ty = Place::ty_from(
455-
base_place.local,
540+
local,
456541
&base_place.projection[..idx],
457542
&self.local_decls,
458543
tcx,
@@ -470,14 +555,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
470555
Rvalue::Ref(
471556
tcx.lifetimes.re_erased,
472557
BorrowKind::Shallow,
473-
Place { local: base_place.local, projection },
558+
Place { local, projection },
474559
),
475560
);
476561
fake_borrow_temps.push(fake_borrow_temp);
477562
}
478563
ProjectionElem::Index(_) => {
479564
let index_ty = Place::ty_from(
480-
base_place.local,
565+
local,
481566
&base_place.projection[..idx],
482567
&self.local_decls,
483568
tcx,

0 commit comments

Comments
 (0)