Skip to content

Commit 977aa99

Browse files
arora-amanroxelo
andcommitted
Use precise places when lowering Closures in THIR
- Closures now use closure_min_captures to figure out captured paths - Build upvar_mutbls using closure_min_captures - Change logic in limit_capture_mutability to differentiate b/w capturing parent's local variable or capturing a variable that is captured by the parent (in case of nested closure) using PlaceBase. Co-authored-by: Roxane Fruytier <[email protected]>
1 parent 1e68021 commit 977aa99

File tree

4 files changed

+99
-63
lines changed

4 files changed

+99
-63
lines changed

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_target::abi::VariantIdx;
1717
use rustc_index::vec::Idx;
1818

1919
#[derive(Copy, Clone)]
20-
pub enum PlaceBase {
20+
crate enum PlaceBase {
2121
Local(Local),
2222
Upvar { var_hir_id: HirId, closure_def_id: DefId, closure_kind: ty::ClosureKind },
2323
}
@@ -29,7 +29,7 @@ pub enum PlaceBase {
2929
/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
3030
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
3131
#[derive(Clone)]
32-
struct PlaceBuilder<'tcx> {
32+
crate struct PlaceBuilder<'tcx> {
3333
base: PlaceBase,
3434
projection: Vec<PlaceElem<'tcx>>,
3535
}
@@ -241,7 +241,7 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
241241
}
242242

243243
impl<'tcx> PlaceBuilder<'tcx> {
244-
fn into_place<'a>(
244+
crate fn into_place<'a>(
245245
self,
246246
tcx: TyCtxt<'tcx>,
247247
typeck_results: &'a ty::TypeckResults<'tcx>,
@@ -261,6 +261,10 @@ impl<'tcx> PlaceBuilder<'tcx> {
261261
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
262262
}
263263

264+
crate fn base(&self) -> PlaceBase {
265+
self.base
266+
}
267+
264268
fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
265269
self.project(PlaceElem::Field(f, ty))
266270
}
@@ -308,7 +312,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
308312

309313
/// This is used when constructing a compound `Place`, so that we can avoid creating
310314
/// intermediate `Place` values until we know the full set of projections.
311-
fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
315+
crate fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
312316
where
313317
M: Mirror<'tcx, Output = Expr<'tcx>>,
314318
{

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

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_index::vec::Idx;
44

55
use crate::build::expr::category::{Category, RvalueFunc};
66
use crate::build::{BlockAnd, BlockAndExtension, Builder};
7+
use crate::build::expr::as_place::PlaceBase;
78
use crate::thir::*;
89
use rustc_middle::middle::region;
910
use rustc_middle::mir::AssertKind;
@@ -381,51 +382,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
381382

382383
this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
383384

384-
let arg_place = unpack!(block = this.as_place(block, arg));
385-
386-
let mutability = match arg_place.as_ref() {
387-
PlaceRef { local, projection: &[] } => this.local_decls[local].mutability,
388-
PlaceRef { local, projection: &[ProjectionElem::Deref] } => {
389-
debug_assert!(
390-
this.local_decls[local].is_ref_for_guard(),
391-
"Unexpected capture place",
392-
);
393-
this.local_decls[local].mutability
394-
}
395-
PlaceRef {
396-
local,
397-
projection: &[ref proj_base @ .., ProjectionElem::Field(upvar_index, _)],
398-
}
399-
| PlaceRef {
400-
local,
401-
projection:
402-
&[ref proj_base @ .., ProjectionElem::Field(upvar_index, _), ProjectionElem::Deref],
403-
} => {
404-
let place = PlaceRef { local, projection: proj_base };
405-
406-
// Not projected from the implicit `self` in a closure.
407-
debug_assert!(
408-
match place.local_or_deref_local() {
409-
Some(local) => local == Local::new(1),
410-
None => false,
411-
},
412-
"Unexpected capture place"
413-
);
414-
// Not in a closure
415-
debug_assert!(
416-
this.upvar_mutbls.len() > upvar_index.index(),
417-
"Unexpected capture place"
418-
);
419-
this.upvar_mutbls[upvar_index.index()]
385+
let arg_place_builder = unpack!(block = this.as_place_builder(block, arg));
386+
387+
let mutability = match arg_place_builder.base() {
388+
// Capture parent's local variable
389+
PlaceBase::Local(local) => this.local_decls[local].mutability,
390+
// Parent is a closure and we capturing a variable that is captured
391+
// by the parent itself.
392+
PlaceBase::Upvar { .. } => {
393+
let enclosing_upvars_resolved = arg_place_builder.clone().into_place(
394+
this.hir.tcx(),
395+
this.hir.typeck_results());
396+
397+
match enclosing_upvars_resolved.as_ref() {
398+
PlaceRef { local, projection: &[ProjectionElem::Field(upvar_index, _), ..] }
399+
| PlaceRef {
400+
local,
401+
projection: &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..] } => {
402+
// Not in a closure
403+
debug_assert!(
404+
local == Local::new(1),
405+
"Expected local to be Local(1), found {:?}",
406+
local
407+
);
408+
// Not in a closure
409+
debug_assert!(
410+
this.upvar_mutbls.len() > upvar_index.index(),
411+
"Unexpected capture place, upvar_mutbls={:#?}, upvar_index={:?}",
412+
this.upvar_mutbls, upvar_index
413+
);
414+
this.upvar_mutbls[upvar_index.index()]
415+
}
416+
_ => bug!("Unexpected capture place"),
417+
}
420418
}
421-
_ => bug!("Unexpected capture place"),
422419
};
423420

424421
let borrow_kind = match mutability {
425422
Mutability::Not => BorrowKind::Unique,
426423
Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
427424
};
428425

426+
let arg_place = arg_place_builder.into_place(
427+
this.hir.tcx(),
428+
this.hir.typeck_results());
429+
429430
this.cfg.push_assign(
430431
block,
431432
source_info,

compiler/rustc_mir_build/src/build/mod.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_hir::lang_items::LangItem;
1010
use rustc_hir::{GeneratorKind, HirIdMap, Node};
1111
use rustc_index::vec::{Idx, IndexVec};
1212
use rustc_infer::infer::TyCtxtInferExt;
13+
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
1314
use rustc_middle::middle::region;
1415
use rustc_middle::mir::*;
1516
use rustc_middle::ty::subst::Subst;
@@ -811,7 +812,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
811812
// with the closure's DefId. Here, we run through that vec of UpvarIds for
812813
// the given closure and use the necessary information to create upvar
813814
// debuginfo and to fill `self.upvar_mutbls`.
814-
if let Some(upvars) = hir_typeck_results.closure_captures.get(&fn_def_id) {
815+
if hir_typeck_results.closure_min_captures.get(&fn_def_id).is_some() {
815816
let closure_env_arg = Local::new(1);
816817
let mut closure_env_projs = vec![];
817818
let mut closure_ty = self.local_decls[closure_env_arg].ty;
@@ -824,12 +825,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
824825
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
825826
_ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty),
826827
};
827-
let upvar_tys = upvar_substs.upvar_tys();
828-
let upvars_with_tys = upvars.iter().zip(upvar_tys);
829-
self.upvar_mutbls = upvars_with_tys
828+
let capture_tys = upvar_substs.upvar_tys();
829+
let captures_with_tys = hir_typeck_results
830+
.closure_min_captures_flattened(fn_def_id)
831+
.zip(capture_tys);
832+
833+
self.upvar_mutbls = captures_with_tys
830834
.enumerate()
831-
.map(|(i, ((&var_id, &upvar_id), ty))| {
832-
let capture = hir_typeck_results.upvar_capture(upvar_id);
835+
.map(|(i, (captured_place, ty))| {
836+
let capture = captured_place.info.capture_kind;
837+
let var_id = match captured_place.place.base {
838+
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
839+
_ => bug!("Expected an upvar")
840+
};
833841

834842
let mut mutability = Mutability::Not;
835843

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

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use crate::thir::*;
66
use rustc_hir as hir;
77
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
88
use rustc_index::vec::Idx;
9+
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
10+
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
911
use rustc_middle::mir::interpret::Scalar;
1012
use rustc_middle::mir::BorrowKind;
1113
use rustc_middle::ty::adjustment::{
@@ -386,14 +388,12 @@ fn make_mirror_unadjusted<'a, 'tcx>(
386388
span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
387389
}
388390
};
391+
389392
let upvars = cx
390393
.typeck_results()
391-
.closure_captures
392-
.get(&def_id)
393-
.iter()
394-
.flat_map(|upvars| upvars.iter())
394+
.closure_min_captures_flattened(def_id)
395395
.zip(substs.upvar_tys())
396-
.map(|((&var_hir_id, _), ty)| capture_upvar(cx, expr, var_hir_id, ty))
396+
.map(|(captured_place, ty)| capture_upvar(cx, expr, captured_place, ty))
397397
.collect();
398398
ExprKind::Closure { closure_id: def_id, substs, upvars, movability }
399399
}
@@ -981,27 +981,50 @@ fn overloaded_place<'a, 'tcx>(
981981
ExprKind::Deref { arg: ref_expr.to_ref() }
982982
}
983983

984-
fn capture_upvar<'tcx>(
984+
fn capture_upvar<'a, 'tcx>(
985985
cx: &mut Cx<'_, 'tcx>,
986986
closure_expr: &'tcx hir::Expr<'tcx>,
987-
var_hir_id: hir::HirId,
987+
captured_place: &'a ty::CapturedPlace<'tcx>,
988988
upvar_ty: Ty<'tcx>,
989989
) -> ExprRef<'tcx> {
990-
let upvar_id = ty::UpvarId {
991-
var_path: ty::UpvarPath { hir_id: var_hir_id },
992-
closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id),
993-
};
994-
let upvar_capture = cx.typeck_results().upvar_capture(upvar_id);
990+
let upvar_capture = captured_place.info.capture_kind;
995991
let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
996-
let var_ty = cx.typeck_results().node_type(var_hir_id);
997-
let captured_var = Expr {
992+
let var_ty = captured_place.place.base_ty;
993+
994+
let var_hir_id = match captured_place.place.base {
995+
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
996+
base => bug!("Expected an upvar, found {:?}", base),
997+
};
998+
999+
let mut captured_place_expr = Expr {
9981000
temp_lifetime,
9991001
ty: var_ty,
10001002
span: closure_expr.span,
10011003
kind: convert_var(cx, var_hir_id),
10021004
};
1005+
1006+
for proj in captured_place.place.projections.iter() {
1007+
let kind = match proj.kind {
1008+
HirProjectionKind::Deref => ExprKind::Deref { arg: captured_place_expr.to_ref() },
1009+
HirProjectionKind::Field(field, ..) => {
1010+
// Variant index will always be 0, because for multi-variant
1011+
// enums, we capture the enum entirely.
1012+
ExprKind::Field {
1013+
lhs: captured_place_expr.to_ref(),
1014+
name: Field::new(field as usize),
1015+
}
1016+
}
1017+
HirProjectionKind::Index | HirProjectionKind::Subslice => {
1018+
// We don't capture these projections, so we can ignore them here
1019+
continue;
1020+
}
1021+
};
1022+
1023+
captured_place_expr = Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind };
1024+
}
1025+
10031026
match upvar_capture {
1004-
ty::UpvarCapture::ByValue(_) => captured_var.to_ref(),
1027+
ty::UpvarCapture::ByValue(_) => captured_place_expr.to_ref(),
10051028
ty::UpvarCapture::ByRef(upvar_borrow) => {
10061029
let borrow_kind = match upvar_borrow.kind {
10071030
ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
@@ -1012,7 +1035,7 @@ fn capture_upvar<'tcx>(
10121035
temp_lifetime,
10131036
ty: upvar_ty,
10141037
span: closure_expr.span,
1015-
kind: ExprKind::Borrow { borrow_kind, arg: captured_var.to_ref() },
1038+
kind: ExprKind::Borrow { borrow_kind, arg: captured_place_expr.to_ref() },
10161039
}
10171040
.to_ref()
10181041
}

0 commit comments

Comments
 (0)