Skip to content

Commit 263a433

Browse files
committed
Ensure dataflow of a proc never looks at blocks from closed-over context.
Details: in a program like: ``` type T = proc(int) -> int; /* 4 */ pub fn outer(captured /* pat 16 */: T) -> T { (proc(x /* pat 23 */) { ((captured /* 29 */).foo((x /* 30 */)) /* 28 */) } /* block 27 */ /* 20 */) } /* block 19 */ /* 12 */ ``` the `captured` arg is moved from the outer fn into the inner proc (id=20). The old dataflow analysis for flowed_move_data_moves, when looking at the inner proc, would attempt to add a kill bit for `captured` at the end of its scope; the problem is that it thought the end of the `captured` arg's scope was the outer fn (id=12), even though at that point in the analysis, the `captured` arg's scope should now be restricted to the proc itself (id=20). This patch fixes handling of upvars so that dataflow of a fn/proc should never attempts to add a gen or kill bit to any NodeId outside of the current fn/proc. It accomplishes this by adding an `LpUpvar` variant to `borrowck::LoanPath`, so for cases like `captured` above will carry both their original `var_id`, as before, as well as the `NodeId` for the closure that is capturing them. As a drive-by fix to another occurrence of a similar bug that nikomatsakis pointed out to me earlier, this also fixes `gather_loans::compute_kill_scope` so that it computes the kill scope of the `captured` arg to be block 27; that is, the block for the proc itself (id=20). (This is an updated version that generalizes the new loan path variant to cover all upvars, and thus renamed the variant from `LpCopiedUpvar` to just `LpUpvar`.)
1 parent 4d82456 commit 263a433

File tree

7 files changed

+82
-42
lines changed

7 files changed

+82
-42
lines changed

src/librustc/middle/borrowck/check_loans.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ impl<'a> CheckLoanCtxt<'a> {
242242
let mut loan_path = loan_path;
243243
loop {
244244
match *loan_path {
245-
LpVar(_) => {
245+
LpVar(_) | LpUpvar(_) => {
246246
break;
247247
}
248248
LpExtend(ref lp_base, _, _) => {
@@ -632,7 +632,7 @@ impl<'a> CheckLoanCtxt<'a> {
632632
*/
633633

634634
match **lp {
635-
LpVar(_) => {
635+
LpVar(_) | LpUpvar(_) => {
636636
// assigning to `x` does not require that `x` is initialized
637637
}
638638
LpExtend(ref lp_base, _, LpInterior(_)) => {

src/librustc/middle/borrowck/gather_loans/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,8 @@ impl<'a> GatherLoanCtxt<'a> {
395395
//! from a local variable, mark the mutability decl as necessary.
396396
397397
match *loan_path {
398-
LpVar(local_id) => {
398+
LpVar(local_id) |
399+
LpUpvar(ty::UpvarId{ var_id: local_id, closure_expr_id: _ }) => {
399400
self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
400401
}
401402
LpExtend(ref base, mc::McInherited, _) => {
@@ -445,8 +446,8 @@ impl<'a> GatherLoanCtxt<'a> {
445446
//! with immutable `&` pointers, because borrows of such pointers
446447
//! do not require restrictions and hence do not cause a loan.
447448
449+
let lexical_scope = lp.kill_scope(self.bccx.tcx);
448450
let rm = &self.bccx.tcx.region_maps;
449-
let lexical_scope = rm.var_scope(lp.node_id());
450451
if rm.is_subscope_of(lexical_scope, loan_scope) {
451452
lexical_scope
452453
} else {

src/librustc/middle/borrowck/gather_loans/restrictions.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,23 @@ impl<'a> RestrictionsContext<'a> {
6767
}
6868

6969
mc::cat_local(local_id) |
70-
mc::cat_arg(local_id) |
71-
mc::cat_upvar(ty::UpvarId {var_id: local_id, ..}, _) => {
72-
// R-Variable
70+
mc::cat_arg(local_id) => {
71+
// R-Variable, locally declared
7372
let lp = Rc::new(LpVar(local_id));
7473
SafeIf(lp.clone(), vec!(lp))
7574
}
7675

76+
mc::cat_upvar(upvar_id, _) => {
77+
// R-Variable, captured into closure
78+
let lp = Rc::new(LpUpvar(upvar_id));
79+
SafeIf(lp.clone(), vec!(lp))
80+
}
81+
82+
mc::cat_copied_upvar(..) => {
83+
// FIXME(#2152) allow mutation of upvars
84+
Safe
85+
}
86+
7787
mc::cat_downcast(cmt_base) => {
7888
// When we borrow the interior of an enum, we have to
7989
// ensure the enum itself is not mutated, because that
@@ -107,7 +117,6 @@ impl<'a> RestrictionsContext<'a> {
107117
self.extend(result, cmt.mutbl, LpDeref(pk))
108118
}
109119

110-
mc::cat_copied_upvar(..) | // FIXME(#2152) allow mutation of upvars
111120
mc::cat_static_item(..) => {
112121
Safe
113122
}

src/librustc/middle/borrowck/mod.rs

+35-7
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ pub struct Loan {
201201
#[deriving(PartialEq, Eq, Hash)]
202202
pub enum LoanPath {
203203
LpVar(ast::NodeId), // `x` in doc.rs
204+
LpUpvar(ty::UpvarId), // `x` captured by-value into closure
204205
LpExtend(Rc<LoanPath>, mc::MutabilityCategory, LoanPathElem)
205206
}
206207

@@ -210,11 +211,25 @@ pub enum LoanPathElem {
210211
LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
211212
}
212213

214+
pub fn closure_to_block(closure_id: ast::NodeId,
215+
tcx: &ty::ctxt) -> ast::NodeId {
216+
match tcx.map.get(closure_id) {
217+
ast_map::NodeExpr(expr) => match expr.node {
218+
ast::ExprProc(_decl, block) |
219+
ast::ExprFnBlock(_decl, block) => { block.id }
220+
_ => fail!("encountered non-closure id: {}", closure_id)
221+
},
222+
_ => fail!("encountered non-expr id: {}", closure_id)
223+
}
224+
}
225+
213226
impl LoanPath {
214-
pub fn node_id(&self) -> ast::NodeId {
227+
pub fn kill_scope(&self, tcx: &ty::ctxt) -> ast::NodeId {
215228
match *self {
216-
LpVar(local_id) => local_id,
217-
LpExtend(ref base, _, _) => base.node_id()
229+
LpVar(local_id) => tcx.region_maps.var_scope(local_id),
230+
LpUpvar(upvar_id) =>
231+
closure_to_block(upvar_id.closure_expr_id, tcx),
232+
LpExtend(ref base, _, _) => base.kill_scope(tcx),
218233
}
219234
}
220235
}
@@ -234,12 +249,18 @@ pub fn opt_loan_path(cmt: &mc::cmt) -> Option<Rc<LoanPath>> {
234249
}
235250

236251
mc::cat_local(id) |
237-
mc::cat_arg(id) |
238-
mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id: id, .. }) |
239-
mc::cat_upvar(ty::UpvarId {var_id: id, ..}, _) => {
252+
mc::cat_arg(id) => {
240253
Some(Rc::new(LpVar(id)))
241254
}
242255

256+
mc::cat_upvar(ty::UpvarId {var_id: id, closure_expr_id: proc_id}, _) |
257+
mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id: id,
258+
onceness: _,
259+
capturing_proc: proc_id }) => {
260+
let upvar_id = ty::UpvarId{ var_id: id, closure_expr_id: proc_id };
261+
Some(Rc::new(LpUpvar(upvar_id)))
262+
}
263+
243264
mc::cat_deref(ref cmt_base, _, pk) => {
244265
opt_loan_path(cmt_base).map(|lp| {
245266
Rc::new(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
@@ -693,6 +714,7 @@ impl<'a> BorrowckCtxt<'a> {
693714
loan_path: &LoanPath,
694715
out: &mut String) {
695716
match *loan_path {
717+
LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
696718
LpVar(id) => {
697719
out.push_str(ty::local_var_name_str(self.tcx, id).get());
698720
}
@@ -734,7 +756,7 @@ impl<'a> BorrowckCtxt<'a> {
734756
self.append_autoderefd_loan_path_to_str(&**lp_base, out)
735757
}
736758

737-
LpVar(..) | LpExtend(_, _, LpInterior(..)) => {
759+
LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
738760
self.append_loan_path_to_str(loan_path, out)
739761
}
740762
}
@@ -796,6 +818,12 @@ impl Repr for LoanPath {
796818
(format!("$({})", tcx.map.node_to_str(id))).to_string()
797819
}
798820

821+
&LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
822+
let s = tcx.map.node_to_str(var_id);
823+
let s = format!("$({} captured by id={})", s, closure_expr_id);
824+
s.to_string()
825+
}
826+
799827
&LpExtend(ref lp, _, LpDeref(_)) => {
800828
(format!("{}.*", lp.repr(tcx))).to_string()
801829
}

src/librustc/middle/borrowck/move_data.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ impl MoveData {
231231
}
232232

233233
let index = match *lp {
234-
LpVar(..) => {
234+
LpVar(..) | LpUpvar(..) => {
235235
let index = MovePathIndex(self.paths.borrow().len());
236236

237237
self.paths.borrow_mut().push(MovePath {
@@ -302,7 +302,7 @@ impl MoveData {
302302
}
303303
None => {
304304
match **lp {
305-
LpVar(..) => { }
305+
LpVar(..) | LpUpvar(..) => { }
306306
LpExtend(ref b, _, _) => {
307307
self.add_existing_base_paths(b, result);
308308
}
@@ -418,6 +418,11 @@ impl MoveData {
418418
let path = *self.path_map.borrow().get(&path.loan_path);
419419
self.kill_moves(path, kill_id, dfcx_moves);
420420
}
421+
LpUpvar(ty::UpvarId { var_id: _, closure_expr_id }) => {
422+
let kill_id = closure_to_block(closure_expr_id, tcx);
423+
let path = *self.path_map.borrow().get(&path.loan_path);
424+
self.kill_moves(path, kill_id, dfcx_moves);
425+
}
421426
LpExtend(..) => {}
422427
}
423428
}
@@ -430,6 +435,10 @@ impl MoveData {
430435
let kill_id = tcx.region_maps.var_scope(id);
431436
dfcx_assign.add_kill(kill_id, assignment_index);
432437
}
438+
LpUpvar(ty::UpvarId { var_id: _, closure_expr_id }) => {
439+
let kill_id = closure_to_block(closure_expr_id, tcx);
440+
dfcx_assign.add_kill(kill_id, assignment_index);
441+
}
433442
LpExtend(..) => {
434443
tcx.sess.bug("var assignment for non var path");
435444
}

src/librustc/middle/dataflow.rs

+14-24
Original file line numberDiff line numberDiff line change
@@ -266,34 +266,24 @@ impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> {
266266

267267
pub fn add_gen(&mut self, id: ast::NodeId, bit: uint) {
268268
//! Indicates that `id` generates `bit`
269-
if self.nodeid_to_index.contains_key(&id) {
270-
debug!("add_gen(id={:?}, bit={:?})", id, bit);
271-
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
272-
let (start, end) = self.compute_id_range(cfgidx);
273-
{
274-
let gens = self.gens.mut_slice(start, end);
275-
set_bit(gens, bit);
276-
}
277-
} else {
278-
debug!("{:s} add_gen skip (id={:?}, bit={:?}); id not in current fn",
279-
self.analysis_name, id, bit);
280-
}
269+
debug!("{:s} add_gen(id={:?}, bit={:?})",
270+
self.analysis_name, id, bit);
271+
assert!(self.nodeid_to_index.contains_key(&id));
272+
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
273+
let (start, end) = self.compute_id_range(cfgidx);
274+
let gens = self.gens.mut_slice(start, end);
275+
set_bit(gens, bit);
281276
}
282277

283278
pub fn add_kill(&mut self, id: ast::NodeId, bit: uint) {
284279
//! Indicates that `id` kills `bit`
285-
if self.nodeid_to_index.contains_key(&id) {
286-
debug!("add_kill(id={:?}, bit={:?})", id, bit);
287-
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
288-
let (start, end) = self.compute_id_range(cfgidx);
289-
{
290-
let kills = self.kills.mut_slice(start, end);
291-
set_bit(kills, bit);
292-
}
293-
} else {
294-
debug!("{:s} add_kill skip (id={:?}, bit={:?}); id not in current fn",
295-
self.analysis_name, id, bit);
296-
}
280+
debug!("{:s} add_kill(id={:?}, bit={:?})",
281+
self.analysis_name, id, bit);
282+
assert!(self.nodeid_to_index.contains_key(&id));
283+
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
284+
let (start, end) = self.compute_id_range(cfgidx);
285+
let kills = self.kills.mut_slice(start, end);
286+
set_bit(kills, bit);
297287
}
298288

299289
fn apply_gen_kill(&mut self, cfgidx: CFGIndex, bits: &mut [uint]) {

src/librustc/middle/mem_categorization.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub enum categorization {
9797
pub struct CopiedUpvar {
9898
pub upvar_id: ast::NodeId,
9999
pub onceness: ast::Onceness,
100+
pub capturing_proc: ast::NodeId,
100101
}
101102

102103
// different kinds of pointers:
@@ -559,7 +560,9 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> {
559560
span:span,
560561
cat:cat_copied_upvar(CopiedUpvar {
561562
upvar_id: var_id,
562-
onceness: closure_ty.onceness}),
563+
onceness: closure_ty.onceness,
564+
capturing_proc: fn_node_id,
565+
}),
563566
mutbl:McImmutable,
564567
ty:expr_ty
565568
}))

0 commit comments

Comments
 (0)