1 // Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Dataflow analyses are built upon some interpretation of the
12 //! bitvectors attached to each basic block, represented via a
13 //! zero-sized structure.
15 use rustc
::ty
::TyCtxt
;
16 use rustc
::mir
::{self, Mir, Location}
;
17 use rustc_data_structures
::bitslice
::{BitwiseOperator}
;
18 use rustc_data_structures
::indexed_set
::{IdxSet}
;
19 use rustc_data_structures
::indexed_vec
::Idx
;
21 use super::MoveDataParamEnv
;
22 use util
::elaborate_drops
::DropFlagState
;
24 use super::move_paths
::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex, InitIndex}
;
25 use super::move_paths
::{LookupResult, InitKind}
;
26 use super::{BitDenotation, BlockSets, InitialFlow}
;
28 use super::drop_flag_effects_for_function_entry
;
29 use super::drop_flag_effects_for_location
;
30 use super::{on_lookup_result_bits, for_location_inits}
;
34 pub use self::storage_liveness
::*;
37 pub(super) mod borrows
;
39 /// `MaybeInitializedLvals` tracks all l-values that might be
40 /// initialized upon reaching a particular point in the control flow
43 /// For example, in code like the following, we have corresponding
44 /// dataflow information shown in the right-hand comments.
48 /// fn foo(pred: bool) { // maybe-init:
50 /// let a = S; let b = S; let c; let d; // {a, b}
62 /// c = S; // {a, b, c, d}
66 /// To determine whether an l-value *must* be initialized at a
67 /// particular control-flow point, one can take the set-difference
68 /// between this data and the data from `MaybeUninitializedLvals` at the
69 /// corresponding control-flow point.
71 /// Similarly, at a given `drop` statement, the set-intersection
72 /// between this data and `MaybeUninitializedLvals` yields the set of
73 /// l-values that would require a dynamic drop-flag at that statement.
74 pub struct MaybeInitializedLvals
<'a
, 'gcx
: 'tcx
, 'tcx
: 'a
> {
75 tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
77 mdpe
: &'a MoveDataParamEnv
<'gcx
, 'tcx
>,
80 impl<'a
, 'gcx
: 'tcx
, 'tcx
> MaybeInitializedLvals
<'a
, 'gcx
, 'tcx
> {
81 pub fn new(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
83 mdpe
: &'a MoveDataParamEnv
<'gcx
, 'tcx
>)
86 MaybeInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
90 impl<'a
, 'gcx
, 'tcx
> HasMoveData
<'tcx
> for MaybeInitializedLvals
<'a
, 'gcx
, 'tcx
> {
91 fn move_data(&self) -> &MoveData
<'tcx
> { &self.mdpe.move_data }
94 /// `MaybeUninitializedLvals` tracks all l-values that might be
95 /// uninitialized upon reaching a particular point in the control flow
98 /// For example, in code like the following, we have corresponding
99 /// dataflow information shown in the right-hand comments.
103 /// fn foo(pred: bool) { // maybe-uninit:
105 /// let a = S; let b = S; let c; let d; // { c, d}
108 /// drop(a); // {a, c, d}
109 /// b = S; // {a, c, d}
112 /// drop(b); // { b, c, d}
113 /// d = S; // { b, c }
115 /// } // {a, b, c, d}
117 /// c = S; // {a, b, d}
121 /// To determine whether an l-value *must* be uninitialized at a
122 /// particular control-flow point, one can take the set-difference
123 /// between this data and the data from `MaybeInitializedLvals` at the
124 /// corresponding control-flow point.
126 /// Similarly, at a given `drop` statement, the set-intersection
127 /// between this data and `MaybeInitializedLvals` yields the set of
128 /// l-values that would require a dynamic drop-flag at that statement.
129 pub struct MaybeUninitializedLvals
<'a
, 'gcx
: 'tcx
, 'tcx
: 'a
> {
130 tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
132 mdpe
: &'a MoveDataParamEnv
<'gcx
, 'tcx
>,
135 impl<'a
, 'gcx
, 'tcx
> MaybeUninitializedLvals
<'a
, 'gcx
, 'tcx
> {
136 pub fn new(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
138 mdpe
: &'a MoveDataParamEnv
<'gcx
, 'tcx
>)
141 MaybeUninitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
145 impl<'a
, 'gcx
, 'tcx
> HasMoveData
<'tcx
> for MaybeUninitializedLvals
<'a
, 'gcx
, 'tcx
> {
146 fn move_data(&self) -> &MoveData
<'tcx
> { &self.mdpe.move_data }
149 /// `DefinitelyInitializedLvals` tracks all l-values that are definitely
150 /// initialized upon reaching a particular point in the control flow
153 /// FIXME: Note that once flow-analysis is complete, this should be
154 /// the set-complement of MaybeUninitializedLvals; thus we can get rid
155 /// of one or the other of these two. I'm inclined to get rid of
156 /// MaybeUninitializedLvals, simply because the sets will tend to be
157 /// smaller in this analysis and thus easier for humans to process
160 /// For example, in code like the following, we have corresponding
161 /// dataflow information shown in the right-hand comments.
165 /// fn foo(pred: bool) { // definite-init:
167 /// let a = S; let b = S; let c; let d; // {a, b }
170 /// drop(a); // { b, }
174 /// drop(b); // {a, }
183 /// To determine whether an l-value *may* be uninitialized at a
184 /// particular control-flow point, one can take the set-complement
187 /// Similarly, at a given `drop` statement, the set-difference between
188 /// this data and `MaybeInitializedLvals` yields the set of l-values
189 /// that would require a dynamic drop-flag at that statement.
190 pub struct DefinitelyInitializedLvals
<'a
, 'gcx
: 'tcx
, 'tcx
: 'a
> {
191 tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
193 mdpe
: &'a MoveDataParamEnv
<'gcx
, 'tcx
>,
196 impl<'a
, 'gcx
, 'tcx
: 'a
> DefinitelyInitializedLvals
<'a
, 'gcx
, 'tcx
> {
197 pub fn new(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
199 mdpe
: &'a MoveDataParamEnv
<'gcx
, 'tcx
>)
202 DefinitelyInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
206 impl<'a
, 'gcx
, 'tcx
: 'a
> HasMoveData
<'tcx
> for DefinitelyInitializedLvals
<'a
, 'gcx
, 'tcx
> {
207 fn move_data(&self) -> &MoveData
<'tcx
> { &self.mdpe.move_data }
210 /// `MovingOutStatements` tracks the statements that perform moves out
211 /// of particular l-values. More precisely, it tracks whether the
212 /// *effect* of such moves (namely, the uninitialization of the
213 /// l-value in question) can reach some point in the control-flow of
214 /// the function, or if that effect is "killed" by some intervening
215 /// operation reinitializing that l-value.
217 /// The resulting dataflow is a more enriched version of
218 /// `MaybeUninitializedLvals`. Both structures on their own only tell
219 /// you if an l-value *might* be uninitialized at a given point in the
220 /// control flow. But `MovingOutStatements` also includes the added
221 /// data of *which* particular statement causing the deinitialization
222 /// that the borrow checker's error message may need to report.
224 pub struct MovingOutStatements
<'a
, 'gcx
: 'tcx
, 'tcx
: 'a
> {
225 tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
227 mdpe
: &'a MoveDataParamEnv
<'gcx
, 'tcx
>,
230 impl<'a
, 'gcx
: 'tcx
, 'tcx
: 'a
> MovingOutStatements
<'a
, 'gcx
, 'tcx
> {
231 pub fn new(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
233 mdpe
: &'a MoveDataParamEnv
<'gcx
, 'tcx
>)
236 MovingOutStatements { tcx: tcx, mir: mir, mdpe: mdpe }
240 impl<'a
, 'gcx
, 'tcx
> HasMoveData
<'tcx
> for MovingOutStatements
<'a
, 'gcx
, 'tcx
> {
241 fn move_data(&self) -> &MoveData
<'tcx
> { &self.mdpe.move_data }
244 /// `EverInitializedLvals` tracks all l-values that might have ever been
245 /// initialized upon reaching a particular point in the control flow
246 /// for a function, without an intervening `Storage Dead`.
248 /// This dataflow is used to determine if an immutable local variable may
251 /// For example, in code like the following, we have corresponding
252 /// dataflow information shown in the right-hand comments.
256 /// fn foo(pred: bool) { // ever-init:
258 /// let a = S; let b = S; let c; let d; // {a, b }
261 /// drop(a); // {a, b, }
262 /// b = S; // {a, b, }
265 /// drop(b); // {a, b, }
266 /// d = S; // {a, b, d }
270 /// c = S; // {a, b, c, d }
273 pub struct EverInitializedLvals
<'a
, 'gcx
: 'tcx
, 'tcx
: 'a
> {
274 tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
276 mdpe
: &'a MoveDataParamEnv
<'gcx
, 'tcx
>,
279 impl<'a
, 'gcx
: 'tcx
, 'tcx
: 'a
> EverInitializedLvals
<'a
, 'gcx
, 'tcx
> {
280 pub fn new(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
282 mdpe
: &'a MoveDataParamEnv
<'gcx
, 'tcx
>)
285 EverInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
289 impl<'a
, 'gcx
, 'tcx
> HasMoveData
<'tcx
> for EverInitializedLvals
<'a
, 'gcx
, 'tcx
> {
290 fn move_data(&self) -> &MoveData
<'tcx
> { &self.mdpe.move_data }
294 impl<'a
, 'gcx
, 'tcx
> MaybeInitializedLvals
<'a
, 'gcx
, 'tcx
> {
295 fn update_bits(sets
: &mut BlockSets
<MovePathIndex
>, path
: MovePathIndex
,
296 state
: DropFlagState
)
299 DropFlagState
::Absent
=> sets
.kill(&path
),
300 DropFlagState
::Present
=> sets
.gen(&path
),
305 impl<'a
, 'gcx
, 'tcx
> MaybeUninitializedLvals
<'a
, 'gcx
, 'tcx
> {
306 fn update_bits(sets
: &mut BlockSets
<MovePathIndex
>, path
: MovePathIndex
,
307 state
: DropFlagState
)
310 DropFlagState
::Absent
=> sets
.gen(&path
),
311 DropFlagState
::Present
=> sets
.kill(&path
),
316 impl<'a
, 'gcx
, 'tcx
> DefinitelyInitializedLvals
<'a
, 'gcx
, 'tcx
> {
317 fn update_bits(sets
: &mut BlockSets
<MovePathIndex
>, path
: MovePathIndex
,
318 state
: DropFlagState
)
321 DropFlagState
::Absent
=> sets
.kill(&path
),
322 DropFlagState
::Present
=> sets
.gen(&path
),
327 impl<'a
, 'gcx
, 'tcx
> BitDenotation
for MaybeInitializedLvals
<'a
, 'gcx
, 'tcx
> {
328 type Idx
= MovePathIndex
;
329 fn name() -> &'
static str { "maybe_init" }
330 fn bits_per_block(&self) -> usize {
331 self.move_data().move_paths
.len()
334 fn start_block_effect(&self, entry_set
: &mut IdxSet
<MovePathIndex
>) {
335 drop_flag_effects_for_function_entry(
336 self.tcx
, self.mir
, self.mdpe
,
338 assert
!(s
== DropFlagState
::Present
);
339 entry_set
.add(&path
);
343 fn statement_effect(&self,
344 sets
: &mut BlockSets
<MovePathIndex
>,
347 drop_flag_effects_for_location(
348 self.tcx
, self.mir
, self.mdpe
,
350 |path
, s
| Self::update_bits(sets
, path
, s
)
354 fn terminator_effect(&self,
355 sets
: &mut BlockSets
<MovePathIndex
>,
358 drop_flag_effects_for_location(
359 self.tcx
, self.mir
, self.mdpe
,
361 |path
, s
| Self::update_bits(sets
, path
, s
)
365 fn propagate_call_return(&self,
366 in_out
: &mut IdxSet
<MovePathIndex
>,
367 _call_bb
: mir
::BasicBlock
,
368 _dest_bb
: mir
::BasicBlock
,
369 dest_place
: &mir
::Place
) {
370 // when a call returns successfully, that means we need to set
371 // the bits for that dest_place to 1 (initialized).
372 on_lookup_result_bits(self.tcx
, self.mir
, self.move_data(),
373 self.move_data().rev_lookup
.find(dest_place
),
374 |mpi
| { in_out.add(&mpi); }
);
378 impl<'a
, 'gcx
, 'tcx
> BitDenotation
for MaybeUninitializedLvals
<'a
, 'gcx
, 'tcx
> {
379 type Idx
= MovePathIndex
;
380 fn name() -> &'
static str { "maybe_uninit" }
381 fn bits_per_block(&self) -> usize {
382 self.move_data().move_paths
.len()
385 // sets on_entry bits for Arg places
386 fn start_block_effect(&self, entry_set
: &mut IdxSet
<MovePathIndex
>) {
387 // set all bits to 1 (uninit) before gathering counterevidence
388 for e
in entry_set
.words_mut() { *e = !0; }
390 drop_flag_effects_for_function_entry(
391 self.tcx
, self.mir
, self.mdpe
,
393 assert
!(s
== DropFlagState
::Present
);
394 entry_set
.remove(&path
);
398 fn statement_effect(&self,
399 sets
: &mut BlockSets
<MovePathIndex
>,
402 drop_flag_effects_for_location(
403 self.tcx
, self.mir
, self.mdpe
,
405 |path
, s
| Self::update_bits(sets
, path
, s
)
409 fn terminator_effect(&self,
410 sets
: &mut BlockSets
<MovePathIndex
>,
413 drop_flag_effects_for_location(
414 self.tcx
, self.mir
, self.mdpe
,
416 |path
, s
| Self::update_bits(sets
, path
, s
)
420 fn propagate_call_return(&self,
421 in_out
: &mut IdxSet
<MovePathIndex
>,
422 _call_bb
: mir
::BasicBlock
,
423 _dest_bb
: mir
::BasicBlock
,
424 dest_place
: &mir
::Place
) {
425 // when a call returns successfully, that means we need to set
426 // the bits for that dest_place to 0 (initialized).
427 on_lookup_result_bits(self.tcx
, self.mir
, self.move_data(),
428 self.move_data().rev_lookup
.find(dest_place
),
429 |mpi
| { in_out.remove(&mpi); }
);
433 impl<'a
, 'gcx
, 'tcx
> BitDenotation
for DefinitelyInitializedLvals
<'a
, 'gcx
, 'tcx
> {
434 type Idx
= MovePathIndex
;
435 fn name() -> &'
static str { "definite_init" }
436 fn bits_per_block(&self) -> usize {
437 self.move_data().move_paths
.len()
440 // sets on_entry bits for Arg places
441 fn start_block_effect(&self, entry_set
: &mut IdxSet
<MovePathIndex
>) {
442 for e
in entry_set
.words_mut() { *e = 0; }
444 drop_flag_effects_for_function_entry(
445 self.tcx
, self.mir
, self.mdpe
,
447 assert
!(s
== DropFlagState
::Present
);
448 entry_set
.add(&path
);
452 fn statement_effect(&self,
453 sets
: &mut BlockSets
<MovePathIndex
>,
456 drop_flag_effects_for_location(
457 self.tcx
, self.mir
, self.mdpe
,
459 |path
, s
| Self::update_bits(sets
, path
, s
)
463 fn terminator_effect(&self,
464 sets
: &mut BlockSets
<MovePathIndex
>,
467 drop_flag_effects_for_location(
468 self.tcx
, self.mir
, self.mdpe
,
470 |path
, s
| Self::update_bits(sets
, path
, s
)
474 fn propagate_call_return(&self,
475 in_out
: &mut IdxSet
<MovePathIndex
>,
476 _call_bb
: mir
::BasicBlock
,
477 _dest_bb
: mir
::BasicBlock
,
478 dest_place
: &mir
::Place
) {
479 // when a call returns successfully, that means we need to set
480 // the bits for that dest_place to 1 (initialized).
481 on_lookup_result_bits(self.tcx
, self.mir
, self.move_data(),
482 self.move_data().rev_lookup
.find(dest_place
),
483 |mpi
| { in_out.add(&mpi); }
);
487 impl<'a
, 'gcx
, 'tcx
> BitDenotation
for MovingOutStatements
<'a
, 'gcx
, 'tcx
> {
488 type Idx
= MoveOutIndex
;
489 fn name() -> &'
static str { "moving_out" }
490 fn bits_per_block(&self) -> usize {
491 self.move_data().moves
.len()
494 fn start_block_effect(&self, _sets
: &mut IdxSet
<MoveOutIndex
>) {
495 // no move-statements have been executed prior to function
496 // execution, so this method has no effect on `_sets`.
499 fn statement_effect(&self,
500 sets
: &mut BlockSets
<MoveOutIndex
>,
501 location
: Location
) {
502 let (tcx
, mir
, move_data
) = (self.tcx
, self.mir
, self.move_data());
503 let stmt
= &mir
[location
.block
].statements
[location
.statement_index
];
504 let loc_map
= &move_data
.loc_map
;
505 let path_map
= &move_data
.path_map
;
508 // this analysis only tries to find moves explicitly
509 // written by the user, so we ignore the move-outs
510 // created by `StorageDead` and at the beginning
512 mir
::StatementKind
::StorageDead(_
) => {}
514 debug
!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
515 stmt
, location
, &loc_map
[location
]);
516 // Every path deinitialized by a *particular move*
517 // has corresponding bit, "gen'ed" (i.e. set)
518 // here, in dataflow vector
519 sets
.gen_all_and_assert_dead(&loc_map
[location
]);
523 for_location_inits(tcx
, mir
, move_data
, location
,
524 |mpi
| sets
.kill_all(&path_map
[mpi
]));
527 fn terminator_effect(&self,
528 sets
: &mut BlockSets
<MoveOutIndex
>,
531 let (tcx
, mir
, move_data
) = (self.tcx
, self.mir
, self.move_data());
532 let term
= mir
[location
.block
].terminator();
533 let loc_map
= &move_data
.loc_map
;
534 let path_map
= &move_data
.path_map
;
536 debug
!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
537 term
, location
, &loc_map
[location
]);
538 sets
.gen_all_and_assert_dead(&loc_map
[location
]);
540 for_location_inits(tcx
, mir
, move_data
, location
,
541 |mpi
| sets
.kill_all(&path_map
[mpi
]));
544 fn propagate_call_return(&self,
545 in_out
: &mut IdxSet
<MoveOutIndex
>,
546 _call_bb
: mir
::BasicBlock
,
547 _dest_bb
: mir
::BasicBlock
,
548 dest_place
: &mir
::Place
) {
549 let move_data
= self.move_data();
550 let bits_per_block
= self.bits_per_block();
552 let path_map
= &move_data
.path_map
;
553 on_lookup_result_bits(self.tcx
,
556 move_data
.rev_lookup
.find(dest_place
),
557 |mpi
| for moi
in &path_map
[mpi
] {
558 assert
!(moi
.index() < bits_per_block
);
564 impl<'a
, 'gcx
, 'tcx
> BitDenotation
for EverInitializedLvals
<'a
, 'gcx
, 'tcx
> {
565 type Idx
= InitIndex
;
566 fn name() -> &'
static str { "ever_init" }
567 fn bits_per_block(&self) -> usize {
568 self.move_data().inits
.len()
571 fn start_block_effect(&self, entry_set
: &mut IdxSet
<InitIndex
>) {
572 for arg_init
in 0..self.mir
.arg_count
{
573 entry_set
.add(&InitIndex
::new(arg_init
));
577 fn statement_effect(&self,
578 sets
: &mut BlockSets
<InitIndex
>,
579 location
: Location
) {
580 let (_
, mir
, move_data
) = (self.tcx
, self.mir
, self.move_data());
581 let stmt
= &mir
[location
.block
].statements
[location
.statement_index
];
582 let init_path_map
= &move_data
.init_path_map
;
583 let init_loc_map
= &move_data
.init_loc_map
;
584 let rev_lookup
= &move_data
.rev_lookup
;
586 debug
!("statement {:?} at loc {:?} initializes move_indexes {:?}",
587 stmt
, location
, &init_loc_map
[location
]);
588 sets
.gen_all(&init_loc_map
[location
]);
591 mir
::StatementKind
::StorageDead(local
) |
592 mir
::StatementKind
::StorageLive(local
) => {
593 // End inits for StorageDead and StorageLive, so that an immutable
594 // variable can be reinitialized on the next iteration of the loop.
596 // FIXME(#46525): We *need* to do this for StorageLive as well as
597 // StorageDead, because lifetimes of match bindings with guards are
598 // weird - i.e. this code
604 // if { println!("a={}", a); false } => {}
610 // runs the guard twice, using the same binding for `a`, and only
611 // storagedeads after everything ends, so if we don't regard the
612 // storagelive as killing storage, we would have a multiple assignment
613 // to immutable data error.
614 if let LookupResult
::Exact(mpi
) = rev_lookup
.find(&mir
::Place
::Local(local
)) {
615 debug
!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
616 stmt
, location
, &init_path_map
[mpi
]);
617 sets
.kill_all(&init_path_map
[mpi
]);
624 fn terminator_effect(&self,
625 sets
: &mut BlockSets
<InitIndex
>,
628 let (mir
, move_data
) = (self.mir
, self.move_data());
629 let term
= mir
[location
.block
].terminator();
630 let init_loc_map
= &move_data
.init_loc_map
;
631 debug
!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
632 term
, location
, &init_loc_map
[location
]);
634 init_loc_map
[location
].iter().filter(|init_index
| {
635 move_data
.inits
[**init_index
].kind
!= InitKind
::NonPanicPathOnly
640 fn propagate_call_return(&self,
641 in_out
: &mut IdxSet
<InitIndex
>,
642 call_bb
: mir
::BasicBlock
,
643 _dest_bb
: mir
::BasicBlock
,
644 _dest_place
: &mir
::Place
) {
645 let move_data
= self.move_data();
646 let bits_per_block
= self.bits_per_block();
647 let init_loc_map
= &move_data
.init_loc_map
;
649 let call_loc
= Location
{
651 statement_index
: self.mir
[call_bb
].statements
.len(),
653 for init_index
in &init_loc_map
[call_loc
] {
654 assert
!(init_index
.index() < bits_per_block
);
655 in_out
.add(init_index
);
660 impl<'a
, 'gcx
, 'tcx
> BitwiseOperator
for MaybeInitializedLvals
<'a
, 'gcx
, 'tcx
> {
662 fn join(&self, pred1
: usize, pred2
: usize) -> usize {
663 pred1
| pred2
// "maybe" means we union effects of both preds
667 impl<'a
, 'gcx
, 'tcx
> BitwiseOperator
for MaybeUninitializedLvals
<'a
, 'gcx
, 'tcx
> {
669 fn join(&self, pred1
: usize, pred2
: usize) -> usize {
670 pred1
| pred2
// "maybe" means we union effects of both preds
674 impl<'a
, 'gcx
, 'tcx
> BitwiseOperator
for DefinitelyInitializedLvals
<'a
, 'gcx
, 'tcx
> {
676 fn join(&self, pred1
: usize, pred2
: usize) -> usize {
677 pred1
& pred2
// "definitely" means we intersect effects of both preds
681 impl<'a
, 'gcx
, 'tcx
> BitwiseOperator
for MovingOutStatements
<'a
, 'gcx
, 'tcx
> {
683 fn join(&self, pred1
: usize, pred2
: usize) -> usize {
684 pred1
| pred2
// moves from both preds are in scope
688 impl<'a
, 'gcx
, 'tcx
> BitwiseOperator
for EverInitializedLvals
<'a
, 'gcx
, 'tcx
> {
690 fn join(&self, pred1
: usize, pred2
: usize) -> usize {
691 pred1
| pred2
// inits from both preds are in scope
695 // The way that dataflow fixed point iteration works, you want to
696 // start at bottom and work your way to a fixed point. Control-flow
697 // merges will apply the `join` operator to each block entry's current
698 // state (which starts at that bottom value).
700 // This means, for propagation across the graph, that you either want
701 // to start at all-zeroes and then use Union as your merge when
702 // propagating, or you start at all-ones and then use Intersect as
703 // your merge when propagating.
705 impl<'a
, 'gcx
, 'tcx
> InitialFlow
for MaybeInitializedLvals
<'a
, 'gcx
, 'tcx
> {
707 fn bottom_value() -> bool
{
708 false // bottom = uninitialized
712 impl<'a
, 'gcx
, 'tcx
> InitialFlow
for MaybeUninitializedLvals
<'a
, 'gcx
, 'tcx
> {
714 fn bottom_value() -> bool
{
715 false // bottom = initialized (start_block_effect counters this at outset)
719 impl<'a
, 'gcx
, 'tcx
> InitialFlow
for DefinitelyInitializedLvals
<'a
, 'gcx
, 'tcx
> {
721 fn bottom_value() -> bool
{
722 true // bottom = initialized (start_block_effect counters this at outset)
726 impl<'a
, 'gcx
, 'tcx
> InitialFlow
for MovingOutStatements
<'a
, 'gcx
, 'tcx
> {
728 fn bottom_value() -> bool
{
729 false // bottom = no loans in scope by default
733 impl<'a
, 'gcx
, 'tcx
> InitialFlow
for EverInitializedLvals
<'a
, 'gcx
, 'tcx
> {
735 fn bottom_value() -> bool
{
736 false // bottom = no initialized variables by default