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::{self, Mir, Location};
13 use rustc_data_structures::bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
14 use rustc_data_structures::bitslice::{BitwiseOperator};
15 use rustc_data_structures::indexed_set::{IdxSet};
16 use rustc_data_structures::indexed_vec::Idx;
18 use super::super::gather_moves::{MoveOutIndex, MovePathIndex};
19 use super::super::MoveDataParamEnv;
20 use super::super::DropFlagState;
21 use super::super::drop_flag_effects_for_function_entry;
22 use super::super::drop_flag_effects_for_location;
23 use super::super::on_lookup_result_bits;
25 use super::{BitDenotation, BlockSets, DataflowOperator};
27 // Dataflow analyses are built upon some interpretation of the
28 // bitvectors attached to each basic block, represented via a
29 // zero-sized structure.
31 /// `MaybeInitializedLvals` tracks all l-values that might be
32 /// initialized upon reaching a particular point in the control flow
35 /// For example, in code like the following, we have corresponding
36 /// dataflow information shown in the right-hand comments.
40 /// fn foo(pred: bool) { // maybe-init:
42 /// let a = S; let b = S; let c; let d; // {a, b}
54 /// c = S; // {a, b, c, d}
58 /// To determine whether an l-value *must* be initialized at a
59 /// particular control-flow point, one can take the set-difference
60 /// between this data and the data from `MaybeUninitializedLvals` at the
61 /// corresponding control-flow point.
63 /// Similarly, at a given `drop` statement, the set-intersection
64 /// between this data and `MaybeUninitializedLvals` yields the set of
65 /// l-values that would require a dynamic drop-flag at that statement.
66 pub struct MaybeInitializedLvals<'a, 'tcx: 'a> {
67 tcx: TyCtxt<'a, 'tcx, 'tcx>,
71 impl<'a, 'tcx: 'a> MaybeInitializedLvals<'a, 'tcx> {
72 pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
73 MaybeInitializedLvals { tcx: tcx, mir: mir }
77 /// `MaybeUninitializedLvals` tracks all l-values that might be
78 /// uninitialized upon reaching a particular point in the control flow
81 /// For example, in code like the following, we have corresponding
82 /// dataflow information shown in the right-hand comments.
86 /// fn foo(pred: bool) { // maybe-uninit:
88 /// let a = S; let b = S; let c; let d; // { c, d}
91 /// drop(a); // {a, c, d}
92 /// b = S; // {a, c, d}
95 /// drop(b); // { b, c, d}
96 /// d = S; // { b, c }
100 /// c = S; // {a, b, d}
104 /// To determine whether an l-value *must* be uninitialized at a
105 /// particular control-flow point, one can take the set-difference
106 /// between this data and the data from `MaybeInitializedLvals` at the
107 /// corresponding control-flow point.
109 /// Similarly, at a given `drop` statement, the set-intersection
110 /// between this data and `MaybeInitializedLvals` yields the set of
111 /// l-values that would require a dynamic drop-flag at that statement.
112 pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> {
113 tcx: TyCtxt<'a, 'tcx, 'tcx>,
117 impl<'a, 'tcx: 'a> MaybeUninitializedLvals<'a, 'tcx> {
118 pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
119 MaybeUninitializedLvals { tcx: tcx, mir: mir }
123 /// `DefinitelyInitializedLvals` tracks all l-values that are definitely
124 /// initialized upon reaching a particular point in the control flow
127 /// FIXME: Note that once flow-analysis is complete, this should be
128 /// the set-complement of MaybeUninitializedLvals; thus we can get rid
129 /// of one or the other of these two. I'm inclined to get rid of
130 /// MaybeUninitializedLvals, simply because the sets will tend to be
131 /// smaller in this analysis and thus easier for humans to process
134 /// For example, in code like the following, we have corresponding
135 /// dataflow information shown in the right-hand comments.
139 /// fn foo(pred: bool) { // definite-init:
141 /// let a = S; let b = S; let c; let d; // {a, b }
144 /// drop(a); // { b, }
148 /// drop(b); // {a, }
157 /// To determine whether an l-value *may* be uninitialized at a
158 /// particular control-flow point, one can take the set-complement
161 /// Similarly, at a given `drop` statement, the set-difference between
162 /// this data and `MaybeInitializedLvals` yields the set of l-values
163 /// that would require a dynamic drop-flag at that statement.
164 pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> {
165 tcx: TyCtxt<'a, 'tcx, 'tcx>,
169 impl<'a, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'tcx> {
170 pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
171 DefinitelyInitializedLvals { tcx: tcx, mir: mir }
175 /// `MovingOutStatements` tracks the statements that perform moves out
176 /// of particular l-values. More precisely, it tracks whether the
177 /// *effect* of such moves (namely, the uninitialization of the
178 /// l-value in question) can reach some point in the control-flow of
179 /// the function, or if that effect is "killed" by some intervening
180 /// operation reinitializing that l-value.
182 /// The resulting dataflow is a more enriched version of
183 /// `MaybeUninitializedLvals`. Both structures on their own only tell
184 /// you if an l-value *might* be uninitialized at a given point in the
185 /// control flow. But `MovingOutStatements` also includes the added
186 /// data of *which* particular statement causing the deinitialization
187 /// that the borrow checker's error meessage may need to report.
189 pub struct MovingOutStatements<'a, 'tcx: 'a> {
190 tcx: TyCtxt<'a, 'tcx, 'tcx>,
194 impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
195 fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
196 state: DropFlagState)
199 DropFlagState::Absent => sets.kill(&path),
200 DropFlagState::Present => sets.gen(&path),
205 impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
206 fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
207 state: DropFlagState)
210 DropFlagState::Absent => sets.gen(&path),
211 DropFlagState::Present => sets.kill(&path),
216 impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
217 fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
218 state: DropFlagState)
221 DropFlagState::Absent => sets.kill(&path),
222 DropFlagState::Present => sets.gen(&path),
227 impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
228 type Idx = MovePathIndex;
229 type Ctxt = MoveDataParamEnv<'tcx>;
230 fn name() -> &'static str { "maybe_init" }
231 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
232 ctxt.move_data.move_paths.len()
235 fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>)
237 drop_flag_effects_for_function_entry(
238 self.tcx, self.mir, ctxt,
240 assert!(s == DropFlagState::Present);
241 sets.on_entry.add(&path);
245 fn statement_effect(&self,
247 sets: &mut BlockSets<MovePathIndex>,
251 drop_flag_effects_for_location(
252 self.tcx, self.mir, ctxt,
253 Location { block: bb, statement_index: idx },
254 |path, s| Self::update_bits(sets, path, s)
258 fn terminator_effect(&self,
260 sets: &mut BlockSets<MovePathIndex>,
262 statements_len: usize)
264 drop_flag_effects_for_location(
265 self.tcx, self.mir, ctxt,
266 Location { block: bb, statement_index: statements_len },
267 |path, s| Self::update_bits(sets, path, s)
271 fn propagate_call_return(&self,
273 in_out: &mut IdxSet<MovePathIndex>,
274 _call_bb: mir::BasicBlock,
275 _dest_bb: mir::BasicBlock,
276 dest_lval: &mir::Lvalue) {
277 // when a call returns successfully, that means we need to set
278 // the bits for that dest_lval to 1 (initialized).
279 on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data,
280 ctxt.move_data.rev_lookup.find(dest_lval),
281 |mpi| { in_out.add(&mpi); });
285 impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
286 type Idx = MovePathIndex;
287 type Ctxt = MoveDataParamEnv<'tcx>;
288 fn name() -> &'static str { "maybe_uninit" }
289 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
290 ctxt.move_data.move_paths.len()
293 // sets on_entry bits for Arg lvalues
294 fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>) {
295 // set all bits to 1 (uninit) before gathering counterevidence
296 for e in sets.on_entry.words_mut() { *e = !0; }
298 drop_flag_effects_for_function_entry(
299 self.tcx, self.mir, ctxt,
301 assert!(s == DropFlagState::Present);
302 sets.on_entry.remove(&path);
306 fn statement_effect(&self,
308 sets: &mut BlockSets<MovePathIndex>,
312 drop_flag_effects_for_location(
313 self.tcx, self.mir, ctxt,
314 Location { block: bb, statement_index: idx },
315 |path, s| Self::update_bits(sets, path, s)
319 fn terminator_effect(&self,
321 sets: &mut BlockSets<MovePathIndex>,
323 statements_len: usize)
325 drop_flag_effects_for_location(
326 self.tcx, self.mir, ctxt,
327 Location { block: bb, statement_index: statements_len },
328 |path, s| Self::update_bits(sets, path, s)
332 fn propagate_call_return(&self,
334 in_out: &mut IdxSet<MovePathIndex>,
335 _call_bb: mir::BasicBlock,
336 _dest_bb: mir::BasicBlock,
337 dest_lval: &mir::Lvalue) {
338 // when a call returns successfully, that means we need to set
339 // the bits for that dest_lval to 0 (initialized).
340 on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data,
341 ctxt.move_data.rev_lookup.find(dest_lval),
342 |mpi| { in_out.remove(&mpi); });
346 impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
347 type Idx = MovePathIndex;
348 type Ctxt = MoveDataParamEnv<'tcx>;
349 fn name() -> &'static str { "definite_init" }
350 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
351 ctxt.move_data.move_paths.len()
354 // sets on_entry bits for Arg lvalues
355 fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>) {
356 for e in sets.on_entry.words_mut() { *e = 0; }
358 drop_flag_effects_for_function_entry(
359 self.tcx, self.mir, ctxt,
361 assert!(s == DropFlagState::Present);
362 sets.on_entry.add(&path);
366 fn statement_effect(&self,
368 sets: &mut BlockSets<MovePathIndex>,
372 drop_flag_effects_for_location(
373 self.tcx, self.mir, ctxt,
374 Location { block: bb, statement_index: idx },
375 |path, s| Self::update_bits(sets, path, s)
379 fn terminator_effect(&self,
381 sets: &mut BlockSets<MovePathIndex>,
383 statements_len: usize)
385 drop_flag_effects_for_location(
386 self.tcx, self.mir, ctxt,
387 Location { block: bb, statement_index: statements_len },
388 |path, s| Self::update_bits(sets, path, s)
392 fn propagate_call_return(&self,
394 in_out: &mut IdxSet<MovePathIndex>,
395 _call_bb: mir::BasicBlock,
396 _dest_bb: mir::BasicBlock,
397 dest_lval: &mir::Lvalue) {
398 // when a call returns successfully, that means we need to set
399 // the bits for that dest_lval to 1 (initialized).
400 on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data,
401 ctxt.move_data.rev_lookup.find(dest_lval),
402 |mpi| { in_out.add(&mpi); });
406 impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
407 type Idx = MoveOutIndex;
408 type Ctxt = MoveDataParamEnv<'tcx>;
409 fn name() -> &'static str { "moving_out" }
410 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
411 ctxt.move_data.moves.len()
414 fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets<MoveOutIndex>) {
415 // no move-statements have been executed prior to function
416 // execution, so this method has no effect on `_sets`.
418 fn statement_effect(&self,
420 sets: &mut BlockSets<MoveOutIndex>,
423 let (tcx, mir, move_data) = (self.tcx, self.mir, &ctxt.move_data);
424 let stmt = &mir[bb].statements[idx];
425 let loc_map = &move_data.loc_map;
426 let path_map = &move_data.path_map;
427 let rev_lookup = &move_data.rev_lookup;
429 let loc = Location { block: bb, statement_index: idx };
430 debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
431 stmt, loc, &loc_map[loc]);
432 for move_index in &loc_map[loc] {
433 // Every path deinitialized by a *particular move*
434 // has corresponding bit, "gen'ed" (i.e. set)
435 // here, in dataflow vector
436 zero_to_one(sets.gen_set.words_mut(), *move_index);
438 let bits_per_block = self.bits_per_block(ctxt);
440 mir::StatementKind::SetDiscriminant { .. } => {
441 span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck");
443 mir::StatementKind::Assign(ref lvalue, _) => {
444 // assigning into this `lvalue` kills all
445 // MoveOuts from it, and *also* all MoveOuts
446 // for children and associated fragment sets.
447 on_lookup_result_bits(tcx,
450 rev_lookup.find(lvalue),
451 |mpi| for moi in &path_map[mpi] {
452 assert!(moi.index() < bits_per_block);
453 sets.kill_set.add(&moi);
456 mir::StatementKind::StorageLive(_) |
457 mir::StatementKind::StorageDead(_) |
458 mir::StatementKind::Nop => {}
462 fn terminator_effect(&self,
464 sets: &mut BlockSets<MoveOutIndex>,
466 statements_len: usize)
468 let (mir, move_data) = (self.mir, &ctxt.move_data);
469 let term = mir[bb].terminator();
470 let loc_map = &move_data.loc_map;
471 let loc = Location { block: bb, statement_index: statements_len };
472 debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
473 term, loc, &loc_map[loc]);
474 let bits_per_block = self.bits_per_block(ctxt);
475 for move_index in &loc_map[loc] {
476 assert!(move_index.index() < bits_per_block);
477 zero_to_one(sets.gen_set.words_mut(), *move_index);
481 fn propagate_call_return(&self,
483 in_out: &mut IdxSet<MoveOutIndex>,
484 _call_bb: mir::BasicBlock,
485 _dest_bb: mir::BasicBlock,
486 dest_lval: &mir::Lvalue) {
487 let move_data = &ctxt.move_data;
488 let bits_per_block = self.bits_per_block(ctxt);
490 let path_map = &move_data.path_map;
491 on_lookup_result_bits(self.tcx,
494 move_data.rev_lookup.find(dest_lval),
495 |mpi| for moi in &path_map[mpi] {
496 assert!(moi.index() < bits_per_block);
502 fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
503 let retval = bitvec.set_bit(move_index.index());
507 impl<'a, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'tcx> {
509 fn join(&self, pred1: usize, pred2: usize) -> usize {
510 pred1 | pred2 // moves from both preds are in scope
514 impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> {
516 fn join(&self, pred1: usize, pred2: usize) -> usize {
517 pred1 | pred2 // "maybe" means we union effects of both preds
521 impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> {
523 fn join(&self, pred1: usize, pred2: usize) -> usize {
524 pred1 | pred2 // "maybe" means we union effects of both preds
528 impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
530 fn join(&self, pred1: usize, pred2: usize) -> usize {
531 pred1 & pred2 // "definitely" means we intersect effects of both preds
535 // The way that dataflow fixed point iteration works, you want to
536 // start at bottom and work your way to a fixed point. Control-flow
537 // merges will apply the `join` operator to each block entry's current
538 // state (which starts at that bottom value).
540 // This means, for propagation across the graph, that you either want
541 // to start at all-zeroes and then use Union as your merge when
542 // propagating, or you start at all-ones and then use Intersect as
543 // your merge when propagating.
545 impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> {
547 fn bottom_value() -> bool {
548 false // bottom = no loans in scope by default
552 impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> {
554 fn bottom_value() -> bool {
555 false // bottom = uninitialized
559 impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> {
561 fn bottom_value() -> bool {
562 false // bottom = initialized (start_block_effect counters this at outset)
566 impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> {
568 fn bottom_value() -> bool {
569 true // bottom = initialized (start_block_effect counters this at outset)