Skip to content

Commit 7faebe5

Browse files
committed
Move capture lowering from THIR to MIR
This allows us to: - Handle precise Places captured by a closure directly in MIR. Handling captures in MIR is easier since we can rely on/ tweak PlaceBuilder to generate `mir::Place`s that resemble how we store captures (`hir::Place`). - Allows us to handle `let _ = x` case when feature `capture_disjoint_fields` is enabled directly in MIR. This is required to be done in MIR since patterns are desugared in MIR.
1 parent b5c37e8 commit 7faebe5

File tree

7 files changed

+108
-118
lines changed

7 files changed

+108
-118
lines changed

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

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
160160
expr_span,
161161
source_info,
162162
),
163+
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)
185+
}
186+
163187
ExprKind::SelfRef => block.and(PlaceBuilder::from(Local::new(1))),
164188
ExprKind::VarRef { id } => {
165189
let place_builder = if this.is_bound_var_in_guard(id) {
@@ -270,6 +294,61 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
270294
}
271295
}
272296

297+
/// Lower a closure/generator capture by representing it as a field
298+
/// access within the desugared closure/generator.
299+
///
300+
/// `capture_index` is the index of the capture within the desugared
301+
/// closure/generator.
302+
fn lower_closure_capture(
303+
&mut self,
304+
block: BasicBlock,
305+
capture_index: usize,
306+
upvar_id: ty::UpvarId,
307+
) -> BlockAnd<PlaceBuilder<'tcx>> {
308+
let closure_ty = self
309+
.hir
310+
.typeck_results()
311+
.node_type(self.hir.tcx().hir().local_def_id_to_hir_id(upvar_id.closure_expr_id));
312+
313+
// Captures are represented using fields inside a structure.
314+
// This represents accessing self in the closure structure
315+
let mut place_builder = PlaceBuilder::from(Local::new(1));
316+
317+
// In case of Fn/FnMut closures we must deref to access the fields
318+
// Generators are considered FnOnce, so we ignore this step for them.
319+
if let ty::Closure(_, closure_substs) = closure_ty.kind() {
320+
match self.hir.infcx().closure_kind(closure_substs).unwrap() {
321+
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
322+
place_builder = place_builder.deref();
323+
}
324+
ty::ClosureKind::FnOnce => {}
325+
}
326+
}
327+
328+
let substs = match closure_ty.kind() {
329+
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
330+
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
331+
_ => bug!("Lowering capture for non-closure type {:?}", closure_ty)
332+
};
333+
334+
// Access the capture by accessing the field within the Closure struct.
335+
//
336+
// We must have inferred the capture types since we are building MIR, therefore
337+
// it's safe to call `upvar_tys` and we can unwrap here because
338+
// we know that the capture exists and is the `capture_index`-th capture.
339+
let var_ty = substs.upvar_tys().nth(capture_index).unwrap();
340+
place_builder = place_builder.field(Field::new(capture_index), var_ty);
341+
342+
// If the variable is captured via ByRef(Immutable/Mutable) Borrow,
343+
// we need to deref it
344+
match self.hir.typeck_results.upvar_capture(upvar_id) {
345+
ty::UpvarCapture::ByRef(_) => {
346+
block.and(place_builder.deref())
347+
}
348+
ty::UpvarCapture::ByValue(_) => block.and(place_builder),
349+
}
350+
}
351+
273352
/// Lower an index expression
274353
///
275354
/// This has two complications;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
251251
| ExprKind::Index { .. }
252252
| ExprKind::VarRef { .. }
253253
| ExprKind::SelfRef
254+
| ExprKind::UpvarRef { .. }
254255
| ExprKind::Break { .. }
255256
| ExprKind::Continue { .. }
256257
| ExprKind::Return { .. }

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ impl Category {
3939
| ExprKind::Deref { .. }
4040
| ExprKind::Index { .. }
4141
| ExprKind::SelfRef
42+
| ExprKind::UpvarRef { .. }
4243
| ExprKind::VarRef { .. }
4344
| ExprKind::PlaceTypeAscription { .. }
4445
| ExprKind::ValueTypeAscription { .. } => Some(Category::Place),

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
401401
// Avoid creating a temporary
402402
ExprKind::VarRef { .. }
403403
| ExprKind::SelfRef
404+
| ExprKind::UpvarRef { .. }
404405
| ExprKind::PlaceTypeAscription { .. }
405406
| ExprKind::ValueTypeAscription { .. } => {
406407
debug_assert!(Category::of(&expr.kind) == Some(Category::Place));

compiler/rustc_mir_build/src/thir/cx/expr.rs

Lines changed: 14 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -880,130 +880,26 @@ fn convert_path_expr<'a, 'tcx>(
880880
ExprKind::Deref { arg: Expr { ty, temp_lifetime, span: expr.span, kind }.to_ref() }
881881
}
882882

883-
Res::Local(var_hir_id) => convert_var(cx, expr, var_hir_id),
883+
Res::Local(var_hir_id) => convert_var(cx, var_hir_id),
884884

885885
_ => span_bug!(expr.span, "res `{:?}` not yet implemented", res),
886886
}
887887
}
888888

889-
fn convert_var<'tcx>(
890-
cx: &mut Cx<'_, 'tcx>,
891-
expr: &'tcx hir::Expr<'tcx>,
892-
var_hir_id: hir::HirId,
893-
) -> ExprKind<'tcx> {
894-
let upvar_index = cx
895-
.typeck_results()
896-
.closure_captures
897-
.get(&cx.body_owner)
898-
.and_then(|upvars| upvars.get_full(&var_hir_id).map(|(i, _, _)| i));
899-
900-
debug!(
901-
"convert_var({:?}): upvar_index={:?}, body_owner={:?}",
902-
var_hir_id, upvar_index, cx.body_owner
903-
);
904-
905-
let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
906-
907-
match upvar_index {
908-
None => ExprKind::VarRef { id: var_hir_id },
889+
fn convert_var<'tcx>(cx: &mut Cx<'_, 'tcx>, var_hir_id: hir::HirId) -> ExprKind<'tcx> {
890+
// We want upvars here not captures.
891+
// Captures will be handled in MIR.
892+
let is_upvar = cx
893+
.tcx
894+
.upvars_mentioned(cx.body_owner)
895+
.map_or(false, |upvars| upvars.contains_key(&var_hir_id));
909896

910-
Some(upvar_index) => {
911-
let closure_def_id = cx.body_owner;
912-
let upvar_id = ty::UpvarId {
913-
var_path: ty::UpvarPath { hir_id: var_hir_id },
914-
closure_expr_id: closure_def_id.expect_local(),
915-
};
916-
let var_ty = cx.typeck_results().node_type(var_hir_id);
897+
debug!("convert_var({:?}): is_upvar={}, body_owner={:?}", var_hir_id, is_upvar, cx.body_owner);
917898

918-
// FIXME free regions in closures are not right
919-
let closure_ty = cx
920-
.typeck_results()
921-
.node_type(cx.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id));
922-
923-
// FIXME we're just hard-coding the idea that the
924-
// signature will be &self or &mut self and hence will
925-
// have a bound region with number 0
926-
let region = ty::ReFree(ty::FreeRegion {
927-
scope: closure_def_id,
928-
bound_region: ty::BoundRegion::BrAnon(0),
929-
});
930-
let region = cx.tcx.mk_region(region);
931-
932-
let self_expr = if let ty::Closure(_, closure_substs) = closure_ty.kind() {
933-
match cx.infcx.closure_kind(closure_substs).unwrap() {
934-
ty::ClosureKind::Fn => {
935-
let ref_closure_ty = cx.tcx.mk_ref(
936-
region,
937-
ty::TypeAndMut { ty: closure_ty, mutbl: hir::Mutability::Not },
938-
);
939-
Expr {
940-
ty: closure_ty,
941-
temp_lifetime,
942-
span: expr.span,
943-
kind: ExprKind::Deref {
944-
arg: Expr {
945-
ty: ref_closure_ty,
946-
temp_lifetime,
947-
span: expr.span,
948-
kind: ExprKind::SelfRef,
949-
}
950-
.to_ref(),
951-
},
952-
}
953-
}
954-
ty::ClosureKind::FnMut => {
955-
let ref_closure_ty = cx.tcx.mk_ref(
956-
region,
957-
ty::TypeAndMut { ty: closure_ty, mutbl: hir::Mutability::Mut },
958-
);
959-
Expr {
960-
ty: closure_ty,
961-
temp_lifetime,
962-
span: expr.span,
963-
kind: ExprKind::Deref {
964-
arg: Expr {
965-
ty: ref_closure_ty,
966-
temp_lifetime,
967-
span: expr.span,
968-
kind: ExprKind::SelfRef,
969-
}
970-
.to_ref(),
971-
},
972-
}
973-
}
974-
ty::ClosureKind::FnOnce => Expr {
975-
ty: closure_ty,
976-
temp_lifetime,
977-
span: expr.span,
978-
kind: ExprKind::SelfRef,
979-
},
980-
}
981-
} else {
982-
Expr { ty: closure_ty, temp_lifetime, span: expr.span, kind: ExprKind::SelfRef }
983-
};
984-
985-
// at this point we have `self.n`, which loads up the upvar
986-
let field_kind =
987-
ExprKind::Field { lhs: self_expr.to_ref(), name: Field::new(upvar_index) };
988-
989-
// ...but the upvar might be an `&T` or `&mut T` capture, at which
990-
// point we need an implicit deref
991-
match cx.typeck_results().upvar_capture(upvar_id) {
992-
ty::UpvarCapture::ByValue(_) => field_kind,
993-
ty::UpvarCapture::ByRef(borrow) => ExprKind::Deref {
994-
arg: Expr {
995-
temp_lifetime,
996-
ty: cx.tcx.mk_ref(
997-
borrow.region,
998-
ty::TypeAndMut { ty: var_ty, mutbl: borrow.kind.to_mutbl_lossy() },
999-
),
1000-
span: expr.span,
1001-
kind: field_kind,
1002-
}
1003-
.to_ref(),
1004-
},
1005-
}
1006-
}
899+
if is_upvar {
900+
ExprKind::UpvarRef { closure_def_id: cx.body_owner, var_hir_id }
901+
} else {
902+
ExprKind::VarRef { id: var_hir_id }
1007903
}
1008904
}
1009905

@@ -1102,7 +998,7 @@ fn capture_upvar<'tcx>(
1102998
temp_lifetime,
1103999
ty: var_ty,
11041000
span: closure_expr.span,
1105-
kind: convert_var(cx, closure_expr, var_hir_id),
1001+
kind: convert_var(cx, var_hir_id),
11061002
};
11071003
match upvar_capture {
11081004
ty::UpvarCapture::ByValue(_) => captured_var.to_ref(),

compiler/rustc_mir_build/src/thir/cx/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ impl<'a, 'tcx> Cx<'a, 'tcx> {
186186
ty.needs_drop(self.tcx, self.param_env)
187187
}
188188

189+
crate fn infcx(&self) -> &'a InferCtxt<'a, 'tcx> {
190+
self.infcx
191+
}
192+
189193
crate fn tcx(&self) -> TyCtxt<'tcx> {
190194
self.tcx
191195
}

compiler/rustc_mir_build/src/thir/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,14 @@ crate enum ExprKind<'tcx> {
211211
VarRef {
212212
id: hir::HirId,
213213
},
214+
/// Used to represent upvars mentioned in a closure/generator
215+
UpvarRef {
216+
/// DefId of the closure/generator
217+
closure_def_id: DefId,
218+
219+
/// HirId of the root variable
220+
var_hir_id: hir::HirId,
221+
},
214222
/// first argument, used for self in a closure
215223
SelfRef,
216224
Borrow {

0 commit comments

Comments
 (0)