]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
Auto merge of #35856 - phimuemue:master, r=brson
[rust.git] / src / librustc_borrowck / borrowck / mir / dataflow / impls.rs
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.
4 //
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.
10
11 use rustc::ty::TyCtxt;
12 use rustc::mir::repr::{self, Mir, Location};
13 use rustc_data_structures::indexed_vec::Idx;
14
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;
21
22 use super::{BitDenotation, BlockSets, DataflowOperator};
23
24 use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
25 use bitslice::{BitwiseOperator};
26 use indexed_set::{IdxSet};
27
28 // Dataflow analyses are built upon some interpretation of the
29 // bitvectors attached to each basic block, represented via a
30 // zero-sized structure.
31
32 /// `MaybeInitializedLvals` tracks all l-values that might be
33 /// initialized upon reaching a particular point in the control flow
34 /// for a function.
35 ///
36 /// For example, in code like the following, we have corresponding
37 /// dataflow information shown in the right-hand comments.
38 ///
39 /// ```rust
40 /// struct S;
41 /// fn foo(pred: bool) {                       // maybe-init:
42 ///                                            // {}
43 ///     let a = S; let b = S; let c; let d;    // {a, b}
44 ///
45 ///     if pred {
46 ///         drop(a);                           // {   b}
47 ///         b = S;                             // {   b}
48 ///
49 ///     } else {
50 ///         drop(b);                           // {a}
51 ///         d = S;                             // {a,       d}
52 ///
53 ///     }                                      // {a, b,    d}
54 ///
55 ///     c = S;                                 // {a, b, c, d}
56 /// }
57 /// ```
58 ///
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.
63 ///
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>,
69     mir: &'a Mir<'tcx>,
70 }
71
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 }
75     }
76 }
77
78 /// `MaybeUninitializedLvals` tracks all l-values that might be
79 /// uninitialized upon reaching a particular point in the control flow
80 /// for a function.
81 ///
82 /// For example, in code like the following, we have corresponding
83 /// dataflow information shown in the right-hand comments.
84 ///
85 /// ```rust
86 /// struct S;
87 /// fn foo(pred: bool) {                       // maybe-uninit:
88 ///                                            // {a, b, c, d}
89 ///     let a = S; let b = S; let c; let d;    // {      c, d}
90 ///
91 ///     if pred {
92 ///         drop(a);                           // {a,    c, d}
93 ///         b = S;                             // {a,    c, d}
94 ///
95 ///     } else {
96 ///         drop(b);                           // {   b, c, d}
97 ///         d = S;                             // {   b, c   }
98 ///
99 ///     }                                      // {a, b, c, d}
100 ///
101 ///     c = S;                                 // {a, b,    d}
102 /// }
103 /// ```
104 ///
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.
109 ///
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>,
115     mir: &'a Mir<'tcx>,
116 }
117
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 }
121     }
122 }
123
124 /// `DefinitelyInitializedLvals` tracks all l-values that are definitely
125 /// initialized upon reaching a particular point in the control flow
126 /// for a function.
127 ///
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
133 /// when debugging.
134 ///
135 /// For example, in code like the following, we have corresponding
136 /// dataflow information shown in the right-hand comments.
137 ///
138 /// ```rust
139 /// struct S;
140 /// fn foo(pred: bool) {                       // definite-init:
141 ///                                            // {          }
142 ///     let a = S; let b = S; let c; let d;    // {a, b      }
143 ///
144 ///     if pred {
145 ///         drop(a);                           // {   b,     }
146 ///         b = S;                             // {   b,     }
147 ///
148 ///     } else {
149 ///         drop(b);                           // {a,        }
150 ///         d = S;                             // {a,       d}
151 ///
152 ///     }                                      // {          }
153 ///
154 ///     c = S;                                 // {       c  }
155 /// }
156 /// ```
157 ///
158 /// To determine whether an l-value *may* be uninitialized at a
159 /// particular control-flow point, one can take the set-complement
160 /// of this data.
161 ///
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>,
167     mir: &'a Mir<'tcx>,
168 }
169
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 }
173     }
174 }
175
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.
182 ///
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.
189 #[allow(dead_code)]
190 pub struct MovingOutStatements<'a, 'tcx: 'a> {
191     tcx: TyCtxt<'a, 'tcx, 'tcx>,
192     mir: &'a Mir<'tcx>,
193 }
194
195 impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
196     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
197                    state: DropFlagState)
198     {
199         match state {
200             DropFlagState::Absent => sets.kill(&path),
201             DropFlagState::Present => sets.gen(&path),
202         }
203     }
204 }
205
206 impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
207     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
208                    state: DropFlagState)
209     {
210         match state {
211             DropFlagState::Absent => sets.gen(&path),
212             DropFlagState::Present => sets.kill(&path),
213         }
214     }
215 }
216
217 impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
218     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
219                    state: DropFlagState)
220     {
221         match state {
222             DropFlagState::Absent => sets.kill(&path),
223             DropFlagState::Present => sets.gen(&path),
224         }
225     }
226 }
227
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()
234     }
235
236     fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>)
237     {
238         drop_flag_effects_for_function_entry(
239             self.tcx, self.mir, ctxt,
240             |path, s| {
241                 assert!(s == DropFlagState::Present);
242                 sets.on_entry.add(&path);
243             });
244     }
245
246     fn statement_effect(&self,
247                         ctxt: &Self::Ctxt,
248                         sets: &mut BlockSets<MovePathIndex>,
249                         bb: repr::BasicBlock,
250                         idx: usize)
251     {
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)
256         )
257     }
258
259     fn terminator_effect(&self,
260                          ctxt: &Self::Ctxt,
261                          sets: &mut BlockSets<MovePathIndex>,
262                          bb: repr::BasicBlock,
263                          statements_len: usize)
264     {
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)
269         )
270     }
271
272     fn propagate_call_return(&self,
273                              ctxt: &Self::Ctxt,
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,
282                              move_path_index,
283                              |mpi| { in_out.add(&mpi); });
284     }
285 }
286
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()
293     }
294
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; }
299
300         drop_flag_effects_for_function_entry(
301             self.tcx, self.mir, ctxt,
302             |path, s| {
303                 assert!(s == DropFlagState::Present);
304                 sets.on_entry.remove(&path);
305             });
306     }
307
308     fn statement_effect(&self,
309                         ctxt: &Self::Ctxt,
310                         sets: &mut BlockSets<MovePathIndex>,
311                         bb: repr::BasicBlock,
312                         idx: usize)
313     {
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)
318         )
319     }
320
321     fn terminator_effect(&self,
322                          ctxt: &Self::Ctxt,
323                          sets: &mut BlockSets<MovePathIndex>,
324                          bb: repr::BasicBlock,
325                          statements_len: usize)
326     {
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)
331         )
332     }
333
334     fn propagate_call_return(&self,
335                              ctxt: &Self::Ctxt,
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,
344                              move_path_index,
345                              |mpi| { in_out.remove(&mpi); });
346     }
347 }
348
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()
355     }
356
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; }
360
361         drop_flag_effects_for_function_entry(
362             self.tcx, self.mir, ctxt,
363             |path, s| {
364                 assert!(s == DropFlagState::Present);
365                 sets.on_entry.add(&path);
366             });
367     }
368
369     fn statement_effect(&self,
370                         ctxt: &Self::Ctxt,
371                         sets: &mut BlockSets<MovePathIndex>,
372                         bb: repr::BasicBlock,
373                         idx: usize)
374     {
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)
379         )
380     }
381
382     fn terminator_effect(&self,
383                          ctxt: &Self::Ctxt,
384                          sets: &mut BlockSets<MovePathIndex>,
385                          bb: repr::BasicBlock,
386                          statements_len: usize)
387     {
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)
392         )
393     }
394
395     fn propagate_call_return(&self,
396                              ctxt: &Self::Ctxt,
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,
405                              move_path_index,
406                              |mpi| { in_out.add(&mpi); });
407     }
408 }
409
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()
416     }
417
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`.
421     }
422     fn statement_effect(&self,
423                         ctxt: &Self::Ctxt,
424                         sets: &mut BlockSets<MoveOutIndex>,
425                         bb: repr::BasicBlock,
426                         idx: usize) {
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;
432
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);
441         }
442         let bits_per_block = self.bits_per_block(ctxt);
443         match stmt.kind {
444             repr::StatementKind::SetDiscriminant { .. } => {
445                 span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck");
446             }
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,
453                                      mir,
454                                      move_data,
455                                      move_path_index,
456                                      |mpi| for moi in &path_map[mpi] {
457                                          assert!(moi.index() < bits_per_block);
458                                          sets.kill_set.add(&moi);
459                                      });
460             }
461             repr::StatementKind::StorageLive(_) |
462             repr::StatementKind::StorageDead(_) => {}
463         }
464     }
465
466     fn terminator_effect(&self,
467                          ctxt: &Self::Ctxt,
468                          sets: &mut BlockSets<MoveOutIndex>,
469                          bb: repr::BasicBlock,
470                          statements_len: usize)
471     {
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);
482         }
483     }
484
485     fn propagate_call_return(&self,
486                              ctxt: &Self::Ctxt,
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);
494
495         let path_map = &move_data.path_map;
496         on_all_children_bits(self.tcx,
497                              self.mir,
498                              move_data,
499                              move_path_index,
500                              |mpi| for moi in &path_map[mpi] {
501                                  assert!(moi.index() < bits_per_block);
502                                  in_out.remove(&moi);
503                              });
504     }
505 }
506
507 fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
508     let retval = bitvec.set_bit(move_index.index());
509     assert!(retval);
510 }
511
512 impl<'a, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'tcx> {
513     #[inline]
514     fn join(&self, pred1: usize, pred2: usize) -> usize {
515         pred1 | pred2 // moves from both preds are in scope
516     }
517 }
518
519 impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> {
520     #[inline]
521     fn join(&self, pred1: usize, pred2: usize) -> usize {
522         pred1 | pred2 // "maybe" means we union effects of both preds
523     }
524 }
525
526 impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> {
527     #[inline]
528     fn join(&self, pred1: usize, pred2: usize) -> usize {
529         pred1 | pred2 // "maybe" means we union effects of both preds
530     }
531 }
532
533 impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
534     #[inline]
535     fn join(&self, pred1: usize, pred2: usize) -> usize {
536         pred1 & pred2 // "definitely" means we intersect effects of both preds
537     }
538 }
539
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).
544 //
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.
549
550 impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> {
551     #[inline]
552     fn bottom_value() -> bool {
553         false // bottom = no loans in scope by default
554     }
555 }
556
557 impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> {
558     #[inline]
559     fn bottom_value() -> bool {
560         false // bottom = uninitialized
561     }
562 }
563
564 impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> {
565     #[inline]
566     fn bottom_value() -> bool {
567         false // bottom = initialized (start_block_effect counters this at outset)
568     }
569 }
570
571 impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> {
572     #[inline]
573     fn bottom_value() -> bool {
574         true // bottom = initialized (start_block_effect counters this at outset)
575     }
576 }