]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
Rollup merge of #37425 - polo-language:doc, r=GuillaumeGomez
[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::{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;
17
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;
24
25 use super::{BitDenotation, BlockSets, DataflowOperator};
26
27 // Dataflow analyses are built upon some interpretation of the
28 // bitvectors attached to each basic block, represented via a
29 // zero-sized structure.
30
31 /// `MaybeInitializedLvals` tracks all l-values that might be
32 /// initialized upon reaching a particular point in the control flow
33 /// for a function.
34 ///
35 /// For example, in code like the following, we have corresponding
36 /// dataflow information shown in the right-hand comments.
37 ///
38 /// ```rust
39 /// struct S;
40 /// fn foo(pred: bool) {                       // maybe-init:
41 ///                                            // {}
42 ///     let a = S; let b = S; let c; let d;    // {a, b}
43 ///
44 ///     if pred {
45 ///         drop(a);                           // {   b}
46 ///         b = S;                             // {   b}
47 ///
48 ///     } else {
49 ///         drop(b);                           // {a}
50 ///         d = S;                             // {a,       d}
51 ///
52 ///     }                                      // {a, b,    d}
53 ///
54 ///     c = S;                                 // {a, b, c, d}
55 /// }
56 /// ```
57 ///
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.
62 ///
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>,
68     mir: &'a Mir<'tcx>,
69 }
70
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 }
74     }
75 }
76
77 /// `MaybeUninitializedLvals` tracks all l-values that might be
78 /// uninitialized upon reaching a particular point in the control flow
79 /// for a function.
80 ///
81 /// For example, in code like the following, we have corresponding
82 /// dataflow information shown in the right-hand comments.
83 ///
84 /// ```rust
85 /// struct S;
86 /// fn foo(pred: bool) {                       // maybe-uninit:
87 ///                                            // {a, b, c, d}
88 ///     let a = S; let b = S; let c; let d;    // {      c, d}
89 ///
90 ///     if pred {
91 ///         drop(a);                           // {a,    c, d}
92 ///         b = S;                             // {a,    c, d}
93 ///
94 ///     } else {
95 ///         drop(b);                           // {   b, c, d}
96 ///         d = S;                             // {   b, c   }
97 ///
98 ///     }                                      // {a, b, c, d}
99 ///
100 ///     c = S;                                 // {a, b,    d}
101 /// }
102 /// ```
103 ///
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.
108 ///
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>,
114     mir: &'a Mir<'tcx>,
115 }
116
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 }
120     }
121 }
122
123 /// `DefinitelyInitializedLvals` tracks all l-values that are definitely
124 /// initialized upon reaching a particular point in the control flow
125 /// for a function.
126 ///
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
132 /// when debugging.
133 ///
134 /// For example, in code like the following, we have corresponding
135 /// dataflow information shown in the right-hand comments.
136 ///
137 /// ```rust
138 /// struct S;
139 /// fn foo(pred: bool) {                       // definite-init:
140 ///                                            // {          }
141 ///     let a = S; let b = S; let c; let d;    // {a, b      }
142 ///
143 ///     if pred {
144 ///         drop(a);                           // {   b,     }
145 ///         b = S;                             // {   b,     }
146 ///
147 ///     } else {
148 ///         drop(b);                           // {a,        }
149 ///         d = S;                             // {a,       d}
150 ///
151 ///     }                                      // {          }
152 ///
153 ///     c = S;                                 // {       c  }
154 /// }
155 /// ```
156 ///
157 /// To determine whether an l-value *may* be uninitialized at a
158 /// particular control-flow point, one can take the set-complement
159 /// of this data.
160 ///
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>,
166     mir: &'a Mir<'tcx>,
167 }
168
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 }
172     }
173 }
174
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.
181 ///
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.
188 #[allow(dead_code)]
189 pub struct MovingOutStatements<'a, 'tcx: 'a> {
190     tcx: TyCtxt<'a, 'tcx, 'tcx>,
191     mir: &'a Mir<'tcx>,
192 }
193
194 impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
195     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
196                    state: DropFlagState)
197     {
198         match state {
199             DropFlagState::Absent => sets.kill(&path),
200             DropFlagState::Present => sets.gen(&path),
201         }
202     }
203 }
204
205 impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
206     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
207                    state: DropFlagState)
208     {
209         match state {
210             DropFlagState::Absent => sets.gen(&path),
211             DropFlagState::Present => sets.kill(&path),
212         }
213     }
214 }
215
216 impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
217     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
218                    state: DropFlagState)
219     {
220         match state {
221             DropFlagState::Absent => sets.kill(&path),
222             DropFlagState::Present => sets.gen(&path),
223         }
224     }
225 }
226
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()
233     }
234
235     fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>)
236     {
237         drop_flag_effects_for_function_entry(
238             self.tcx, self.mir, ctxt,
239             |path, s| {
240                 assert!(s == DropFlagState::Present);
241                 sets.on_entry.add(&path);
242             });
243     }
244
245     fn statement_effect(&self,
246                         ctxt: &Self::Ctxt,
247                         sets: &mut BlockSets<MovePathIndex>,
248                         bb: mir::BasicBlock,
249                         idx: usize)
250     {
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)
255         )
256     }
257
258     fn terminator_effect(&self,
259                          ctxt: &Self::Ctxt,
260                          sets: &mut BlockSets<MovePathIndex>,
261                          bb: mir::BasicBlock,
262                          statements_len: usize)
263     {
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)
268         )
269     }
270
271     fn propagate_call_return(&self,
272                              ctxt: &Self::Ctxt,
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); });
282     }
283 }
284
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()
291     }
292
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; }
297
298         drop_flag_effects_for_function_entry(
299             self.tcx, self.mir, ctxt,
300             |path, s| {
301                 assert!(s == DropFlagState::Present);
302                 sets.on_entry.remove(&path);
303             });
304     }
305
306     fn statement_effect(&self,
307                         ctxt: &Self::Ctxt,
308                         sets: &mut BlockSets<MovePathIndex>,
309                         bb: mir::BasicBlock,
310                         idx: usize)
311     {
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)
316         )
317     }
318
319     fn terminator_effect(&self,
320                          ctxt: &Self::Ctxt,
321                          sets: &mut BlockSets<MovePathIndex>,
322                          bb: mir::BasicBlock,
323                          statements_len: usize)
324     {
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)
329         )
330     }
331
332     fn propagate_call_return(&self,
333                              ctxt: &Self::Ctxt,
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); });
343     }
344 }
345
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()
352     }
353
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; }
357
358         drop_flag_effects_for_function_entry(
359             self.tcx, self.mir, ctxt,
360             |path, s| {
361                 assert!(s == DropFlagState::Present);
362                 sets.on_entry.add(&path);
363             });
364     }
365
366     fn statement_effect(&self,
367                         ctxt: &Self::Ctxt,
368                         sets: &mut BlockSets<MovePathIndex>,
369                         bb: mir::BasicBlock,
370                         idx: usize)
371     {
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)
376         )
377     }
378
379     fn terminator_effect(&self,
380                          ctxt: &Self::Ctxt,
381                          sets: &mut BlockSets<MovePathIndex>,
382                          bb: mir::BasicBlock,
383                          statements_len: usize)
384     {
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)
389         )
390     }
391
392     fn propagate_call_return(&self,
393                              ctxt: &Self::Ctxt,
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); });
403     }
404 }
405
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()
412     }
413
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`.
417     }
418     fn statement_effect(&self,
419                         ctxt: &Self::Ctxt,
420                         sets: &mut BlockSets<MoveOutIndex>,
421                         bb: mir::BasicBlock,
422                         idx: usize) {
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;
428
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);
437         }
438         let bits_per_block = self.bits_per_block(ctxt);
439         match stmt.kind {
440             mir::StatementKind::SetDiscriminant { .. } => {
441                 span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck");
442             }
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,
448                                      mir,
449                                      move_data,
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);
454                                      });
455             }
456             mir::StatementKind::StorageLive(_) |
457             mir::StatementKind::StorageDead(_) |
458             mir::StatementKind::Nop => {}
459         }
460     }
461
462     fn terminator_effect(&self,
463                          ctxt: &Self::Ctxt,
464                          sets: &mut BlockSets<MoveOutIndex>,
465                          bb: mir::BasicBlock,
466                          statements_len: usize)
467     {
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);
478         }
479     }
480
481     fn propagate_call_return(&self,
482                              ctxt: &Self::Ctxt,
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);
489
490         let path_map = &move_data.path_map;
491         on_lookup_result_bits(self.tcx,
492                               self.mir,
493                               move_data,
494                               move_data.rev_lookup.find(dest_lval),
495                               |mpi| for moi in &path_map[mpi] {
496                                   assert!(moi.index() < bits_per_block);
497                                   in_out.remove(&moi);
498                               });
499     }
500 }
501
502 fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
503     let retval = bitvec.set_bit(move_index.index());
504     assert!(retval);
505 }
506
507 impl<'a, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'tcx> {
508     #[inline]
509     fn join(&self, pred1: usize, pred2: usize) -> usize {
510         pred1 | pred2 // moves from both preds are in scope
511     }
512 }
513
514 impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> {
515     #[inline]
516     fn join(&self, pred1: usize, pred2: usize) -> usize {
517         pred1 | pred2 // "maybe" means we union effects of both preds
518     }
519 }
520
521 impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> {
522     #[inline]
523     fn join(&self, pred1: usize, pred2: usize) -> usize {
524         pred1 | pred2 // "maybe" means we union effects of both preds
525     }
526 }
527
528 impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
529     #[inline]
530     fn join(&self, pred1: usize, pred2: usize) -> usize {
531         pred1 & pred2 // "definitely" means we intersect effects of both preds
532     }
533 }
534
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).
539 //
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.
544
545 impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> {
546     #[inline]
547     fn bottom_value() -> bool {
548         false // bottom = no loans in scope by default
549     }
550 }
551
552 impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> {
553     #[inline]
554     fn bottom_value() -> bool {
555         false // bottom = uninitialized
556     }
557 }
558
559 impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> {
560     #[inline]
561     fn bottom_value() -> bool {
562         false // bottom = initialized (start_block_effect counters this at outset)
563     }
564 }
565
566 impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> {
567     #[inline]
568     fn bottom_value() -> bool {
569         true // bottom = initialized (start_block_effect counters this at outset)
570     }
571 }