Skip to content

Commit e2efdd1

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 6a1d069 commit e2efdd1

File tree

4 files changed

+109
-63
lines changed

4 files changed

+109
-63
lines changed

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

+8-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_index::vec::Idx;
1818

1919
/// The "outermost" place that holds this value.
2020
#[derive(Copy, Clone)]
21-
pub enum PlaceBase {
21+
crate enum PlaceBase {
2222
/// Denotes the start of a `Place`.
2323
Local(Local),
2424

@@ -67,7 +67,7 @@ pub enum PlaceBase {
6767
/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
6868
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
6969
#[derive(Clone)]
70-
struct PlaceBuilder<'tcx> {
70+
crate struct PlaceBuilder<'tcx> {
7171
base: PlaceBase,
7272
projection: Vec<PlaceElem<'tcx>>,
7373
}
@@ -279,7 +279,7 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
279279
}
280280

281281
impl<'tcx> PlaceBuilder<'tcx> {
282-
fn into_place<'a>(
282+
crate fn into_place<'a>(
283283
self,
284284
tcx: TyCtxt<'tcx>,
285285
typeck_results: &'a ty::TypeckResults<'tcx>,
@@ -299,6 +299,10 @@ impl<'tcx> PlaceBuilder<'tcx> {
299299
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
300300
}
301301

302+
crate fn base(&self) -> PlaceBase {
303+
self.base
304+
}
305+
302306
fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
303307
self.project(PlaceElem::Field(f, ty))
304308
}
@@ -352,7 +356,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
352356

353357
/// This is used when constructing a compound `Place`, so that we can avoid creating
354358
/// intermediate `Place` values until we know the full set of projections.
355-
fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
359+
crate fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
356360
where
357361
M: Mirror<'tcx, Output = Expr<'tcx>>,
358362
{

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

+41-37
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;
@@ -393,51 +394,54 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
393394

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

396-
let arg_place = unpack!(block = this.as_place(block, arg));
397-
398-
let mutability = match arg_place.as_ref() {
399-
PlaceRef { local, projection: &[] } => this.local_decls[local].mutability,
400-
PlaceRef { local, projection: &[ProjectionElem::Deref] } => {
401-
debug_assert!(
402-
this.local_decls[local].is_ref_for_guard(),
403-
"Unexpected capture place",
404-
);
405-
this.local_decls[local].mutability
406-
}
407-
PlaceRef {
408-
local,
409-
projection: &[ref proj_base @ .., ProjectionElem::Field(upvar_index, _)],
410-
}
411-
| PlaceRef {
412-
local,
413-
projection:
414-
&[ref proj_base @ .., ProjectionElem::Field(upvar_index, _), ProjectionElem::Deref],
415-
} => {
416-
let place = PlaceRef { local, projection: proj_base };
417-
418-
// Not projected from the implicit `self` in a closure.
419-
debug_assert!(
420-
match place.local_or_deref_local() {
421-
Some(local) => local == Local::new(1),
422-
None => false,
423-
},
424-
"Unexpected capture place"
425-
);
426-
// Not in a closure
427-
debug_assert!(
428-
this.upvar_mutbls.len() > upvar_index.index(),
429-
"Unexpected capture place"
430-
);
431-
this.upvar_mutbls[upvar_index.index()]
397+
let arg_place_builder = unpack!(block = this.as_place_builder(block, arg));
398+
399+
let mutability = match arg_place_builder.base() {
400+
// We are capturing a path that starts off a local variable in the parent.
401+
// The mutability of the current capture is same as the mutability
402+
// of the local declaration in the parent.
403+
PlaceBase::Local(local) => this.local_decls[local].mutability,
404+
// Parent is a closure and we are capturing a path that is captured
405+
// by the parent itself. The mutability of the current capture
406+
// is same as that of the capture in the parent closure.
407+
PlaceBase::Upvar { .. } => {
408+
let enclosing_upvars_resolved = arg_place_builder.clone().into_place(
409+
this.hir.tcx(),
410+
this.hir.typeck_results());
411+
412+
match enclosing_upvars_resolved.as_ref() {
413+
PlaceRef { local, projection: &[ProjectionElem::Field(upvar_index, _), ..] }
414+
| PlaceRef {
415+
local,
416+
projection: &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..] } => {
417+
// Not in a closure
418+
debug_assert!(
419+
local == Local::new(1),
420+
"Expected local to be Local(1), found {:?}",
421+
local
422+
);
423+
// Not in a closure
424+
debug_assert!(
425+
this.upvar_mutbls.len() > upvar_index.index(),
426+
"Unexpected capture place, upvar_mutbls={:#?}, upvar_index={:?}",
427+
this.upvar_mutbls, upvar_index
428+
);
429+
this.upvar_mutbls[upvar_index.index()]
430+
}
431+
_ => bug!("Unexpected capture place"),
432+
}
432433
}
433-
_ => bug!("Unexpected capture place"),
434434
};
435435

436436
let borrow_kind = match mutability {
437437
Mutability::Not => BorrowKind::Unique,
438438
Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
439439
};
440440

441+
let arg_place = arg_place_builder.into_place(
442+
this.hir.tcx(),
443+
this.hir.typeck_results());
444+
441445
this.cfg.push_assign(
442446
block,
443447
source_info,

compiler/rustc_mir_build/src/build/mod.rs

+16-6
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;
@@ -823,7 +824,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
823824
// with the closure's DefId. Here, we run through that vec of UpvarIds for
824825
// the given closure and use the necessary information to create upvar
825826
// debuginfo and to fill `self.upvar_mutbls`.
826-
if let Some(upvars) = hir_typeck_results.closure_captures.get(&fn_def_id) {
827+
if hir_typeck_results.closure_min_captures.get(&fn_def_id).is_some() {
827828
let closure_env_arg = Local::new(1);
828829
let mut closure_env_projs = vec![];
829830
let mut closure_ty = self.local_decls[closure_env_arg].ty;
@@ -836,14 +837,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
836837
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
837838
_ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty),
838839
};
839-
let upvar_tys = upvar_substs.upvar_tys();
840-
let upvars_with_tys = upvars.iter().zip(upvar_tys);
841-
self.upvar_mutbls = upvars_with_tys
840+
let capture_tys = upvar_substs.upvar_tys();
841+
let captures_with_tys = hir_typeck_results
842+
.closure_min_captures_flattened(fn_def_id)
843+
.zip(capture_tys);
844+
845+
self.upvar_mutbls = captures_with_tys
842846
.enumerate()
843-
.map(|(i, ((&var_id, &upvar_id), ty))| {
844-
let capture = hir_typeck_results.upvar_capture(upvar_id);
847+
.map(|(i, (captured_place, ty))| {
848+
let capture = captured_place.info.capture_kind;
849+
let var_id = match captured_place.place.base {
850+
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
851+
_ => bug!("Expected an upvar")
852+
};
845853

846854
let mut mutability = Mutability::Not;
855+
856+
// FIXME(project-rfc-2229#8): Store more precise information
847857
let mut name = kw::Invalid;
848858
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
849859
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {

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

+44-16
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,55 @@ 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+
// The result of capture analysis in `rustc_typeck/check/upvar.rs`represents a captured path
995+
// as it's seen for use within the closure and not at the time of closure creation.
996+
//
997+
// That is we see expect to see it start from a captured upvar and not something that is local
998+
// to the closure's parent.
999+
let var_hir_id = match captured_place.place.base {
1000+
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
1001+
base => bug!("Expected an upvar, found {:?}", base),
1002+
};
1003+
1004+
let mut captured_place_expr = Expr {
9981005
temp_lifetime,
9991006
ty: var_ty,
10001007
span: closure_expr.span,
10011008
kind: convert_var(cx, var_hir_id),
10021009
};
1010+
1011+
for proj in captured_place.place.projections.iter() {
1012+
let kind = match proj.kind {
1013+
HirProjectionKind::Deref => ExprKind::Deref { arg: captured_place_expr.to_ref() },
1014+
HirProjectionKind::Field(field, ..) => {
1015+
// Variant index will always be 0, because for multi-variant
1016+
// enums, we capture the enum entirely.
1017+
ExprKind::Field {
1018+
lhs: captured_place_expr.to_ref(),
1019+
name: Field::new(field as usize),
1020+
}
1021+
}
1022+
HirProjectionKind::Index | HirProjectionKind::Subslice => {
1023+
// We don't capture these projections, so we can ignore them here
1024+
continue;
1025+
}
1026+
};
1027+
1028+
captured_place_expr = Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind };
1029+
}
1030+
10031031
match upvar_capture {
1004-
ty::UpvarCapture::ByValue(_) => captured_var.to_ref(),
1032+
ty::UpvarCapture::ByValue(_) => captured_place_expr.to_ref(),
10051033
ty::UpvarCapture::ByRef(upvar_borrow) => {
10061034
let borrow_kind = match upvar_borrow.kind {
10071035
ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
@@ -1012,7 +1040,7 @@ fn capture_upvar<'tcx>(
10121040
temp_lifetime,
10131041
ty: upvar_ty,
10141042
span: closure_expr.span,
1015-
kind: ExprKind::Borrow { borrow_kind, arg: captured_var.to_ref() },
1043+
kind: ExprKind::Borrow { borrow_kind, arg: captured_place_expr.to_ref() },
10161044
}
10171045
.to_ref()
10181046
}

0 commit comments

Comments
 (0)