1 // Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use rustc::ty::TyCtxt;
12 use rustc::mir::repr::{self, Mir, Location};
13 use rustc_data_structures::indexed_vec::Idx;
15 use super::super::gather_moves::{MoveOutIndex, MovePathIndex};
16 use super::super::MoveDataParamEnv;
17 use super::super::DropFlagState;
18 use super::super::drop_flag_effects_for_function_entry;
19 use super::super::drop_flag_effects_for_location;
20 use super::super::on_all_children_bits;
22 use super::{BitDenotation, BlockSets, DataflowOperator};
24 use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
25 use bitslice::{BitwiseOperator};
26 use indexed_set::{IdxSet};
28 // Dataflow analyses are built upon some interpretation of the
29 // bitvectors attached to each basic block, represented via a
30 // zero-sized structure.
32 /// `MaybeInitializedLvals` tracks all l-values that might be
33 /// initialized upon reaching a particular point in the control flow
36 /// For example, in code like the following, we have corresponding
37 /// dataflow information shown in the right-hand comments.
41 /// fn foo(pred: bool) { // maybe-init:
43 /// let a = S; let b = S; let c; let d; // {a, b}
55 /// c = S; // {a, b, c, d}
59 /// To determine whether an l-value *must* be initialized at a
60 /// particular control-flow point, one can take the set-difference
61 /// between this data and the data from `MaybeUninitializedLvals` at the
62 /// corresponding control-flow point.
64 /// Similarly, at a given `drop` statement, the set-intersection
65 /// between this data and `MaybeUninitializedLvals` yields the set of
66 /// l-values that would require a dynamic drop-flag at that statement.
67 pub struct MaybeInitializedLvals<'a, 'tcx: 'a> {
68 tcx: TyCtxt<'a, 'tcx, 'tcx>,
72 impl<'a, 'tcx: 'a> MaybeInitializedLvals<'a, 'tcx> {
73 pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
74 MaybeInitializedLvals { tcx: tcx, mir: mir }
78 /// `MaybeUninitializedLvals` tracks all l-values that might be
79 /// uninitialized upon reaching a particular point in the control flow
82 /// For example, in code like the following, we have corresponding
83 /// dataflow information shown in the right-hand comments.
87 /// fn foo(pred: bool) { // maybe-uninit:
89 /// let a = S; let b = S; let c; let d; // { c, d}
92 /// drop(a); // {a, c, d}
93 /// b = S; // {a, c, d}
96 /// drop(b); // { b, c, d}
97 /// d = S; // { b, c }
101 /// c = S; // {a, b, d}
105 /// To determine whether an l-value *must* be uninitialized at a
106 /// particular control-flow point, one can take the set-difference
107 /// between this data and the data from `MaybeInitializedLvals` at the
108 /// corresponding control-flow point.
110 /// Similarly, at a given `drop` statement, the set-intersection
111 /// between this data and `MaybeInitializedLvals` yields the set of
112 /// l-values that would require a dynamic drop-flag at that statement.
113 pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> {
114 tcx: TyCtxt<'a, 'tcx, 'tcx>,
118 impl<'a, 'tcx: 'a> MaybeUninitializedLvals<'a, 'tcx> {
119 pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
120 MaybeUninitializedLvals { tcx: tcx, mir: mir }
124 /// `DefinitelyInitializedLvals` tracks all l-values that are definitely
125 /// initialized upon reaching a particular point in the control flow
128 /// FIXME: Note that once flow-analysis is complete, this should be
129 /// the set-complement of MaybeUninitializedLvals; thus we can get rid
130 /// of one or the other of these two. I'm inclined to get rid of
131 /// MaybeUninitializedLvals, simply because the sets will tend to be
132 /// smaller in this analysis and thus easier for humans to process
135 /// For example, in code like the following, we have corresponding
136 /// dataflow information shown in the right-hand comments.
140 /// fn foo(pred: bool) { // definite-init:
142 /// let a = S; let b = S; let c; let d; // {a, b }
145 /// drop(a); // { b, }
149 /// drop(b); // {a, }
158 /// To determine whether an l-value *may* be uninitialized at a
159 /// particular control-flow point, one can take the set-complement
162 /// Similarly, at a given `drop` statement, the set-difference between
163 /// this data and `MaybeInitializedLvals` yields the set of l-values
164 /// that would require a dynamic drop-flag at that statement.
165 pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> {
166 tcx: TyCtxt<'a, 'tcx, 'tcx>,
170 impl<'a, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'tcx> {
171 pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
172 DefinitelyInitializedLvals { tcx: tcx, mir: mir }
176 /// `MovingOutStatements` tracks the statements that perform moves out
177 /// of particular l-values. More precisely, it tracks whether the
178 /// *effect* of such moves (namely, the uninitialization of the
179 /// l-value in question) can reach some point in the control-flow of
180 /// the function, or if that effect is "killed" by some intervening
181 /// operation reinitializing that l-value.
183 /// The resulting dataflow is a more enriched version of
184 /// `MaybeUninitializedLvals`. Both structures on their own only tell
185 /// you if an l-value *might* be uninitialized at a given point in the
186 /// control flow. But `MovingOutStatements` also includes the added
187 /// data of *which* particular statement causing the deinitialization
188 /// that the borrow checker's error meessage may need to report.
190 pub struct MovingOutStatements<'a, 'tcx: 'a> {
191 tcx: TyCtxt<'a, 'tcx, 'tcx>,
195 impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
196 fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
197 state: DropFlagState)
200 DropFlagState::Absent => sets.kill(&path),
201 DropFlagState::Present => sets.gen(&path),
206 impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
207 fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
208 state: DropFlagState)
211 DropFlagState::Absent => sets.gen(&path),
212 DropFlagState::Present => sets.kill(&path),
217 impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
218 fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
219 state: DropFlagState)
222 DropFlagState::Absent => sets.kill(&path),
223 DropFlagState::Present => sets.gen(&path),
228 impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
229 type Idx = MovePathIndex;
230 type Ctxt = MoveDataParamEnv<'tcx>;
231 fn name() -> &'static str { "maybe_init" }
232 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
233 ctxt.move_data.move_paths.len()
236 fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>)
238 drop_flag_effects_for_function_entry(
239 self.tcx, self.mir, ctxt,
241 assert!(s == DropFlagState::Present);
242 sets.on_entry.add(&path);
246 fn statement_effect(&self,
248 sets: &mut BlockSets<MovePathIndex>,
249 bb: repr::BasicBlock,
252 drop_flag_effects_for_location(
253 self.tcx, self.mir, ctxt,
254 Location { block: bb, statement_index: idx },
255 |path, s| Self::update_bits(sets, path, s)
259 fn terminator_effect(&self,
261 sets: &mut BlockSets<MovePathIndex>,
262 bb: repr::BasicBlock,
263 statements_len: usize)
265 drop_flag_effects_for_location(
266 self.tcx, self.mir, ctxt,
267 Location { block: bb, statement_index: statements_len },
268 |path, s| Self::update_bits(sets, path, s)
272 fn propagate_call_return(&self,
274 in_out: &mut IdxSet<MovePathIndex>,
275 _call_bb: repr::BasicBlock,
276 _dest_bb: repr::BasicBlock,
277 dest_lval: &repr::Lvalue) {
278 // when a call returns successfully, that means we need to set
279 // the bits for that dest_lval to 1 (initialized).
280 let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval);
281 on_all_children_bits(self.tcx, self.mir, &ctxt.move_data,
283 |mpi| { in_out.add(&mpi); });
287 impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
288 type Idx = MovePathIndex;
289 type Ctxt = MoveDataParamEnv<'tcx>;
290 fn name() -> &'static str { "maybe_uninit" }
291 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
292 ctxt.move_data.move_paths.len()
295 // sets on_entry bits for Arg lvalues
296 fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>) {
297 // set all bits to 1 (uninit) before gathering counterevidence
298 for e in sets.on_entry.words_mut() { *e = !0; }
300 drop_flag_effects_for_function_entry(
301 self.tcx, self.mir, ctxt,
303 assert!(s == DropFlagState::Present);
304 sets.on_entry.remove(&path);
308 fn statement_effect(&self,
310 sets: &mut BlockSets<MovePathIndex>,
311 bb: repr::BasicBlock,
314 drop_flag_effects_for_location(
315 self.tcx, self.mir, ctxt,
316 Location { block: bb, statement_index: idx },
317 |path, s| Self::update_bits(sets, path, s)
321 fn terminator_effect(&self,
323 sets: &mut BlockSets<MovePathIndex>,
324 bb: repr::BasicBlock,
325 statements_len: usize)
327 drop_flag_effects_for_location(
328 self.tcx, self.mir, ctxt,
329 Location { block: bb, statement_index: statements_len },
330 |path, s| Self::update_bits(sets, path, s)
334 fn propagate_call_return(&self,
336 in_out: &mut IdxSet<MovePathIndex>,
337 _call_bb: repr::BasicBlock,
338 _dest_bb: repr::BasicBlock,
339 dest_lval: &repr::Lvalue) {
340 // when a call returns successfully, that means we need to set
341 // the bits for that dest_lval to 1 (initialized).
342 let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval);
343 on_all_children_bits(self.tcx, self.mir, &ctxt.move_data,
345 |mpi| { in_out.remove(&mpi); });
349 impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
350 type Idx = MovePathIndex;
351 type Ctxt = MoveDataParamEnv<'tcx>;
352 fn name() -> &'static str { "definite_init" }
353 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
354 ctxt.move_data.move_paths.len()
357 // sets on_entry bits for Arg lvalues
358 fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>) {
359 for e in sets.on_entry.words_mut() { *e = 0; }
361 drop_flag_effects_for_function_entry(
362 self.tcx, self.mir, ctxt,
364 assert!(s == DropFlagState::Present);
365 sets.on_entry.add(&path);
369 fn statement_effect(&self,
371 sets: &mut BlockSets<MovePathIndex>,
372 bb: repr::BasicBlock,
375 drop_flag_effects_for_location(
376 self.tcx, self.mir, ctxt,
377 Location { block: bb, statement_index: idx },
378 |path, s| Self::update_bits(sets, path, s)
382 fn terminator_effect(&self,
384 sets: &mut BlockSets<MovePathIndex>,
385 bb: repr::BasicBlock,
386 statements_len: usize)
388 drop_flag_effects_for_location(
389 self.tcx, self.mir, ctxt,
390 Location { block: bb, statement_index: statements_len },
391 |path, s| Self::update_bits(sets, path, s)
395 fn propagate_call_return(&self,
397 in_out: &mut IdxSet<MovePathIndex>,
398 _call_bb: repr::BasicBlock,
399 _dest_bb: repr::BasicBlock,
400 dest_lval: &repr::Lvalue) {
401 // when a call returns successfully, that means we need to set
402 // the bits for that dest_lval to 1 (initialized).
403 let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval);
404 on_all_children_bits(self.tcx, self.mir, &ctxt.move_data,
406 |mpi| { in_out.add(&mpi); });
410 impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
411 type Idx = MoveOutIndex;
412 type Ctxt = MoveDataParamEnv<'tcx>;
413 fn name() -> &'static str { "moving_out" }
414 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
415 ctxt.move_data.moves.len()
418 fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets<MoveOutIndex>) {
419 // no move-statements have been executed prior to function
420 // execution, so this method has no effect on `_sets`.
422 fn statement_effect(&self,
424 sets: &mut BlockSets<MoveOutIndex>,
425 bb: repr::BasicBlock,
427 let (tcx, mir, move_data) = (self.tcx, self.mir, &ctxt.move_data);
428 let stmt = &mir[bb].statements[idx];
429 let loc_map = &move_data.loc_map;
430 let path_map = &move_data.path_map;
431 let rev_lookup = &move_data.rev_lookup;
433 let loc = Location { block: bb, statement_index: idx };
434 debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
435 stmt, loc, &loc_map[loc]);
436 for move_index in &loc_map[loc] {
437 // Every path deinitialized by a *particular move*
438 // has corresponding bit, "gen'ed" (i.e. set)
439 // here, in dataflow vector
440 zero_to_one(sets.gen_set.words_mut(), *move_index);
442 let bits_per_block = self.bits_per_block(ctxt);
444 repr::StatementKind::SetDiscriminant { .. } => {
445 span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck");
447 repr::StatementKind::Assign(ref lvalue, _) => {
448 // assigning into this `lvalue` kills all
449 // MoveOuts from it, and *also* all MoveOuts
450 // for children and associated fragment sets.
451 let move_path_index = rev_lookup.find(lvalue);
452 on_all_children_bits(tcx,
456 |mpi| for moi in &path_map[mpi] {
457 assert!(moi.index() < bits_per_block);
458 sets.kill_set.add(&moi);
461 repr::StatementKind::StorageLive(_) |
462 repr::StatementKind::StorageDead(_) => {}
466 fn terminator_effect(&self,
468 sets: &mut BlockSets<MoveOutIndex>,
469 bb: repr::BasicBlock,
470 statements_len: usize)
472 let (mir, move_data) = (self.mir, &ctxt.move_data);
473 let term = mir[bb].terminator();
474 let loc_map = &move_data.loc_map;
475 let loc = Location { block: bb, statement_index: statements_len };
476 debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
477 term, loc, &loc_map[loc]);
478 let bits_per_block = self.bits_per_block(ctxt);
479 for move_index in &loc_map[loc] {
480 assert!(move_index.index() < bits_per_block);
481 zero_to_one(sets.gen_set.words_mut(), *move_index);
485 fn propagate_call_return(&self,
487 in_out: &mut IdxSet<MoveOutIndex>,
488 _call_bb: repr::BasicBlock,
489 _dest_bb: repr::BasicBlock,
490 dest_lval: &repr::Lvalue) {
491 let move_data = &ctxt.move_data;
492 let move_path_index = move_data.rev_lookup.find(dest_lval);
493 let bits_per_block = self.bits_per_block(ctxt);
495 let path_map = &move_data.path_map;
496 on_all_children_bits(self.tcx,
500 |mpi| for moi in &path_map[mpi] {
501 assert!(moi.index() < bits_per_block);
507 fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
508 let retval = bitvec.set_bit(move_index.index());
512 impl<'a, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'tcx> {
514 fn join(&self, pred1: usize, pred2: usize) -> usize {
515 pred1 | pred2 // moves from both preds are in scope
519 impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> {
521 fn join(&self, pred1: usize, pred2: usize) -> usize {
522 pred1 | pred2 // "maybe" means we union effects of both preds
526 impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> {
528 fn join(&self, pred1: usize, pred2: usize) -> usize {
529 pred1 | pred2 // "maybe" means we union effects of both preds
533 impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
535 fn join(&self, pred1: usize, pred2: usize) -> usize {
536 pred1 & pred2 // "definitely" means we intersect effects of both preds
540 // The way that dataflow fixed point iteration works, you want to
541 // start at bottom and work your way to a fixed point. Control-flow
542 // merges will apply the `join` operator to each block entry's current
543 // state (which starts at that bottom value).
545 // This means, for propagation across the graph, that you either want
546 // to start at all-zeroes and then use Union as your merge when
547 // propagating, or you start at all-ones and then use Intersect as
548 // your merge when propagating.
550 impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> {
552 fn bottom_value() -> bool {
553 false // bottom = no loans in scope by default
557 impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> {
559 fn bottom_value() -> bool {
560 false // bottom = uninitialized
564 impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> {
566 fn bottom_value() -> bool {
567 false // bottom = initialized (start_block_effect counters this at outset)
571 impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> {
573 fn bottom_value() -> bool {
574 true // bottom = initialized (start_block_effect counters this at outset)