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 use rustc
::ty
::TyCtxt
;
12 use rustc
::mir
::repr
::{self, Mir, Location}
;
13 use rustc_data_structures
::indexed_vec
::Idx
;
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
;
22 use super::{BitDenotation, BlockSets, DataflowOperator}
;
24 use bitslice
::BitSlice
; // adds set_bit/get_bit to &[usize] bitvector rep.
25 use bitslice
::{BitwiseOperator}
;
26 use indexed_set
::{IdxSet}
;
28 // Dataflow analyses are built upon some interpretation of the
29 // bitvectors attached to each basic block, represented via a
30 // zero-sized structure.
32 /// `MaybeInitializedLvals` tracks all l-values that might be
33 /// initialized upon reaching a particular point in the control flow
36 /// For example, in code like the following, we have corresponding
37 /// dataflow information shown in the right-hand comments.
41 /// fn foo(pred: bool) { // maybe-init:
43 /// let a = S; let b = S; let c; let d; // {a, b}
55 /// c = S; // {a, b, c, d}
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.
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
>,
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 }
78 /// `MaybeUninitializedLvals` tracks all l-values that might be
79 /// uninitialized upon reaching a particular point in the control flow
82 /// For example, in code like the following, we have corresponding
83 /// dataflow information shown in the right-hand comments.
87 /// fn foo(pred: bool) { // maybe-uninit:
89 /// let a = S; let b = S; let c; let d; // { c, d}
92 /// drop(a); // {a, c, d}
93 /// b = S; // {a, c, d}
96 /// drop(b); // { b, c, d}
97 /// d = S; // { b, c }
101 /// c = S; // {a, b, d}
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.
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
>,
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 }
124 /// `DefinitelyInitializedLvals` tracks all l-values that are definitely
125 /// initialized upon reaching a particular point in the control flow
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
135 /// For example, in code like the following, we have corresponding
136 /// dataflow information shown in the right-hand comments.
140 /// fn foo(pred: bool) { // definite-init:
142 /// let a = S; let b = S; let c; let d; // {a, b }
145 /// drop(a); // { b, }
149 /// drop(b); // {a, }
158 /// To determine whether an l-value *may* be uninitialized at a
159 /// particular control-flow point, one can take the set-complement
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
>,
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 }
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.
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.
190 pub struct MovingOutStatements
<'a
, 'tcx
: 'a
> {
191 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
195 impl<'a
, 'tcx
> MaybeInitializedLvals
<'a
, 'tcx
> {
196 fn update_bits(sets
: &mut BlockSets
<MovePathIndex
>, path
: MovePathIndex
,
197 state
: DropFlagState
)
200 DropFlagState
::Absent
=> sets
.kill(&path
),
201 DropFlagState
::Present
=> sets
.gen(&path
),
206 impl<'a
, 'tcx
> MaybeUninitializedLvals
<'a
, 'tcx
> {
207 fn update_bits(sets
: &mut BlockSets
<MovePathIndex
>, path
: MovePathIndex
,
208 state
: DropFlagState
)
211 DropFlagState
::Absent
=> sets
.gen(&path
),
212 DropFlagState
::Present
=> sets
.kill(&path
),
217 impl<'a
, 'tcx
> DefinitelyInitializedLvals
<'a
, 'tcx
> {
218 fn update_bits(sets
: &mut BlockSets
<MovePathIndex
>, path
: MovePathIndex
,
219 state
: DropFlagState
)
222 DropFlagState
::Absent
=> sets
.kill(&path
),
223 DropFlagState
::Present
=> sets
.gen(&path
),
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()
236 fn start_block_effect(&self, ctxt
: &Self::Ctxt
, sets
: &mut BlockSets
<MovePathIndex
>)
238 drop_flag_effects_for_function_entry(
239 self.tcx
, self.mir
, ctxt
,
241 assert
!(s
== DropFlagState
::Present
);
242 sets
.on_entry
.add(&path
);
246 fn statement_effect(&self,
248 sets
: &mut BlockSets
<MovePathIndex
>,
249 bb
: repr
::BasicBlock
,
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
)
259 fn terminator_effect(&self,
261 sets
: &mut BlockSets
<MovePathIndex
>,
262 bb
: repr
::BasicBlock
,
263 statements_len
: usize)
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
)
272 fn propagate_call_return(&self,
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); }
);
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()
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; }
299 drop_flag_effects_for_function_entry(
300 self.tcx
, self.mir
, ctxt
,
302 assert
!(s
== DropFlagState
::Present
);
303 sets
.on_entry
.remove(&path
);
307 fn statement_effect(&self,
309 sets
: &mut BlockSets
<MovePathIndex
>,
310 bb
: repr
::BasicBlock
,
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
)
320 fn terminator_effect(&self,
322 sets
: &mut BlockSets
<MovePathIndex
>,
323 bb
: repr
::BasicBlock
,
324 statements_len
: usize)
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
)
333 fn propagate_call_return(&self,
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); }
);
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()
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; }
359 drop_flag_effects_for_function_entry(
360 self.tcx
, self.mir
, ctxt
,
362 assert
!(s
== DropFlagState
::Present
);
363 sets
.on_entry
.add(&path
);
367 fn statement_effect(&self,
369 sets
: &mut BlockSets
<MovePathIndex
>,
370 bb
: repr
::BasicBlock
,
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
)
380 fn terminator_effect(&self,
382 sets
: &mut BlockSets
<MovePathIndex
>,
383 bb
: repr
::BasicBlock
,
384 statements_len
: usize)
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
)
393 fn propagate_call_return(&self,
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); }
);
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()
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`.
419 fn statement_effect(&self,
421 sets
: &mut BlockSets
<MoveOutIndex
>,
422 bb
: repr
::BasicBlock
,
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
;
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
);
439 let bits_per_block
= self.bits_per_block(ctxt
);
441 repr
::StatementKind
::SetDiscriminant { .. }
=> {
442 span_bug
!(stmt
.source_info
.span
, "SetDiscriminant should not exist in borrowck");
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
,
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
);
457 repr
::StatementKind
::StorageLive(_
) |
458 repr
::StatementKind
::StorageDead(_
) |
459 repr
::StatementKind
::Nop
=> {}
463 fn terminator_effect(&self,
465 sets
: &mut BlockSets
<MoveOutIndex
>,
466 bb
: repr
::BasicBlock
,
467 statements_len
: usize)
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
);
482 fn propagate_call_return(&self,
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
);
491 let path_map
= &move_data
.path_map
;
492 on_lookup_result_bits(self.tcx
,
495 move_data
.rev_lookup
.find(dest_lval
),
496 |mpi
| for moi
in &path_map
[mpi
] {
497 assert
!(moi
.index() < bits_per_block
);
503 fn zero_to_one(bitvec
: &mut [usize], move_index
: MoveOutIndex
) {
504 let retval
= bitvec
.set_bit(move_index
.index());
508 impl<'a
, 'tcx
> BitwiseOperator
for MovingOutStatements
<'a
, 'tcx
> {
510 fn join(&self, pred1
: usize, pred2
: usize) -> usize {
511 pred1
| pred2
// moves from both preds are in scope
515 impl<'a
, 'tcx
> BitwiseOperator
for MaybeInitializedLvals
<'a
, 'tcx
> {
517 fn join(&self, pred1
: usize, pred2
: usize) -> usize {
518 pred1
| pred2
// "maybe" means we union effects of both preds
522 impl<'a
, 'tcx
> BitwiseOperator
for MaybeUninitializedLvals
<'a
, 'tcx
> {
524 fn join(&self, pred1
: usize, pred2
: usize) -> usize {
525 pred1
| pred2
// "maybe" means we union effects of both preds
529 impl<'a
, 'tcx
> BitwiseOperator
for DefinitelyInitializedLvals
<'a
, 'tcx
> {
531 fn join(&self, pred1
: usize, pred2
: usize) -> usize {
532 pred1
& pred2
// "definitely" means we intersect effects of both preds
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).
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.
546 impl<'a
, 'tcx
> DataflowOperator
for MovingOutStatements
<'a
, 'tcx
> {
548 fn bottom_value() -> bool
{
549 false // bottom = no loans in scope by default
553 impl<'a
, 'tcx
> DataflowOperator
for MaybeInitializedLvals
<'a
, 'tcx
> {
555 fn bottom_value() -> bool
{
556 false // bottom = uninitialized
560 impl<'a
, 'tcx
> DataflowOperator
for MaybeUninitializedLvals
<'a
, 'tcx
> {
562 fn bottom_value() -> bool
{
563 false // bottom = initialized (start_block_effect counters this at outset)
567 impl<'a
, 'tcx
> DataflowOperator
for DefinitelyInitializedLvals
<'a
, 'tcx
> {
569 fn bottom_value() -> bool
{
570 true // bottom = initialized (start_block_effect counters this at outset)