Skip to content

Commit 539cd9d

Browse files
committed
Dataflow changes and associated borrowck fix.
Revise rustc::middle::dataflow: one must select kill-kind when calling add_kill. The current kill-kinds are (1.) kills associated with ends-of-scopes and (2.) kills associated with the actual action of the expression/pattern. Then, use this to fix borrowck analysis so that it will not treat a break that pops through an assignment `x = { ... break; ... }` as a kill of the "moved-out" bit for `x`. Fix #24267.
1 parent a9d8065 commit 539cd9d

File tree

3 files changed

+90
-23
lines changed

3 files changed

+90
-23
lines changed

src/librustc/middle/dataflow.rs

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,12 @@ pub struct DataFlowContext<'a, 'tcx: 'a, O> {
6464
/// bits generated as we exit the cfg node. Updated by `add_gen()`.
6565
gens: Vec<usize>,
6666

67-
/// bits killed as we exit the cfg node. Updated by `add_kill()`.
68-
kills: Vec<usize>,
67+
/// bits killed as we exit the cfg node, or non-locally jump over
68+
/// it. Updated by `add_kill(KillFrom::ScopeEnd)`.
69+
scope_kills: Vec<usize>,
70+
71+
/// bits killed as we exit the cfg node directly. Updated by `add_kill(KillFrom::Execution)`.
72+
action_kills: Vec<usize>,
6973

7074
/// bits that are valid on entry to the cfg node. Updated by
7175
/// `propagate()`.
@@ -130,15 +134,23 @@ impl<'a, 'tcx, O:DataFlowOperator> pprust::PpAnn for DataFlowContext<'a, 'tcx, O
130134
"".to_string()
131135
};
132136

133-
let kills = &self.kills[start .. end];
134-
let kills_str = if kills.iter().any(|&u| u != 0) {
135-
format!(" kill: {}", bits_to_string(kills))
137+
let action_kills = &self.action_kills[start .. end];
138+
let action_kills_str = if action_kills.iter().any(|&u| u != 0) {
139+
format!(" action_kill: {}", bits_to_string(action_kills))
140+
} else {
141+
"".to_string()
142+
};
143+
144+
let scope_kills = &self.scope_kills[start .. end];
145+
let scope_kills_str = if scope_kills.iter().any(|&u| u != 0) {
146+
format!(" scope_kill: {}", bits_to_string(scope_kills))
136147
} else {
137148
"".to_string()
138149
};
139150

140-
try!(ps.synth_comment(format!("id {}: {}{}{}", id, entry_str,
141-
gens_str, kills_str)));
151+
try!(ps.synth_comment(
152+
format!("id {}: {}{}{}{}", id, entry_str,
153+
gens_str, action_kills_str, scope_kills_str)));
142154
try!(pp::space(&mut ps.s));
143155
}
144156
Ok(())
@@ -187,6 +199,25 @@ fn build_nodeid_to_index(decl: Option<&ast::FnDecl>,
187199
}
188200
}
189201

202+
/// Flag used by `add_kill` to indicate whether the provided kill
203+
/// takes effect only when control flows directly through the node in
204+
/// question, or if the kill's effect is associated with any
205+
/// control-flow directly through or indirectly over the node.
206+
#[derive(Copy, Clone, PartialEq, Debug)]
207+
pub enum KillFrom {
208+
/// A `ScopeEnd` kill is one that takes effect when any control
209+
/// flow goes over the node. A kill associated with the end of the
210+
/// scope of a variable declaration `let x;` is an example of a
211+
/// `ScopeEnd` kill.
212+
ScopeEnd,
213+
214+
/// An `Execution` kill is one that takes effect only when controw
215+
/// flow goes through the node to completion. A kill associated
216+
/// with an assignment statement `x = expr;` is an example of an
217+
/// `Execution` kill.
218+
Execution,
219+
}
220+
190221
impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
191222
pub fn new(tcx: &'a ty::ctxt<'tcx>,
192223
analysis_name: &'static str,
@@ -206,8 +237,10 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
206237

207238
let entry = if oper.initial_value() { usize::MAX } else {0};
208239

209-
let gens: Vec<_> = repeat(0).take(num_nodes * words_per_id).collect();
210-
let kills: Vec<_> = repeat(0).take(num_nodes * words_per_id).collect();
240+
let zeroes: Vec<_> = repeat(0).take(num_nodes * words_per_id).collect();
241+
let gens: Vec<_> = zeroes.clone();
242+
let kills1: Vec<_> = zeroes.clone();
243+
let kills2: Vec<_> = zeroes;
211244
let on_entry: Vec<_> = repeat(entry).take(num_nodes * words_per_id).collect();
212245

213246
let nodeid_to_index = build_nodeid_to_index(decl, cfg);
@@ -220,7 +253,8 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
220253
bits_per_id: bits_per_id,
221254
oper: oper,
222255
gens: gens,
223-
kills: kills,
256+
action_kills: kills1,
257+
scope_kills: kills2,
224258
on_entry: on_entry
225259
}
226260
}
@@ -240,7 +274,7 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
240274
}
241275
}
242276

243-
pub fn add_kill(&mut self, id: ast::NodeId, bit: usize) {
277+
pub fn add_kill(&mut self, kind: KillFrom, id: ast::NodeId, bit: usize) {
244278
//! Indicates that `id` kills `bit`
245279
debug!("{} add_kill(id={}, bit={})",
246280
self.analysis_name, id, bit);
@@ -250,7 +284,10 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
250284
let indices = get_cfg_indices(id, &self.nodeid_to_index);
251285
for &cfgidx in indices {
252286
let (start, end) = self.compute_id_range(cfgidx);
253-
let kills = &mut self.kills[start.. end];
287+
let kills = match kind {
288+
KillFrom::Execution => &mut self.action_kills[start.. end],
289+
KillFrom::ScopeEnd => &mut self.scope_kills[start.. end],
290+
};
254291
set_bit(kills, bit);
255292
}
256293
}
@@ -264,7 +301,9 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
264301
let (start, end) = self.compute_id_range(cfgidx);
265302
let gens = &self.gens[start.. end];
266303
bitwise(bits, gens, &Union);
267-
let kills = &self.kills[start.. end];
304+
let kills = &self.action_kills[start.. end];
305+
bitwise(bits, kills, &Subtract);
306+
let kills = &self.scope_kills[start.. end];
268307
bitwise(bits, kills, &Subtract);
269308

270309
debug!("{} apply_gen_kill(cfgidx={:?}, bits={}) [after]",
@@ -278,7 +317,8 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
278317

279318
assert!(start < self.gens.len());
280319
assert!(end <= self.gens.len());
281-
assert!(self.gens.len() == self.kills.len());
320+
assert!(self.gens.len() == self.action_kills.len());
321+
assert!(self.gens.len() == self.scope_kills.len());
282322
assert!(self.gens.len() == self.on_entry.len());
283323

284324
(start, end)
@@ -402,6 +442,11 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
402442
//!
403443
//! This is usually called (if it is called at all), after
404444
//! all add_gen and add_kill calls, but before propagate.
445+
//!
446+
//! The callback predicate `p(b, e)` is invoked on each node
447+
//! `e` that has been registered as an `exiting_scope` for a
448+
//! given control-breaking node `b`, to determine whether to
449+
//! include the associated kill-effects or not.
405450
406451
debug!("{} add_kills_from_flow_exits", self.analysis_name);
407452
if self.bits_per_id == 0 {
@@ -412,7 +457,7 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
412457
cfg.graph.each_edge(|_edge_index, edge| {
413458
let flow_exit = edge.source();
414459
let (start, end) = self.compute_id_range(flow_exit);
415-
let mut orig_kills = self.kills[start.. end].to_vec();
460+
let mut orig_kills = self.scope_kills[start.. end].to_vec();
416461

417462
let mut changed = false;
418463
for &node_id in &edge.data.exiting_scopes {
@@ -421,8 +466,12 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
421466
Some(indices) => {
422467
for &cfg_idx in indices {
423468
let (start, end) = self.compute_id_range(cfg_idx);
424-
let kills = &self.kills[start.. end];
469+
let kills = &self.scope_kills[start.. end];
425470
if bitwise(&mut orig_kills, kills, &Union) {
471+
debug!("scope exits: scope id={} \
472+
(node={:?} of {:?}) added killset: {}",
473+
node_id, cfg_idx, indices,
474+
bits_to_string(kills));
426475
changed = true;
427476
}
428477
}
@@ -436,7 +485,7 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
436485
}
437486

438487
if changed {
439-
let bits = &mut self.kills[start.. end];
488+
let bits = &mut self.scope_kills[start.. end];
440489
debug!("{} add_kills_from_flow_exits flow_exit={:?} bits={} [before]",
441490
self.analysis_name, flow_exit, mut_bits_to_string(bits));
442491
bits.clone_from_slice(&orig_kills[..]);

src/librustc_borrowck/borrowck/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc::middle::cfg;
2424
use rustc::middle::dataflow::DataFlowContext;
2525
use rustc::middle::dataflow::BitwiseOperator;
2626
use rustc::middle::dataflow::DataFlowOperator;
27+
use rustc::middle::dataflow::KillFrom;
2728
use rustc::middle::expr_use_visitor as euv;
2829
use rustc::middle::mem_categorization as mc;
2930
use rustc::middle::region;
@@ -167,7 +168,7 @@ fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
167168
all_loans.len());
168169
for (loan_idx, loan) in all_loans.iter().enumerate() {
169170
loan_dfcx.add_gen(loan.gen_scope.node_id(), loan_idx);
170-
loan_dfcx.add_kill(loan.kill_scope.node_id(), loan_idx);
171+
loan_dfcx.add_kill(KillFrom::ScopeEnd, loan.kill_scope.node_id(), loan_idx);
171172
}
172173
loan_dfcx.add_kills_from_flow_exits(cfg);
173174
loan_dfcx.propagate(cfg, body);

src/librustc_borrowck/borrowck/move_data.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use rustc::middle::cfg;
1818
use rustc::middle::dataflow::DataFlowContext;
1919
use rustc::middle::dataflow::BitwiseOperator;
2020
use rustc::middle::dataflow::DataFlowOperator;
21+
use rustc::middle::dataflow::KillFrom;
2122
use rustc::middle::expr_use_visitor as euv;
2223
use rustc::middle::ty;
2324
use rustc::util::nodemap::{FnvHashMap, NodeSet};
@@ -59,6 +60,9 @@ pub struct MoveData<'tcx> {
5960
/// Assignments to a variable or path, like `x = foo`, but not `x += foo`.
6061
pub assignee_ids: RefCell<NodeSet>,
6162

63+
/// AST node ids for move entries that have kind `Declared`.
64+
pub declared_ids: RefCell<NodeSet>,
65+
6266
/// Path-fragments from moves in to or out of parts of structured data.
6367
pub fragments: RefCell<fragments::FragmentSets>,
6468
}
@@ -216,6 +220,7 @@ impl<'tcx> MoveData<'tcx> {
216220
var_assignments: RefCell::new(Vec::new()),
217221
variant_matches: RefCell::new(Vec::new()),
218222
assignee_ids: RefCell::new(NodeSet()),
223+
declared_ids: RefCell::new(NodeSet()),
219224
fragments: RefCell::new(fragments::FragmentSets::new()),
220225
}
221226
}
@@ -376,6 +381,10 @@ impl<'tcx> MoveData<'tcx> {
376381
let next_move = self.path_first_move(path_index);
377382
self.set_path_first_move(path_index, move_index);
378383

384+
if kind == MoveKind::Declared {
385+
self.declared_ids.borrow_mut().insert(id);
386+
}
387+
379388
self.moves.borrow_mut().push(Move {
380389
path: path_index,
381390
id: id,
@@ -473,11 +482,13 @@ impl<'tcx> MoveData<'tcx> {
473482

474483
for (i, assignment) in self.var_assignments.borrow().iter().enumerate() {
475484
dfcx_assign.add_gen(assignment.id, i);
476-
self.kill_moves(assignment.path, assignment.id, dfcx_moves);
485+
self.kill_moves(assignment.path, assignment.id,
486+
KillFrom::Execution, dfcx_moves);
477487
}
478488

479489
for assignment in &*self.path_assignments.borrow() {
480-
self.kill_moves(assignment.path, assignment.id, dfcx_moves);
490+
self.kill_moves(assignment.path, assignment.id,
491+
KillFrom::Execution, dfcx_moves);
481492
}
482493

483494
// Kill all moves related to a variable `x` when
@@ -487,7 +498,8 @@ impl<'tcx> MoveData<'tcx> {
487498
LpVar(..) | LpUpvar(..) | LpDowncast(..) => {
488499
let kill_scope = path.loan_path.kill_scope(tcx);
489500
let path = *self.path_map.borrow().get(&path.loan_path).unwrap();
490-
self.kill_moves(path, kill_scope.node_id(), dfcx_moves);
501+
self.kill_moves(path, kill_scope.node_id(),
502+
KillFrom::ScopeEnd, dfcx_moves);
491503
}
492504
LpExtend(..) => {}
493505
}
@@ -500,7 +512,9 @@ impl<'tcx> MoveData<'tcx> {
500512
match lp.kind {
501513
LpVar(..) | LpUpvar(..) | LpDowncast(..) => {
502514
let kill_scope = lp.kill_scope(tcx);
503-
dfcx_assign.add_kill(kill_scope.node_id(), assignment_index);
515+
dfcx_assign.add_kill(KillFrom::ScopeEnd,
516+
kill_scope.node_id(),
517+
assignment_index);
504518
}
505519
LpExtend(..) => {
506520
tcx.sess.bug("var assignment for non var path");
@@ -568,6 +582,7 @@ impl<'tcx> MoveData<'tcx> {
568582
fn kill_moves(&self,
569583
path: MovePathIndex,
570584
kill_id: ast::NodeId,
585+
kill_kind: KillFrom,
571586
dfcx_moves: &mut MoveDataFlow) {
572587
// We can only perform kills for paths that refer to a unique location,
573588
// since otherwise we may kill a move from one location with an
@@ -576,7 +591,9 @@ impl<'tcx> MoveData<'tcx> {
576591
let loan_path = self.path_loan_path(path);
577592
if loan_path_is_precise(&*loan_path) {
578593
self.each_applicable_move(path, |move_index| {
579-
dfcx_moves.add_kill(kill_id, move_index.get());
594+
debug!("kill_moves add_kill {:?} kill_id={} move_index={}",
595+
kill_kind, kill_id, move_index.get());
596+
dfcx_moves.add_kill(kill_kind, kill_id, move_index.get());
580597
true
581598
});
582599
}

0 commit comments

Comments
 (0)