Skip to content

Commit 61fcbfc

Browse files
committed
rustc_mir: don't rely on mir::UpvarDecl in the MIR borrowck.
1 parent 3bee49f commit 61fcbfc

File tree

12 files changed

+159
-91
lines changed

12 files changed

+159
-91
lines changed

src/librustc/mir/tcx.rs

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -133,41 +133,6 @@ impl<'tcx> Place<'tcx> {
133133
proj.base.ty(local_decls, tcx).projection_ty(tcx, &proj.elem),
134134
}
135135
}
136-
137-
/// If this is a field projection, and the field is being projected from a closure type,
138-
/// then returns the index of the field being projected. Note that this closure will always
139-
/// be `self` in the current MIR, because that is the only time we directly access the fields
140-
/// of a closure type.
141-
pub fn is_upvar_field_projection<'cx, 'gcx>(&self, mir: &'cx Mir<'tcx>,
142-
tcx: &TyCtxt<'cx, 'gcx, 'tcx>) -> Option<Field> {
143-
let (place, by_ref) = if let Place::Projection(ref proj) = self {
144-
if let ProjectionElem::Deref = proj.elem {
145-
(&proj.base, true)
146-
} else {
147-
(self, false)
148-
}
149-
} else {
150-
(self, false)
151-
};
152-
153-
match place {
154-
Place::Projection(ref proj) => match proj.elem {
155-
ProjectionElem::Field(field, _ty) => {
156-
let base_ty = proj.base.ty(mir, *tcx).ty;
157-
158-
if (base_ty.is_closure() || base_ty.is_generator()) &&
159-
(!by_ref || mir.upvar_decls[field.index()].by_ref)
160-
{
161-
Some(field)
162-
} else {
163-
None
164-
}
165-
},
166-
_ => None,
167-
}
168-
_ => None,
169-
}
170-
}
171136
}
172137

173138
pub enum RvalueInitializationState {

src/librustc_mir/borrow_check/error_reporting.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10881088
| LocalKind::Temp => bug!("temporary or return pointer with a name"),
10891089
LocalKind::Var => "local variable ",
10901090
LocalKind::Arg
1091-
if !self.mir.upvar_decls.is_empty()
1091+
if !self.upvars.is_empty()
10921092
&& local == Local::new(1) => {
10931093
"variable captured by `move` "
10941094
}
@@ -1632,11 +1632,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16321632
match proj.elem {
16331633
ProjectionElem::Deref => {
16341634
let upvar_field_projection =
1635-
place.is_upvar_field_projection(self.mir, &self.infcx.tcx);
1635+
self.is_upvar_field_projection(place);
16361636
if let Some(field) = upvar_field_projection {
16371637
let var_index = field.index();
1638-
let name = self.mir.upvar_decls[var_index].debug_name.to_string();
1639-
if self.mir.upvar_decls[var_index].by_ref {
1638+
let name = self.upvars[var_index].name.to_string();
1639+
if self.upvars[var_index].by_ref {
16401640
buf.push_str(&name);
16411641
} else {
16421642
buf.push_str(&format!("*{}", &name));
@@ -1694,10 +1694,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16941694
autoderef = true;
16951695

16961696
let upvar_field_projection =
1697-
place.is_upvar_field_projection(self.mir, &self.infcx.tcx);
1697+
self.is_upvar_field_projection(place);
16981698
if let Some(field) = upvar_field_projection {
16991699
let var_index = field.index();
1700-
let name = self.mir.upvar_decls[var_index].debug_name.to_string();
1700+
let name = self.upvars[var_index].name.to_string();
17011701
buf.push_str(&name);
17021702
} else {
17031703
let field_name = self.describe_field(&proj.base, field);

src/librustc_mir/borrow_check/mod.rs

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! This query borrow-checks the MIR to (further) ensure it is not broken.
22
33
use crate::borrow_check::nll::region_infer::RegionInferenceContext;
4-
use rustc::hir;
4+
use rustc::hir::{self, HirId};
55
use rustc::hir::Node;
66
use rustc::hir::def_id::DefId;
77
use rustc::infer::InferCtxt;
@@ -27,6 +27,7 @@ use std::collections::BTreeMap;
2727
use std::mem;
2828
use std::rc::Rc;
2929

30+
use syntax::ast::Name;
3031
use syntax_pos::{Span, DUMMY_SP};
3132

3233
use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex};
@@ -63,6 +64,19 @@ mod used_muts;
6364

6465
pub(crate) mod nll;
6566

67+
// FIXME(eddyb) perhaps move this somewhere more centrally.
68+
#[derive(Debug)]
69+
crate struct Upvar {
70+
name: Name,
71+
72+
var_hir_id: HirId,
73+
74+
/// If true, the capture is behind a reference.
75+
by_ref: bool,
76+
77+
mutability: Mutability,
78+
}
79+
6680
pub fn provide(providers: &mut Providers<'_>) {
6781
*providers = Providers {
6882
mir_borrowck,
@@ -126,6 +140,36 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
126140
.as_local_hir_id(def_id)
127141
.expect("do_mir_borrowck: non-local DefId");
128142

143+
// Gather the upvars of a closure, if any.
144+
let tables = tcx.typeck_tables_of(def_id);
145+
let upvars: Vec<_> = tables
146+
.upvar_list
147+
.get(&def_id)
148+
.into_iter()
149+
.flatten()
150+
.map(|upvar_id| {
151+
let var_hir_id = upvar_id.var_path.hir_id;
152+
let var_node_id = tcx.hir().hir_to_node_id(var_hir_id);
153+
let capture = tables.upvar_capture(*upvar_id);
154+
let by_ref = match capture {
155+
ty::UpvarCapture::ByValue => false,
156+
ty::UpvarCapture::ByRef(..) => true,
157+
};
158+
let mut upvar = Upvar {
159+
name: tcx.hir().name(var_node_id),
160+
var_hir_id,
161+
by_ref,
162+
mutability: Mutability::Not,
163+
};
164+
let bm = *tables.pat_binding_modes().get(var_hir_id)
165+
.expect("missing binding mode");
166+
if bm == ty::BindByValue(hir::MutMutable) {
167+
upvar.mutability = Mutability::Mut;
168+
}
169+
upvar
170+
})
171+
.collect();
172+
129173
// Replace all regions with fresh inference variables. This
130174
// requires first making our own copy of the MIR. This copy will
131175
// be modified (in place) to contain non-lexical lifetimes. It
@@ -168,6 +212,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
168212
def_id,
169213
free_regions,
170214
mir,
215+
&upvars,
171216
location_table,
172217
param_env,
173218
&mut flow_inits,
@@ -240,6 +285,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
240285
used_mut_upvars: SmallVec::new(),
241286
borrow_set,
242287
dominators,
288+
upvars,
243289
};
244290

245291
let mut state = Flows::new(
@@ -475,6 +521,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
475521

476522
/// Dominators for MIR
477523
dominators: Dominators<BasicBlock>,
524+
525+
/// Information about upvars not necessarily preserved in types or MIR
526+
upvars: Vec<Upvar>,
478527
}
479528

480529
// Check that:
@@ -1287,8 +1336,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12871336
let propagate_closure_used_mut_place = |this: &mut Self, place: &Place<'tcx>| {
12881337
match *place {
12891338
Place::Projection { .. } => {
1290-
if let Some(field) = place.is_upvar_field_projection(
1291-
this.mir, &this.infcx.tcx) {
1339+
if let Some(field) = this.is_upvar_field_projection(place) {
12921340
this.used_mut_upvars.push(field);
12931341
}
12941342
}
@@ -2057,7 +2105,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
20572105
place: place @ Place::Projection(_),
20582106
is_local_mutation_allowed: _,
20592107
} => {
2060-
if let Some(field) = place.is_upvar_field_projection(self.mir, &self.infcx.tcx) {
2108+
if let Some(field) = self.is_upvar_field_projection(place) {
20612109
self.used_mut_upvars.push(field);
20622110
}
20632111
}
@@ -2127,13 +2175,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
21272175
// Mutably borrowed data is mutable, but only if we have a
21282176
// unique path to the `&mut`
21292177
hir::MutMutable => {
2130-
let mode = match place.is_upvar_field_projection(
2131-
self.mir, &self.infcx.tcx)
2132-
{
2178+
let mode = match self.is_upvar_field_projection(place) {
21332179
Some(field)
2134-
if {
2135-
self.mir.upvar_decls[field.index()].by_ref
2136-
} =>
2180+
if self.upvars[field.index()].by_ref =>
21372181
{
21382182
is_local_mutation_allowed
21392183
}
@@ -2173,15 +2217,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
21732217
| ProjectionElem::ConstantIndex { .. }
21742218
| ProjectionElem::Subslice { .. }
21752219
| ProjectionElem::Downcast(..) => {
2176-
let upvar_field_projection = place.is_upvar_field_projection(
2177-
self.mir, &self.infcx.tcx);
2220+
let upvar_field_projection = self.is_upvar_field_projection(place);
21782221
if let Some(field) = upvar_field_projection {
2179-
let decl = &self.mir.upvar_decls[field.index()];
2222+
let upvar = &self.upvars[field.index()];
21802223
debug!(
2181-
"decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
2182-
decl, is_local_mutation_allowed, place
2224+
"upvar.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
2225+
upvar, is_local_mutation_allowed, place
21832226
);
2184-
match (decl.mutability, is_local_mutation_allowed) {
2227+
match (upvar.mutability, is_local_mutation_allowed) {
21852228
(Mutability::Not, LocalMutationIsAllowed::No)
21862229
| (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars) => {
21872230
Err(place)
@@ -2229,6 +2272,41 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
22292272
}
22302273
}
22312274
}
2275+
2276+
/// If `place` is a field projection, and the field is being projected from a closure type,
2277+
/// then returns the index of the field being projected. Note that this closure will always
2278+
/// be `self` in the current MIR, because that is the only time we directly access the fields
2279+
/// of a closure type.
2280+
pub fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
2281+
let (place, by_ref) = if let Place::Projection(ref proj) = place {
2282+
if let ProjectionElem::Deref = proj.elem {
2283+
(&proj.base, true)
2284+
} else {
2285+
(place, false)
2286+
}
2287+
} else {
2288+
(place, false)
2289+
};
2290+
2291+
match place {
2292+
Place::Projection(ref proj) => match proj.elem {
2293+
ProjectionElem::Field(field, _ty) => {
2294+
let tcx = self.infcx.tcx;
2295+
let base_ty = proj.base.ty(self.mir, tcx).ty;
2296+
2297+
if (base_ty.is_closure() || base_ty.is_generator()) &&
2298+
(!by_ref || self.upvars[field.index()].by_ref)
2299+
{
2300+
Some(field)
2301+
} else {
2302+
None
2303+
}
2304+
},
2305+
_ => None,
2306+
}
2307+
_ => None,
2308+
}
2309+
}
22322310
}
22332311

22342312
#[derive(Copy, Clone, PartialEq, Eq, Debug)]

src/librustc_mir/borrow_check/move_errors.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
256256
let origin = Origin::Mir;
257257
debug!("report: original_path={:?} span={:?}, kind={:?} \
258258
original_path.is_upvar_field_projection={:?}", original_path, span, kind,
259-
original_path.is_upvar_field_projection(self.mir, &self.infcx.tcx));
259+
self.is_upvar_field_projection(original_path));
260260
(
261261
match kind {
262262
IllegalMoveOriginKind::Static => {
@@ -269,16 +269,15 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
269269
let ty = place.ty(self.mir, self.infcx.tcx).ty;
270270
let is_upvar_field_projection =
271271
self.prefixes(&original_path, PrefixSet::All)
272-
.any(|p| p.is_upvar_field_projection(self.mir, &self.infcx.tcx)
273-
.is_some());
272+
.any(|p| self.is_upvar_field_projection(p).is_some());
274273
debug!("report: ty={:?}", ty);
275274
match ty.sty {
276275
ty::Array(..) | ty::Slice(..) =>
277276
self.infcx.tcx.cannot_move_out_of_interior_noncopy(
278277
span, ty, None, origin
279278
),
280279
ty::Closure(def_id, closure_substs)
281-
if !self.mir.upvar_decls.is_empty() && is_upvar_field_projection
280+
if def_id == self.mir_def_id && is_upvar_field_projection
282281
=> {
283282
let closure_kind_ty =
284283
closure_substs.closure_kind_ty(def_id, self.infcx.tcx);
@@ -303,11 +302,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
303302
span, place_description, origin);
304303

305304
for prefix in self.prefixes(&original_path, PrefixSet::All) {
306-
if let Some(field) = prefix.is_upvar_field_projection(
307-
self.mir, &self.infcx.tcx) {
308-
let upvar_decl = &self.mir.upvar_decls[field.index()];
309-
let upvar_hir_id =
310-
upvar_decl.var_hir_id.assert_crate_local();
305+
if let Some(field) = self.is_upvar_field_projection(prefix) {
306+
let upvar_hir_id = self.upvars[field.index()].var_hir_id;
311307
let upvar_span = self.infcx.tcx.hir().span_by_hir_id(
312308
upvar_hir_id);
313309
diag.span_label(upvar_span, "captured outer variable");

src/librustc_mir/borrow_check/mutability_errors.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
6868
));
6969

7070
item_msg = format!("`{}`", access_place_desc.unwrap());
71-
if access_place.is_upvar_field_projection(self.mir, &self.infcx.tcx).is_some() {
71+
if self.is_upvar_field_projection(access_place).is_some() {
7272
reason = ", as it is not declared as mutable".to_string();
7373
} else {
74-
let name = self.mir.upvar_decls[upvar_index.index()].debug_name;
74+
let name = self.upvars[upvar_index.index()].name;
7575
reason = format!(", as `{}` is not declared as mutable", name);
7676
}
7777
}
@@ -81,15 +81,14 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
8181
elem: ProjectionElem::Deref,
8282
}) => {
8383
if *base == Place::Base(PlaceBase::Local(Local::new(1))) &&
84-
!self.mir.upvar_decls.is_empty() {
84+
!self.upvars.is_empty() {
8585
item_msg = format!("`{}`", access_place_desc.unwrap());
8686
debug_assert!(self.mir.local_decls[Local::new(1)].ty.is_region_ptr());
8787
debug_assert!(is_closure_or_generator(
8888
the_place_err.ty(self.mir, self.infcx.tcx).ty
8989
));
9090

91-
reason = if access_place.is_upvar_field_projection(self.mir,
92-
&self.infcx.tcx).is_some() {
91+
reason = if self.is_upvar_field_projection(access_place).is_some() {
9392
", as it is a captured variable in a `Fn` closure".to_string()
9493
} else {
9594
", as `Fn` closures cannot mutate their captured variables".to_string()
@@ -309,9 +308,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
309308

310309
err.span_label(span, format!("cannot {ACT}", ACT = act));
311310

312-
let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()]
313-
.var_hir_id
314-
.assert_crate_local();
311+
let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id;
315312
let upvar_node_id = self.infcx.tcx.hir().hir_to_node_id(upvar_hir_id);
316313
if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_node_id) {
317314
if let hir::PatKind::Binding(
@@ -452,7 +449,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
452449
base,
453450
elem: ProjectionElem::Deref,
454451
}) if *base == Place::Base(PlaceBase::Local(Local::new(1))) &&
455-
!self.mir.upvar_decls.is_empty() =>
452+
!self.upvars.is_empty() =>
456453
{
457454
err.span_label(span, format!("cannot {ACT}", ACT = act));
458455
err.span_help(

src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
298298
let (category, from_closure, span, region_name) =
299299
self.nonlexical_regioncx.free_region_constraint_info(
300300
self.mir,
301+
&self.upvars,
301302
self.mir_def_id,
302303
self.infcx,
303304
borrow_region_vid,

0 commit comments

Comments
 (0)