]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
move the drop expansion code to rustc_mir
[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 use rustc_mir::util::elaborate_drops::DropFlagState;
18
19 use super::super::gather_moves::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex};
20 use super::super::MoveDataParamEnv;
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     mdpe: &'a MoveDataParamEnv<'tcx>,
70 }
71
72 impl<'a, 'tcx: 'a> MaybeInitializedLvals<'a, 'tcx> {
73     pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
74                mir: &'a Mir<'tcx>,
75                mdpe: &'a MoveDataParamEnv<'tcx>)
76                -> Self
77     {
78         MaybeInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
79     }
80 }
81
82 impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'tcx> {
83     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
84 }
85
86 /// `MaybeUninitializedLvals` tracks all l-values that might be
87 /// uninitialized upon reaching a particular point in the control flow
88 /// for a function.
89 ///
90 /// For example, in code like the following, we have corresponding
91 /// dataflow information shown in the right-hand comments.
92 ///
93 /// ```rust
94 /// struct S;
95 /// fn foo(pred: bool) {                       // maybe-uninit:
96 ///                                            // {a, b, c, d}
97 ///     let a = S; let b = S; let c; let d;    // {      c, d}
98 ///
99 ///     if pred {
100 ///         drop(a);                           // {a,    c, d}
101 ///         b = S;                             // {a,    c, d}
102 ///
103 ///     } else {
104 ///         drop(b);                           // {   b, c, d}
105 ///         d = S;                             // {   b, c   }
106 ///
107 ///     }                                      // {a, b, c, d}
108 ///
109 ///     c = S;                                 // {a, b,    d}
110 /// }
111 /// ```
112 ///
113 /// To determine whether an l-value *must* be uninitialized at a
114 /// particular control-flow point, one can take the set-difference
115 /// between this data and the data from `MaybeInitializedLvals` at the
116 /// corresponding control-flow point.
117 ///
118 /// Similarly, at a given `drop` statement, the set-intersection
119 /// between this data and `MaybeInitializedLvals` yields the set of
120 /// l-values that would require a dynamic drop-flag at that statement.
121 pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> {
122     tcx: TyCtxt<'a, 'tcx, 'tcx>,
123     mir: &'a Mir<'tcx>,
124     mdpe: &'a MoveDataParamEnv<'tcx>,
125 }
126
127 impl<'a, 'tcx: 'a> MaybeUninitializedLvals<'a, 'tcx> {
128     pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
129                mir: &'a Mir<'tcx>,
130                mdpe: &'a MoveDataParamEnv<'tcx>)
131                -> Self
132     {
133         MaybeUninitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
134     }
135 }
136
137 impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'tcx> {
138     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
139 }
140
141 /// `DefinitelyInitializedLvals` tracks all l-values that are definitely
142 /// initialized upon reaching a particular point in the control flow
143 /// for a function.
144 ///
145 /// FIXME: Note that once flow-analysis is complete, this should be
146 /// the set-complement of MaybeUninitializedLvals; thus we can get rid
147 /// of one or the other of these two. I'm inclined to get rid of
148 /// MaybeUninitializedLvals, simply because the sets will tend to be
149 /// smaller in this analysis and thus easier for humans to process
150 /// when debugging.
151 ///
152 /// For example, in code like the following, we have corresponding
153 /// dataflow information shown in the right-hand comments.
154 ///
155 /// ```rust
156 /// struct S;
157 /// fn foo(pred: bool) {                       // definite-init:
158 ///                                            // {          }
159 ///     let a = S; let b = S; let c; let d;    // {a, b      }
160 ///
161 ///     if pred {
162 ///         drop(a);                           // {   b,     }
163 ///         b = S;                             // {   b,     }
164 ///
165 ///     } else {
166 ///         drop(b);                           // {a,        }
167 ///         d = S;                             // {a,       d}
168 ///
169 ///     }                                      // {          }
170 ///
171 ///     c = S;                                 // {       c  }
172 /// }
173 /// ```
174 ///
175 /// To determine whether an l-value *may* be uninitialized at a
176 /// particular control-flow point, one can take the set-complement
177 /// of this data.
178 ///
179 /// Similarly, at a given `drop` statement, the set-difference between
180 /// this data and `MaybeInitializedLvals` yields the set of l-values
181 /// that would require a dynamic drop-flag at that statement.
182 pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> {
183     tcx: TyCtxt<'a, 'tcx, 'tcx>,
184     mir: &'a Mir<'tcx>,
185     mdpe: &'a MoveDataParamEnv<'tcx>,
186 }
187
188 impl<'a, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'tcx> {
189     pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
190                mir: &'a Mir<'tcx>,
191                mdpe: &'a MoveDataParamEnv<'tcx>)
192                -> Self
193     {
194         DefinitelyInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
195     }
196 }
197
198 impl<'a, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'tcx> {
199     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
200 }
201
202 /// `MovingOutStatements` tracks the statements that perform moves out
203 /// of particular l-values. More precisely, it tracks whether the
204 /// *effect* of such moves (namely, the uninitialization of the
205 /// l-value in question) can reach some point in the control-flow of
206 /// the function, or if that effect is "killed" by some intervening
207 /// operation reinitializing that l-value.
208 ///
209 /// The resulting dataflow is a more enriched version of
210 /// `MaybeUninitializedLvals`. Both structures on their own only tell
211 /// you if an l-value *might* be uninitialized at a given point in the
212 /// control flow. But `MovingOutStatements` also includes the added
213 /// data of *which* particular statement causing the deinitialization
214 /// that the borrow checker's error meessage may need to report.
215 #[allow(dead_code)]
216 pub struct MovingOutStatements<'a, 'tcx: 'a> {
217     tcx: TyCtxt<'a, 'tcx, 'tcx>,
218     mir: &'a Mir<'tcx>,
219     mdpe: &'a MoveDataParamEnv<'tcx>,
220 }
221
222 impl<'a, 'tcx> HasMoveData<'tcx> for MovingOutStatements<'a, 'tcx> {
223     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
224 }
225
226 impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
227     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
228                    state: DropFlagState)
229     {
230         match state {
231             DropFlagState::Absent => sets.kill(&path),
232             DropFlagState::Present => sets.gen(&path),
233         }
234     }
235 }
236
237 impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
238     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
239                    state: DropFlagState)
240     {
241         match state {
242             DropFlagState::Absent => sets.gen(&path),
243             DropFlagState::Present => sets.kill(&path),
244         }
245     }
246 }
247
248 impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
249     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
250                    state: DropFlagState)
251     {
252         match state {
253             DropFlagState::Absent => sets.kill(&path),
254             DropFlagState::Present => sets.gen(&path),
255         }
256     }
257 }
258
259 impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
260     type Idx = MovePathIndex;
261     fn name() -> &'static str { "maybe_init" }
262     fn bits_per_block(&self) -> usize {
263         self.move_data().move_paths.len()
264     }
265
266     fn start_block_effect(&self, sets: &mut BlockSets<MovePathIndex>)
267     {
268         drop_flag_effects_for_function_entry(
269             self.tcx, self.mir, self.mdpe,
270             |path, s| {
271                 assert!(s == DropFlagState::Present);
272                 sets.on_entry.add(&path);
273             });
274     }
275
276     fn statement_effect(&self,
277                         sets: &mut BlockSets<MovePathIndex>,
278                         bb: mir::BasicBlock,
279                         idx: usize)
280     {
281         drop_flag_effects_for_location(
282             self.tcx, self.mir, self.mdpe,
283             Location { block: bb, statement_index: idx },
284             |path, s| Self::update_bits(sets, path, s)
285         )
286     }
287
288     fn terminator_effect(&self,
289                          sets: &mut BlockSets<MovePathIndex>,
290                          bb: mir::BasicBlock,
291                          statements_len: usize)
292     {
293         drop_flag_effects_for_location(
294             self.tcx, self.mir, self.mdpe,
295             Location { block: bb, statement_index: statements_len },
296             |path, s| Self::update_bits(sets, path, s)
297         )
298     }
299
300     fn propagate_call_return(&self,
301                              in_out: &mut IdxSet<MovePathIndex>,
302                              _call_bb: mir::BasicBlock,
303                              _dest_bb: mir::BasicBlock,
304                              dest_lval: &mir::Lvalue) {
305         // when a call returns successfully, that means we need to set
306         // the bits for that dest_lval to 1 (initialized).
307         on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
308                               self.move_data().rev_lookup.find(dest_lval),
309                               |mpi| { in_out.add(&mpi); });
310     }
311 }
312
313 impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
314     type Idx = MovePathIndex;
315     fn name() -> &'static str { "maybe_uninit" }
316     fn bits_per_block(&self) -> usize {
317         self.move_data().move_paths.len()
318     }
319
320     // sets on_entry bits for Arg lvalues
321     fn start_block_effect(&self, sets: &mut BlockSets<MovePathIndex>) {
322         // set all bits to 1 (uninit) before gathering counterevidence
323         for e in sets.on_entry.words_mut() { *e = !0; }
324
325         drop_flag_effects_for_function_entry(
326             self.tcx, self.mir, self.mdpe,
327             |path, s| {
328                 assert!(s == DropFlagState::Present);
329                 sets.on_entry.remove(&path);
330             });
331     }
332
333     fn statement_effect(&self,
334                         sets: &mut BlockSets<MovePathIndex>,
335                         bb: mir::BasicBlock,
336                         idx: usize)
337     {
338         drop_flag_effects_for_location(
339             self.tcx, self.mir, self.mdpe,
340             Location { block: bb, statement_index: idx },
341             |path, s| Self::update_bits(sets, path, s)
342         )
343     }
344
345     fn terminator_effect(&self,
346                          sets: &mut BlockSets<MovePathIndex>,
347                          bb: mir::BasicBlock,
348                          statements_len: usize)
349     {
350         drop_flag_effects_for_location(
351             self.tcx, self.mir, self.mdpe,
352             Location { block: bb, statement_index: statements_len },
353             |path, s| Self::update_bits(sets, path, s)
354         )
355     }
356
357     fn propagate_call_return(&self,
358                              in_out: &mut IdxSet<MovePathIndex>,
359                              _call_bb: mir::BasicBlock,
360                              _dest_bb: mir::BasicBlock,
361                              dest_lval: &mir::Lvalue) {
362         // when a call returns successfully, that means we need to set
363         // the bits for that dest_lval to 0 (initialized).
364         on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
365                               self.move_data().rev_lookup.find(dest_lval),
366                               |mpi| { in_out.remove(&mpi); });
367     }
368 }
369
370 impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
371     type Idx = MovePathIndex;
372     fn name() -> &'static str { "definite_init" }
373     fn bits_per_block(&self) -> usize {
374         self.move_data().move_paths.len()
375     }
376
377     // sets on_entry bits for Arg lvalues
378     fn start_block_effect(&self, sets: &mut BlockSets<MovePathIndex>) {
379         for e in sets.on_entry.words_mut() { *e = 0; }
380
381         drop_flag_effects_for_function_entry(
382             self.tcx, self.mir, self.mdpe,
383             |path, s| {
384                 assert!(s == DropFlagState::Present);
385                 sets.on_entry.add(&path);
386             });
387     }
388
389     fn statement_effect(&self,
390                         sets: &mut BlockSets<MovePathIndex>,
391                         bb: mir::BasicBlock,
392                         idx: usize)
393     {
394         drop_flag_effects_for_location(
395             self.tcx, self.mir, self.mdpe,
396             Location { block: bb, statement_index: idx },
397             |path, s| Self::update_bits(sets, path, s)
398         )
399     }
400
401     fn terminator_effect(&self,
402                          sets: &mut BlockSets<MovePathIndex>,
403                          bb: mir::BasicBlock,
404                          statements_len: usize)
405     {
406         drop_flag_effects_for_location(
407             self.tcx, self.mir, self.mdpe,
408             Location { block: bb, statement_index: statements_len },
409             |path, s| Self::update_bits(sets, path, s)
410         )
411     }
412
413     fn propagate_call_return(&self,
414                              in_out: &mut IdxSet<MovePathIndex>,
415                              _call_bb: mir::BasicBlock,
416                              _dest_bb: mir::BasicBlock,
417                              dest_lval: &mir::Lvalue) {
418         // when a call returns successfully, that means we need to set
419         // the bits for that dest_lval to 1 (initialized).
420         on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
421                               self.move_data().rev_lookup.find(dest_lval),
422                               |mpi| { in_out.add(&mpi); });
423     }
424 }
425
426 impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
427     type Idx = MoveOutIndex;
428     fn name() -> &'static str { "moving_out" }
429     fn bits_per_block(&self) -> usize {
430         self.move_data().moves.len()
431     }
432
433     fn start_block_effect(&self, _sets: &mut BlockSets<MoveOutIndex>) {
434         // no move-statements have been executed prior to function
435         // execution, so this method has no effect on `_sets`.
436     }
437     fn statement_effect(&self,
438                         sets: &mut BlockSets<MoveOutIndex>,
439                         bb: mir::BasicBlock,
440                         idx: usize) {
441         let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data());
442         let stmt = &mir[bb].statements[idx];
443         let loc_map = &move_data.loc_map;
444         let path_map = &move_data.path_map;
445         let rev_lookup = &move_data.rev_lookup;
446
447         let loc = Location { block: bb, statement_index: idx };
448         debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
449                stmt, loc, &loc_map[loc]);
450         for move_index in &loc_map[loc] {
451             // Every path deinitialized by a *particular move*
452             // has corresponding bit, "gen'ed" (i.e. set)
453             // here, in dataflow vector
454             zero_to_one(sets.gen_set.words_mut(), *move_index);
455         }
456         let bits_per_block = self.bits_per_block();
457         match stmt.kind {
458             mir::StatementKind::SetDiscriminant { .. } => {
459                 span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck");
460             }
461             mir::StatementKind::Assign(ref lvalue, _) => {
462                 // assigning into this `lvalue` kills all
463                 // MoveOuts from it, and *also* all MoveOuts
464                 // for children and associated fragment sets.
465                 on_lookup_result_bits(tcx,
466                                      mir,
467                                      move_data,
468                                      rev_lookup.find(lvalue),
469                                      |mpi| for moi in &path_map[mpi] {
470                                          assert!(moi.index() < bits_per_block);
471                                          sets.kill_set.add(&moi);
472                                      });
473             }
474             mir::StatementKind::StorageLive(_) |
475             mir::StatementKind::StorageDead(_) |
476             mir::StatementKind::InlineAsm { .. } |
477             mir::StatementKind::Nop => {}
478         }
479     }
480
481     fn terminator_effect(&self,
482                          sets: &mut BlockSets<MoveOutIndex>,
483                          bb: mir::BasicBlock,
484                          statements_len: usize)
485     {
486         let (mir, move_data) = (self.mir, self.move_data());
487         let term = mir[bb].terminator();
488         let loc_map = &move_data.loc_map;
489         let loc = Location { block: bb, statement_index: statements_len };
490         debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
491                term, loc, &loc_map[loc]);
492         let bits_per_block = self.bits_per_block();
493         for move_index in &loc_map[loc] {
494             assert!(move_index.index() < bits_per_block);
495             zero_to_one(sets.gen_set.words_mut(), *move_index);
496         }
497     }
498
499     fn propagate_call_return(&self,
500                              in_out: &mut IdxSet<MoveOutIndex>,
501                              _call_bb: mir::BasicBlock,
502                              _dest_bb: mir::BasicBlock,
503                              dest_lval: &mir::Lvalue) {
504         let move_data = self.move_data();
505         let bits_per_block = self.bits_per_block();
506
507         let path_map = &move_data.path_map;
508         on_lookup_result_bits(self.tcx,
509                               self.mir,
510                               move_data,
511                               move_data.rev_lookup.find(dest_lval),
512                               |mpi| for moi in &path_map[mpi] {
513                                   assert!(moi.index() < bits_per_block);
514                                   in_out.remove(&moi);
515                               });
516     }
517 }
518
519 fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
520     let retval = bitvec.set_bit(move_index.index());
521     assert!(retval);
522 }
523
524 impl<'a, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'tcx> {
525     #[inline]
526     fn join(&self, pred1: usize, pred2: usize) -> usize {
527         pred1 | pred2 // moves from both preds are in scope
528     }
529 }
530
531 impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> {
532     #[inline]
533     fn join(&self, pred1: usize, pred2: usize) -> usize {
534         pred1 | pred2 // "maybe" means we union effects of both preds
535     }
536 }
537
538 impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> {
539     #[inline]
540     fn join(&self, pred1: usize, pred2: usize) -> usize {
541         pred1 | pred2 // "maybe" means we union effects of both preds
542     }
543 }
544
545 impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
546     #[inline]
547     fn join(&self, pred1: usize, pred2: usize) -> usize {
548         pred1 & pred2 // "definitely" means we intersect effects of both preds
549     }
550 }
551
552 // The way that dataflow fixed point iteration works, you want to
553 // start at bottom and work your way to a fixed point. Control-flow
554 // merges will apply the `join` operator to each block entry's current
555 // state (which starts at that bottom value).
556 //
557 // This means, for propagation across the graph, that you either want
558 // to start at all-zeroes and then use Union as your merge when
559 // propagating, or you start at all-ones and then use Intersect as
560 // your merge when propagating.
561
562 impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> {
563     #[inline]
564     fn bottom_value() -> bool {
565         false // bottom = no loans in scope by default
566     }
567 }
568
569 impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> {
570     #[inline]
571     fn bottom_value() -> bool {
572         false // bottom = uninitialized
573     }
574 }
575
576 impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> {
577     #[inline]
578     fn bottom_value() -> bool {
579         false // bottom = initialized (start_block_effect counters this at outset)
580     }
581 }
582
583 impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> {
584     #[inline]
585     fn bottom_value() -> bool {
586         true // bottom = initialized (start_block_effect counters this at outset)
587     }
588 }