]> git.proxmox.com Git - rustc.git/blob - src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
New upstream version 1.13.0+dfsg1
[rustc.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_lookup_result_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 on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data,
281 ctxt.move_data.rev_lookup.find(dest_lval),
282 |mpi| { in_out.add(&mpi); });
283 }
284 }
285
286 impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
287 type Idx = MovePathIndex;
288 type Ctxt = MoveDataParamEnv<'tcx>;
289 fn name() -> &'static str { "maybe_uninit" }
290 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
291 ctxt.move_data.move_paths.len()
292 }
293
294 // sets on_entry bits for Arg lvalues
295 fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>) {
296 // set all bits to 1 (uninit) before gathering counterevidence
297 for e in sets.on_entry.words_mut() { *e = !0; }
298
299 drop_flag_effects_for_function_entry(
300 self.tcx, self.mir, ctxt,
301 |path, s| {
302 assert!(s == DropFlagState::Present);
303 sets.on_entry.remove(&path);
304 });
305 }
306
307 fn statement_effect(&self,
308 ctxt: &Self::Ctxt,
309 sets: &mut BlockSets<MovePathIndex>,
310 bb: repr::BasicBlock,
311 idx: usize)
312 {
313 drop_flag_effects_for_location(
314 self.tcx, self.mir, ctxt,
315 Location { block: bb, statement_index: idx },
316 |path, s| Self::update_bits(sets, path, s)
317 )
318 }
319
320 fn terminator_effect(&self,
321 ctxt: &Self::Ctxt,
322 sets: &mut BlockSets<MovePathIndex>,
323 bb: repr::BasicBlock,
324 statements_len: usize)
325 {
326 drop_flag_effects_for_location(
327 self.tcx, self.mir, ctxt,
328 Location { block: bb, statement_index: statements_len },
329 |path, s| Self::update_bits(sets, path, s)
330 )
331 }
332
333 fn propagate_call_return(&self,
334 ctxt: &Self::Ctxt,
335 in_out: &mut IdxSet<MovePathIndex>,
336 _call_bb: repr::BasicBlock,
337 _dest_bb: repr::BasicBlock,
338 dest_lval: &repr::Lvalue) {
339 // when a call returns successfully, that means we need to set
340 // the bits for that dest_lval to 0 (initialized).
341 on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data,
342 ctxt.move_data.rev_lookup.find(dest_lval),
343 |mpi| { in_out.remove(&mpi); });
344 }
345 }
346
347 impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
348 type Idx = MovePathIndex;
349 type Ctxt = MoveDataParamEnv<'tcx>;
350 fn name() -> &'static str { "definite_init" }
351 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
352 ctxt.move_data.move_paths.len()
353 }
354
355 // sets on_entry bits for Arg lvalues
356 fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>) {
357 for e in sets.on_entry.words_mut() { *e = 0; }
358
359 drop_flag_effects_for_function_entry(
360 self.tcx, self.mir, ctxt,
361 |path, s| {
362 assert!(s == DropFlagState::Present);
363 sets.on_entry.add(&path);
364 });
365 }
366
367 fn statement_effect(&self,
368 ctxt: &Self::Ctxt,
369 sets: &mut BlockSets<MovePathIndex>,
370 bb: repr::BasicBlock,
371 idx: usize)
372 {
373 drop_flag_effects_for_location(
374 self.tcx, self.mir, ctxt,
375 Location { block: bb, statement_index: idx },
376 |path, s| Self::update_bits(sets, path, s)
377 )
378 }
379
380 fn terminator_effect(&self,
381 ctxt: &Self::Ctxt,
382 sets: &mut BlockSets<MovePathIndex>,
383 bb: repr::BasicBlock,
384 statements_len: usize)
385 {
386 drop_flag_effects_for_location(
387 self.tcx, self.mir, ctxt,
388 Location { block: bb, statement_index: statements_len },
389 |path, s| Self::update_bits(sets, path, s)
390 )
391 }
392
393 fn propagate_call_return(&self,
394 ctxt: &Self::Ctxt,
395 in_out: &mut IdxSet<MovePathIndex>,
396 _call_bb: repr::BasicBlock,
397 _dest_bb: repr::BasicBlock,
398 dest_lval: &repr::Lvalue) {
399 // when a call returns successfully, that means we need to set
400 // the bits for that dest_lval to 1 (initialized).
401 on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data,
402 ctxt.move_data.rev_lookup.find(dest_lval),
403 |mpi| { in_out.add(&mpi); });
404 }
405 }
406
407 impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
408 type Idx = MoveOutIndex;
409 type Ctxt = MoveDataParamEnv<'tcx>;
410 fn name() -> &'static str { "moving_out" }
411 fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
412 ctxt.move_data.moves.len()
413 }
414
415 fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets<MoveOutIndex>) {
416 // no move-statements have been executed prior to function
417 // execution, so this method has no effect on `_sets`.
418 }
419 fn statement_effect(&self,
420 ctxt: &Self::Ctxt,
421 sets: &mut BlockSets<MoveOutIndex>,
422 bb: repr::BasicBlock,
423 idx: usize) {
424 let (tcx, mir, move_data) = (self.tcx, self.mir, &ctxt.move_data);
425 let stmt = &mir[bb].statements[idx];
426 let loc_map = &move_data.loc_map;
427 let path_map = &move_data.path_map;
428 let rev_lookup = &move_data.rev_lookup;
429
430 let loc = Location { block: bb, statement_index: idx };
431 debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
432 stmt, loc, &loc_map[loc]);
433 for move_index in &loc_map[loc] {
434 // Every path deinitialized by a *particular move*
435 // has corresponding bit, "gen'ed" (i.e. set)
436 // here, in dataflow vector
437 zero_to_one(sets.gen_set.words_mut(), *move_index);
438 }
439 let bits_per_block = self.bits_per_block(ctxt);
440 match stmt.kind {
441 repr::StatementKind::SetDiscriminant { .. } => {
442 span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck");
443 }
444 repr::StatementKind::Assign(ref lvalue, _) => {
445 // assigning into this `lvalue` kills all
446 // MoveOuts from it, and *also* all MoveOuts
447 // for children and associated fragment sets.
448 on_lookup_result_bits(tcx,
449 mir,
450 move_data,
451 rev_lookup.find(lvalue),
452 |mpi| for moi in &path_map[mpi] {
453 assert!(moi.index() < bits_per_block);
454 sets.kill_set.add(&moi);
455 });
456 }
457 repr::StatementKind::StorageLive(_) |
458 repr::StatementKind::StorageDead(_) |
459 repr::StatementKind::Nop => {}
460 }
461 }
462
463 fn terminator_effect(&self,
464 ctxt: &Self::Ctxt,
465 sets: &mut BlockSets<MoveOutIndex>,
466 bb: repr::BasicBlock,
467 statements_len: usize)
468 {
469 let (mir, move_data) = (self.mir, &ctxt.move_data);
470 let term = mir[bb].terminator();
471 let loc_map = &move_data.loc_map;
472 let loc = Location { block: bb, statement_index: statements_len };
473 debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
474 term, loc, &loc_map[loc]);
475 let bits_per_block = self.bits_per_block(ctxt);
476 for move_index in &loc_map[loc] {
477 assert!(move_index.index() < bits_per_block);
478 zero_to_one(sets.gen_set.words_mut(), *move_index);
479 }
480 }
481
482 fn propagate_call_return(&self,
483 ctxt: &Self::Ctxt,
484 in_out: &mut IdxSet<MoveOutIndex>,
485 _call_bb: repr::BasicBlock,
486 _dest_bb: repr::BasicBlock,
487 dest_lval: &repr::Lvalue) {
488 let move_data = &ctxt.move_data;
489 let bits_per_block = self.bits_per_block(ctxt);
490
491 let path_map = &move_data.path_map;
492 on_lookup_result_bits(self.tcx,
493 self.mir,
494 move_data,
495 move_data.rev_lookup.find(dest_lval),
496 |mpi| for moi in &path_map[mpi] {
497 assert!(moi.index() < bits_per_block);
498 in_out.remove(&moi);
499 });
500 }
501 }
502
503 fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
504 let retval = bitvec.set_bit(move_index.index());
505 assert!(retval);
506 }
507
508 impl<'a, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'tcx> {
509 #[inline]
510 fn join(&self, pred1: usize, pred2: usize) -> usize {
511 pred1 | pred2 // moves from both preds are in scope
512 }
513 }
514
515 impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> {
516 #[inline]
517 fn join(&self, pred1: usize, pred2: usize) -> usize {
518 pred1 | pred2 // "maybe" means we union effects of both preds
519 }
520 }
521
522 impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> {
523 #[inline]
524 fn join(&self, pred1: usize, pred2: usize) -> usize {
525 pred1 | pred2 // "maybe" means we union effects of both preds
526 }
527 }
528
529 impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
530 #[inline]
531 fn join(&self, pred1: usize, pred2: usize) -> usize {
532 pred1 & pred2 // "definitely" means we intersect effects of both preds
533 }
534 }
535
536 // The way that dataflow fixed point iteration works, you want to
537 // start at bottom and work your way to a fixed point. Control-flow
538 // merges will apply the `join` operator to each block entry's current
539 // state (which starts at that bottom value).
540 //
541 // This means, for propagation across the graph, that you either want
542 // to start at all-zeroes and then use Union as your merge when
543 // propagating, or you start at all-ones and then use Intersect as
544 // your merge when propagating.
545
546 impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> {
547 #[inline]
548 fn bottom_value() -> bool {
549 false // bottom = no loans in scope by default
550 }
551 }
552
553 impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> {
554 #[inline]
555 fn bottom_value() -> bool {
556 false // bottom = uninitialized
557 }
558 }
559
560 impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> {
561 #[inline]
562 fn bottom_value() -> bool {
563 false // bottom = initialized (start_block_effect counters this at outset)
564 }
565 }
566
567 impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> {
568 #[inline]
569 fn bottom_value() -> bool {
570 true // bottom = initialized (start_block_effect counters this at outset)
571 }
572 }