Skip to content

Commit 1e68021

Browse files
arora-amanroxelo
andcommitted
Use Places for captures in MIR
- Use closure_min_capture maps to capture precise paths - PlaceBuilder now searches for ancestors in min_capture list - Add API to `Ty` to allow access to the n-th element in a tuple in O(1) time. Co-authored-by: Roxane Fruytier <[email protected]>
1 parent 6706033 commit 1e68021

File tree

3 files changed

+163
-47
lines changed

3 files changed

+163
-47
lines changed

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2179,6 +2179,15 @@ impl<'tcx> TyS<'tcx> {
21792179
}
21802180
}
21812181

2182+
/// Get the `i`-th element of a tuple.
2183+
/// Panics when called on anything but a tuple.
2184+
pub fn tuple_get_field(&self, i: usize) -> Option<Ty<'tcx>> {
2185+
match self.kind() {
2186+
Tuple(substs) => substs.iter().nth(i).map(|field| field.expect_ty()),
2187+
_ => bug!("tuple_fields called on non-tuple"),
2188+
}
2189+
}
2190+
21822191
/// If the type contains variants, returns the valid range of variant indices.
21832192
//
21842193
// FIXME: This requires the optimized MIR in the case of generators.

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

Lines changed: 152 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ use crate::thir::*;
77
use rustc_hir::def_id::DefId;
88
use rustc_hir::HirId;
99
use rustc_middle::middle::region;
10+
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
1011
use rustc_middle::mir::AssertKind::BoundsCheck;
1112
use rustc_middle::mir::*;
1213
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
1314
use rustc_span::Span;
15+
use rustc_target::abi::VariantIdx;
1416

1517
use rustc_index::vec::Idx;
1618

@@ -32,59 +34,141 @@ struct PlaceBuilder<'tcx> {
3234
projection: Vec<PlaceElem<'tcx>>,
3335
}
3436

35-
fn capture_matching_projections<'a, 'tcx>(
36-
tcx: TyCtxt<'tcx>,
37-
typeck_results: &'a ty::TypeckResults<'tcx>,
37+
/// Given a list of MIR projections, convert them to list of hir ProjectionKind.
38+
fn convert_to_hir_projections_for_capture<'tcx>(
39+
mir_projections: &Vec<PlaceElem<'tcx>>) -> Vec<HirProjectionKind> {
40+
41+
let mut hir_projections = Vec::new();
42+
43+
for mir_projection in mir_projections {
44+
let hir_projection = match mir_projection {
45+
ProjectionElem::Deref => HirProjectionKind::Deref,
46+
ProjectionElem::Field(field, _) => {
47+
// We will never encouter this for multivariant enums,
48+
// read the comment for `Downcast`.
49+
HirProjectionKind::Field(field.index() as u32, VariantIdx::new(0))
50+
},
51+
ProjectionElem::Downcast(..) => {
52+
// This projections exist only for enums that have
53+
// multiple variants. Since such enums that are captured
54+
// completely, we can stop here.
55+
break
56+
},
57+
ProjectionElem::Index(..)
58+
| ProjectionElem::ConstantIndex { .. }
59+
| ProjectionElem::Subslice { .. } => {
60+
// We don't capture array-access projections.
61+
// We can stop here as arrays are captured completely.
62+
break
63+
},
64+
};
65+
66+
hir_projections.push(hir_projection);
67+
}
68+
69+
hir_projections
70+
}
71+
72+
fn is_ancestor_of_capture(
73+
proj_possible_ancestor: &Vec<HirProjectionKind>,
74+
proj_capture: &Vec<HirProjectionKind>) -> bool {
75+
76+
for (proj_ans, proj_capture) in proj_possible_ancestor.iter().zip(proj_capture.iter()) {
77+
if proj_ans != proj_capture {
78+
return false;
79+
}
80+
}
81+
82+
// In cases like
83+
// ```rust,ignore(wont-compile-because-pseudocode)
84+
// let c = || {
85+
// x.0.1 += 1;
86+
// let _ = x.0;
87+
// }
88+
// ```
89+
//
90+
// Here with `capture_disjoint_fields` enabled, x.0 won't be
91+
// captured but x.0.1 would be.
92+
//
93+
// Therefore we can't just return true here.
94+
proj_possible_ancestor.len() <= proj_capture.len()
95+
}
96+
97+
/// Computes the index of a capture within the desugared closure provided the closure's
98+
/// `closure_min_captures` and the capture's index of the capture in the
99+
/// `ty::MinCaptureList` of the root variable `var_hir_id`.
100+
fn compute_capture_idx<'tcx>(
101+
closure_min_captures: &ty::RootVariableMinCaptureList<'tcx>,
38102
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-
)
103+
root_var_idx: usize) -> usize {
104+
let mut res = 0;
105+
for (var_id, capture_list) in closure_min_captures {
106+
if *var_id == var_hir_id {
107+
res += root_var_idx;
108+
break;
109+
} else {
110+
res += capture_list.len();
55111
}
56112
}
57113

58-
// Unwrap until the FIXME has been resolved
59-
let (capture_index, _, _) = capture.unwrap();
114+
res
115+
}
60116

61-
let closure_ty =
62-
typeck_results.node_type(tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()));
117+
/// Given a closure, returns the index of a capture within the desugared closure struct and the
118+
/// `ty::CapturedPlace` which is the ancestor of the Place represented using the `var_hir_id`
119+
/// and `projection`.
120+
///
121+
/// Note there will be atmost one ancestor for any given Place.
122+
///
123+
/// Returns None, when the ancestor is not found.
124+
fn find_capture_matching_projections<'a, 'tcx>(
125+
typeck_results: &'a ty::TypeckResults<'tcx>,
126+
var_hir_id: HirId,
127+
closure_def_id: DefId,
128+
projections: &Vec<PlaceElem<'tcx>>,
129+
) -> Option<(usize, &'a ty::CapturedPlace<'tcx>)> {
130+
let closure_min_captures = typeck_results
131+
.closure_min_captures
132+
.get(&closure_def_id);
133+
134+
let closure_min_captures = if let Some(min_captures) = closure_min_captures {
135+
min_captures
136+
} else {
137+
return None;
138+
};
63139

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),
140+
let root_variable_min_captures =
141+
if let Some(captures) = closure_min_captures.get(&var_hir_id) {
142+
captures
143+
} else {
144+
return None
68145
};
69146

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();
147+
let hir_projections = convert_to_hir_projections_for_capture(projections);
148+
149+
let find_ancestor = || {
150+
for (i, capture) in root_variable_min_captures.iter().enumerate() {
151+
let possible_ancestor_proj_kinds =
152+
capture.place.projections.iter().map(|proj| proj.kind).collect();
153+
if is_ancestor_of_capture(&possible_ancestor_proj_kinds, &hir_projections) {
154+
return Some((i, capture));
155+
}
156+
}
157+
return None;
158+
};
76159

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);
79160

80-
Some((capture_index, var_ty, capture_kind))
161+
if let Some((idx, capture)) = find_ancestor() {
162+
Some((compute_capture_idx(closure_min_captures, var_hir_id, idx), capture))
163+
} else {
164+
None
165+
}
81166
}
82167

83-
/// Takes a PlaceBuilder and resolves the upvar (if any) within it,
84-
/// so that the PlaceBuilder now starts from PlaceBase::Local.
168+
/// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the
169+
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
85170
///
86-
/// Returns a Result with the error being the HirId of the
87-
/// Upvar that was not found.
171+
/// Returns a Result with the error being the HirId of the Upvar that was not found.
88172
fn to_upvars_resolved_place_builder<'a, 'tcx>(
89173
from_builder: PlaceBuilder<'tcx>,
90174
tcx: TyCtxt<'tcx>,
@@ -104,29 +188,50 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
104188
ty::ClosureKind::FnOnce => {}
105189
}
106190

107-
let (capture_index, var_ty, capture_kind) =
108-
if let Some(capture_details) = capture_matching_projections(
109-
tcx,
191+
let (capture_index, capture) =
192+
if let Some(capture_details) = find_capture_matching_projections(
110193
typeck_results,
111194
var_hir_id,
112195
closure_def_id,
113196
&from_builder.projection,
114197
) {
115198
capture_details
116199
} else {
200+
if !tcx.features().capture_disjoint_fields {
201+
bug!(
202+
"No associated capture found for {:?}[{:#?}] even though \
203+
capture_disjoint_fields isn't enabled",
204+
var_hir_id,
205+
from_builder.projection,
206+
)
207+
} else {
208+
// FIXME(project-rfc-2229#24): Handle this case properly
209+
println!(
210+
"No associated capture found for {:?}[{:#?}]",
211+
var_hir_id,
212+
from_builder.projection,
213+
)
214+
}
117215
return Err(var_hir_id);
118216
};
119217

218+
let closure_ty = typeck_results
219+
.node_type(tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()));
220+
let substs = match closure_ty.kind() {
221+
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
222+
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
223+
_ => bug!("Lowering capture for non-closure type {:?}", closure_ty)
224+
};
225+
226+
let var_ty = substs.tupled_upvars_ty().tuple_get_field(capture_index).unwrap();
120227
place_builder = place_builder.field(Field::new(capture_index), var_ty);
121228

122-
// If the variable is captured via ByRef(Immutable/Mutable) Borrow,
123-
// we need to deref it
124-
place_builder = match capture_kind {
229+
place_builder = match capture.info.capture_kind {
125230
ty::UpvarCapture::ByRef(_) => place_builder.deref(),
126231
ty::UpvarCapture::ByValue(_) => place_builder,
127232
};
128233

129-
let next_projection = 0;
234+
let next_projection = capture.place.projections.len();
130235
let mut curr_projections = from_builder.projection;
131236
place_builder.projection.extend(curr_projections.drain(next_projection..));
132237

compiler/rustc_mir_build/src/build/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
832832
let capture = hir_typeck_results.upvar_capture(upvar_id);
833833

834834
let mut mutability = Mutability::Not;
835+
836+
// FIXME(project-rfc-2229#8): Store more precise information
835837
let mut name = kw::Invalid;
836838
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
837839
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {

0 commit comments

Comments
 (0)