]>
Commit | Line | Data |
---|---|---|
041b39d2 XL |
1 | //! Dataflow analyses are built upon some interpretation of the |
2 | //! bitvectors attached to each basic block, represented via a | |
3 | //! zero-sized structure. | |
4 | ||
e74abb32 XL |
5 | use rustc_index::bit_set::BitSet; |
6 | use rustc_index::vec::Idx; | |
ba9703b0 XL |
7 | use rustc_middle::mir::{self, Body, Location}; |
8 | use rustc_middle::ty::{self, TyCtxt}; | |
3157f602 | 9 | |
c295e0f8 XL |
10 | use crate::drop_flag_effects; |
11 | use crate::drop_flag_effects_for_function_entry; | |
12 | use crate::drop_flag_effects_for_location; | |
13 | use crate::elaborate_drops::DropFlagState; | |
14 | use crate::framework::SwitchIntEdgeEffects; | |
15 | use crate::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex}; | |
16 | use crate::on_lookup_result_bits; | |
17 | use crate::MoveDataParamEnv; | |
18 | use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis}; | |
3157f602 | 19 | |
2c00a5a8 | 20 | mod borrowed_locals; |
f035d41b | 21 | mod init_locals; |
f9f354fc | 22 | mod liveness; |
e74abb32 | 23 | mod storage_liveness; |
2c00a5a8 | 24 | |
f9f354fc | 25 | pub use self::borrowed_locals::{MaybeBorrowedLocals, MaybeMutBorrowedLocals}; |
f035d41b | 26 | pub use self::init_locals::MaybeInitializedLocals; |
f9f354fc XL |
27 | pub use self::liveness::MaybeLiveLocals; |
28 | pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive}; | |
3b2f2976 | 29 | |
2c00a5a8 | 30 | /// `MaybeInitializedPlaces` tracks all places that might be |
3157f602 XL |
31 | /// initialized upon reaching a particular point in the control flow |
32 | /// for a function. | |
33 | /// | |
34 | /// For example, in code like the following, we have corresponding | |
35 | /// dataflow information shown in the right-hand comments. | |
36 | /// | |
37 | /// ```rust | |
38 | /// struct S; | |
39 | /// fn foo(pred: bool) { // maybe-init: | |
40 | /// // {} | |
41 | /// let a = S; let b = S; let c; let d; // {a, b} | |
42 | /// | |
43 | /// if pred { | |
44 | /// drop(a); // { b} | |
45 | /// b = S; // { b} | |
46 | /// | |
47 | /// } else { | |
48 | /// drop(b); // {a} | |
49 | /// d = S; // {a, d} | |
50 | /// | |
51 | /// } // {a, b, d} | |
52 | /// | |
53 | /// c = S; // {a, b, c, d} | |
54 | /// } | |
55 | /// ``` | |
56 | /// | |
2c00a5a8 | 57 | /// To determine whether a place *must* be initialized at a |
3157f602 | 58 | /// particular control-flow point, one can take the set-difference |
2c00a5a8 | 59 | /// between this data and the data from `MaybeUninitializedPlaces` at the |
3157f602 XL |
60 | /// corresponding control-flow point. |
61 | /// | |
62 | /// Similarly, at a given `drop` statement, the set-intersection | |
2c00a5a8 XL |
63 | /// between this data and `MaybeUninitializedPlaces` yields the set of |
64 | /// places that would require a dynamic drop-flag at that statement. | |
dc9dc135 XL |
65 | pub struct MaybeInitializedPlaces<'a, 'tcx> { |
66 | tcx: TyCtxt<'tcx>, | |
67 | body: &'a Body<'tcx>, | |
68 | mdpe: &'a MoveDataParamEnv<'tcx>, | |
3157f602 XL |
69 | } |
70 | ||
dc9dc135 XL |
71 | impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { |
72 | pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { | |
60c5eb7d | 73 | MaybeInitializedPlaces { tcx, body, mdpe } |
3157f602 XL |
74 | } |
75 | } | |
76 | ||
dc9dc135 | 77 | impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> { |
dfeec247 XL |
78 | fn move_data(&self) -> &MoveData<'tcx> { |
79 | &self.mdpe.move_data | |
80 | } | |
32a655c1 SL |
81 | } |
82 | ||
2c00a5a8 | 83 | /// `MaybeUninitializedPlaces` tracks all places that might be |
3157f602 XL |
84 | /// uninitialized upon reaching a particular point in the control flow |
85 | /// for a function. | |
86 | /// | |
87 | /// For example, in code like the following, we have corresponding | |
88 | /// dataflow information shown in the right-hand comments. | |
89 | /// | |
90 | /// ```rust | |
91 | /// struct S; | |
92 | /// fn foo(pred: bool) { // maybe-uninit: | |
93 | /// // {a, b, c, d} | |
94 | /// let a = S; let b = S; let c; let d; // { c, d} | |
95 | /// | |
96 | /// if pred { | |
97 | /// drop(a); // {a, c, d} | |
98 | /// b = S; // {a, c, d} | |
99 | /// | |
100 | /// } else { | |
101 | /// drop(b); // { b, c, d} | |
102 | /// d = S; // { b, c } | |
103 | /// | |
104 | /// } // {a, b, c, d} | |
105 | /// | |
106 | /// c = S; // {a, b, d} | |
107 | /// } | |
108 | /// ``` | |
109 | /// | |
2c00a5a8 | 110 | /// To determine whether a place *must* be uninitialized at a |
3157f602 | 111 | /// particular control-flow point, one can take the set-difference |
2c00a5a8 | 112 | /// between this data and the data from `MaybeInitializedPlaces` at the |
3157f602 XL |
113 | /// corresponding control-flow point. |
114 | /// | |
115 | /// Similarly, at a given `drop` statement, the set-intersection | |
2c00a5a8 XL |
116 | /// between this data and `MaybeInitializedPlaces` yields the set of |
117 | /// places that would require a dynamic drop-flag at that statement. | |
dc9dc135 XL |
118 | pub struct MaybeUninitializedPlaces<'a, 'tcx> { |
119 | tcx: TyCtxt<'tcx>, | |
120 | body: &'a Body<'tcx>, | |
121 | mdpe: &'a MoveDataParamEnv<'tcx>, | |
f035d41b XL |
122 | |
123 | mark_inactive_variants_as_uninit: bool, | |
3157f602 XL |
124 | } |
125 | ||
dc9dc135 XL |
126 | impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { |
127 | pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { | |
f035d41b XL |
128 | MaybeUninitializedPlaces { tcx, body, mdpe, mark_inactive_variants_as_uninit: false } |
129 | } | |
130 | ||
131 | /// Causes inactive enum variants to be marked as "maybe uninitialized" after a switch on an | |
132 | /// enum discriminant. | |
133 | /// | |
134 | /// This is correct in a vacuum but is not the default because it causes problems in the borrow | |
135 | /// checker, where this information gets propagated along `FakeEdge`s. | |
136 | pub fn mark_inactive_variants_as_uninit(mut self) -> Self { | |
137 | self.mark_inactive_variants_as_uninit = true; | |
138 | self | |
3157f602 XL |
139 | } |
140 | } | |
141 | ||
dc9dc135 | 142 | impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> { |
dfeec247 XL |
143 | fn move_data(&self) -> &MoveData<'tcx> { |
144 | &self.mdpe.move_data | |
145 | } | |
32a655c1 SL |
146 | } |
147 | ||
2c00a5a8 | 148 | /// `DefinitelyInitializedPlaces` tracks all places that are definitely |
3157f602 XL |
149 | /// initialized upon reaching a particular point in the control flow |
150 | /// for a function. | |
151 | /// | |
3157f602 XL |
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 | /// | |
2c00a5a8 | 175 | /// To determine whether a place *may* be uninitialized at a |
3157f602 XL |
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 | |
2c00a5a8 | 180 | /// this data and `MaybeInitializedPlaces` yields the set of places |
3157f602 | 181 | /// that would require a dynamic drop-flag at that statement. |
dc9dc135 XL |
182 | pub struct DefinitelyInitializedPlaces<'a, 'tcx> { |
183 | tcx: TyCtxt<'tcx>, | |
184 | body: &'a Body<'tcx>, | |
185 | mdpe: &'a MoveDataParamEnv<'tcx>, | |
3157f602 XL |
186 | } |
187 | ||
dc9dc135 XL |
188 | impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { |
189 | pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { | |
60c5eb7d | 190 | DefinitelyInitializedPlaces { tcx, body, mdpe } |
3157f602 XL |
191 | } |
192 | } | |
193 | ||
dc9dc135 | 194 | impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { |
dfeec247 XL |
195 | fn move_data(&self) -> &MoveData<'tcx> { |
196 | &self.mdpe.move_data | |
197 | } | |
32a655c1 SL |
198 | } |
199 | ||
2c00a5a8 | 200 | /// `EverInitializedPlaces` tracks all places that might have ever been |
ff7c6d11 | 201 | /// initialized upon reaching a particular point in the control flow |
1b1a35ee | 202 | /// for a function, without an intervening `StorageDead`. |
ff7c6d11 XL |
203 | /// |
204 | /// This dataflow is used to determine if an immutable local variable may | |
205 | /// be assigned to. | |
206 | /// | |
207 | /// For example, in code like the following, we have corresponding | |
208 | /// dataflow information shown in the right-hand comments. | |
209 | /// | |
210 | /// ```rust | |
211 | /// struct S; | |
212 | /// fn foo(pred: bool) { // ever-init: | |
213 | /// // { } | |
214 | /// let a = S; let b = S; let c; let d; // {a, b } | |
215 | /// | |
216 | /// if pred { | |
217 | /// drop(a); // {a, b, } | |
218 | /// b = S; // {a, b, } | |
219 | /// | |
220 | /// } else { | |
221 | /// drop(b); // {a, b, } | |
222 | /// d = S; // {a, b, d } | |
223 | /// | |
224 | /// } // {a, b, d } | |
225 | /// | |
226 | /// c = S; // {a, b, c, d } | |
227 | /// } | |
228 | /// ``` | |
dc9dc135 | 229 | pub struct EverInitializedPlaces<'a, 'tcx> { |
74b04a01 | 230 | #[allow(dead_code)] |
dc9dc135 XL |
231 | tcx: TyCtxt<'tcx>, |
232 | body: &'a Body<'tcx>, | |
233 | mdpe: &'a MoveDataParamEnv<'tcx>, | |
ff7c6d11 XL |
234 | } |
235 | ||
dc9dc135 XL |
236 | impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> { |
237 | pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { | |
60c5eb7d | 238 | EverInitializedPlaces { tcx, body, mdpe } |
ff7c6d11 XL |
239 | } |
240 | } | |
241 | ||
dc9dc135 | 242 | impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> { |
dfeec247 XL |
243 | fn move_data(&self) -> &MoveData<'tcx> { |
244 | &self.mdpe.move_data | |
245 | } | |
ff7c6d11 XL |
246 | } |
247 | ||
dc9dc135 | 248 | impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { |
dfeec247 | 249 | fn update_bits( |
74b04a01 | 250 | trans: &mut impl GenKill<MovePathIndex>, |
dfeec247 XL |
251 | path: MovePathIndex, |
252 | state: DropFlagState, | |
253 | ) { | |
3157f602 | 254 | match state { |
dc9dc135 XL |
255 | DropFlagState::Absent => trans.kill(path), |
256 | DropFlagState::Present => trans.gen(path), | |
3157f602 XL |
257 | } |
258 | } | |
259 | } | |
260 | ||
dc9dc135 | 261 | impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { |
dfeec247 | 262 | fn update_bits( |
74b04a01 | 263 | trans: &mut impl GenKill<MovePathIndex>, |
dfeec247 XL |
264 | path: MovePathIndex, |
265 | state: DropFlagState, | |
266 | ) { | |
3157f602 | 267 | match state { |
dc9dc135 XL |
268 | DropFlagState::Absent => trans.gen(path), |
269 | DropFlagState::Present => trans.kill(path), | |
3157f602 XL |
270 | } |
271 | } | |
272 | } | |
273 | ||
dc9dc135 | 274 | impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { |
dfeec247 | 275 | fn update_bits( |
74b04a01 | 276 | trans: &mut impl GenKill<MovePathIndex>, |
dfeec247 XL |
277 | path: MovePathIndex, |
278 | state: DropFlagState, | |
279 | ) { | |
3157f602 | 280 | match state { |
dc9dc135 XL |
281 | DropFlagState::Absent => trans.kill(path), |
282 | DropFlagState::Present => trans.gen(path), | |
3157f602 XL |
283 | } |
284 | } | |
285 | } | |
286 | ||
74b04a01 | 287 | impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { |
1b1a35ee | 288 | type Domain = BitSet<MovePathIndex>; |
74b04a01 XL |
289 | const NAME: &'static str = "maybe_init"; |
290 | ||
1b1a35ee XL |
291 | fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { |
292 | // bottom = uninitialized | |
293 | BitSet::new_empty(self.move_data().move_paths.len()) | |
3157f602 XL |
294 | } |
295 | ||
1b1a35ee | 296 | fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { |
dfeec247 XL |
297 | drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { |
298 | assert!(s == DropFlagState::Present); | |
74b04a01 | 299 | state.insert(path); |
dfeec247 XL |
300 | }); |
301 | } | |
74b04a01 XL |
302 | } |
303 | ||
304 | impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { | |
1b1a35ee XL |
305 | type Idx = MovePathIndex; |
306 | ||
74b04a01 XL |
307 | fn statement_effect( |
308 | &self, | |
309 | trans: &mut impl GenKill<Self::Idx>, | |
310 | _statement: &mir::Statement<'tcx>, | |
311 | location: Location, | |
312 | ) { | |
dfeec247 XL |
313 | drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { |
314 | Self::update_bits(trans, path, s) | |
315 | }) | |
316 | } | |
317 | ||
74b04a01 XL |
318 | fn terminator_effect( |
319 | &self, | |
320 | trans: &mut impl GenKill<Self::Idx>, | |
321 | _terminator: &mir::Terminator<'tcx>, | |
322 | location: Location, | |
323 | ) { | |
dfeec247 XL |
324 | drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { |
325 | Self::update_bits(trans, path, s) | |
326 | }) | |
3157f602 XL |
327 | } |
328 | ||
74b04a01 | 329 | fn call_return_effect( |
0731742a | 330 | &self, |
74b04a01 XL |
331 | trans: &mut impl GenKill<Self::Idx>, |
332 | _block: mir::BasicBlock, | |
333 | _func: &mir::Operand<'tcx>, | |
334 | _args: &[mir::Operand<'tcx>], | |
ba9703b0 | 335 | dest_place: mir::Place<'tcx>, |
0731742a | 336 | ) { |
3157f602 | 337 | // when a call returns successfully, that means we need to set |
ff7c6d11 | 338 | // the bits for that dest_place to 1 (initialized). |
dfeec247 XL |
339 | on_lookup_result_bits( |
340 | self.tcx, | |
341 | self.body, | |
342 | self.move_data(), | |
343 | self.move_data().rev_lookup.find(dest_place.as_ref()), | |
344 | |mpi| { | |
74b04a01 | 345 | trans.gen(mpi); |
dfeec247 XL |
346 | }, |
347 | ); | |
3157f602 | 348 | } |
74b04a01 | 349 | |
1b1a35ee | 350 | fn switch_int_edge_effects<G: GenKill<Self::Idx>>( |
74b04a01 | 351 | &self, |
1b1a35ee XL |
352 | block: mir::BasicBlock, |
353 | discr: &mir::Operand<'tcx>, | |
354 | edge_effects: &mut impl SwitchIntEdgeEffects<G>, | |
74b04a01 | 355 | ) { |
1b1a35ee XL |
356 | if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration { |
357 | return; | |
358 | } | |
359 | ||
360 | let enum_ = discr.place().and_then(|discr| { | |
361 | switch_on_enum_discriminant(self.tcx, &self.body, &self.body[block], discr) | |
362 | }); | |
363 | ||
364 | let (enum_place, enum_def) = match enum_ { | |
365 | Some(x) => x, | |
366 | None => return, | |
367 | }; | |
368 | ||
369 | let mut discriminants = enum_def.discriminants(self.tcx); | |
370 | edge_effects.apply(|trans, edge| { | |
371 | let value = match edge.value { | |
372 | Some(x) => x, | |
373 | None => return, | |
374 | }; | |
375 | ||
376 | // MIR building adds discriminants to the `values` array in the same order as they | |
377 | // are yielded by `AdtDef::discriminants`. We rely on this to match each | |
378 | // discriminant in `values` to its corresponding variant in linear time. | |
379 | let (variant, _) = discriminants | |
380 | .find(|&(_, discr)| discr.val == value) | |
381 | .expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`"); | |
382 | ||
383 | // Kill all move paths that correspond to variants we know to be inactive along this | |
384 | // particular outgoing edge of a `SwitchInt`. | |
385 | drop_flag_effects::on_all_inactive_variants( | |
386 | self.tcx, | |
387 | self.body, | |
388 | self.move_data(), | |
389 | enum_place, | |
390 | variant, | |
391 | |mpi| trans.kill(mpi), | |
392 | ); | |
393 | }); | |
74b04a01 | 394 | } |
3157f602 XL |
395 | } |
396 | ||
74b04a01 | 397 | impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { |
1b1a35ee | 398 | type Domain = BitSet<MovePathIndex>; |
74b04a01 XL |
399 | |
400 | const NAME: &'static str = "maybe_uninit"; | |
401 | ||
1b1a35ee XL |
402 | fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { |
403 | // bottom = initialized (start_block_effect counters this at outset) | |
404 | BitSet::new_empty(self.move_data().move_paths.len()) | |
3157f602 XL |
405 | } |
406 | ||
ff7c6d11 | 407 | // sets on_entry bits for Arg places |
1b1a35ee | 408 | fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { |
3157f602 | 409 | // set all bits to 1 (uninit) before gathering counterevidence |
74b04a01 | 410 | state.insert_all(); |
3157f602 | 411 | |
dfeec247 XL |
412 | drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { |
413 | assert!(s == DropFlagState::Present); | |
74b04a01 | 414 | state.remove(path); |
dfeec247 | 415 | }); |
3157f602 | 416 | } |
74b04a01 XL |
417 | } |
418 | ||
419 | impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { | |
1b1a35ee XL |
420 | type Idx = MovePathIndex; |
421 | ||
74b04a01 XL |
422 | fn statement_effect( |
423 | &self, | |
424 | trans: &mut impl GenKill<Self::Idx>, | |
425 | _statement: &mir::Statement<'tcx>, | |
426 | location: Location, | |
427 | ) { | |
dfeec247 XL |
428 | drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { |
429 | Self::update_bits(trans, path, s) | |
430 | }) | |
3157f602 XL |
431 | } |
432 | ||
74b04a01 XL |
433 | fn terminator_effect( |
434 | &self, | |
435 | trans: &mut impl GenKill<Self::Idx>, | |
436 | _terminator: &mir::Terminator<'tcx>, | |
437 | location: Location, | |
438 | ) { | |
dfeec247 XL |
439 | drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { |
440 | Self::update_bits(trans, path, s) | |
441 | }) | |
3157f602 XL |
442 | } |
443 | ||
74b04a01 | 444 | fn call_return_effect( |
0731742a | 445 | &self, |
74b04a01 XL |
446 | trans: &mut impl GenKill<Self::Idx>, |
447 | _block: mir::BasicBlock, | |
448 | _func: &mir::Operand<'tcx>, | |
449 | _args: &[mir::Operand<'tcx>], | |
ba9703b0 | 450 | dest_place: mir::Place<'tcx>, |
0731742a | 451 | ) { |
3157f602 | 452 | // when a call returns successfully, that means we need to set |
ff7c6d11 | 453 | // the bits for that dest_place to 0 (initialized). |
dfeec247 XL |
454 | on_lookup_result_bits( |
455 | self.tcx, | |
456 | self.body, | |
457 | self.move_data(), | |
458 | self.move_data().rev_lookup.find(dest_place.as_ref()), | |
459 | |mpi| { | |
74b04a01 | 460 | trans.kill(mpi); |
dfeec247 XL |
461 | }, |
462 | ); | |
3157f602 | 463 | } |
f035d41b | 464 | |
1b1a35ee | 465 | fn switch_int_edge_effects<G: GenKill<Self::Idx>>( |
f035d41b | 466 | &self, |
1b1a35ee XL |
467 | block: mir::BasicBlock, |
468 | discr: &mir::Operand<'tcx>, | |
469 | edge_effects: &mut impl SwitchIntEdgeEffects<G>, | |
f035d41b | 470 | ) { |
1b1a35ee XL |
471 | if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration { |
472 | return; | |
473 | } | |
474 | ||
f035d41b XL |
475 | if !self.mark_inactive_variants_as_uninit { |
476 | return; | |
477 | } | |
478 | ||
1b1a35ee XL |
479 | let enum_ = discr.place().and_then(|discr| { |
480 | switch_on_enum_discriminant(self.tcx, &self.body, &self.body[block], discr) | |
481 | }); | |
482 | ||
483 | let (enum_place, enum_def) = match enum_ { | |
484 | Some(x) => x, | |
485 | None => return, | |
486 | }; | |
487 | ||
488 | let mut discriminants = enum_def.discriminants(self.tcx); | |
489 | edge_effects.apply(|trans, edge| { | |
490 | let value = match edge.value { | |
491 | Some(x) => x, | |
492 | None => return, | |
493 | }; | |
494 | ||
495 | // MIR building adds discriminants to the `values` array in the same order as they | |
496 | // are yielded by `AdtDef::discriminants`. We rely on this to match each | |
497 | // discriminant in `values` to its corresponding variant in linear time. | |
498 | let (variant, _) = discriminants | |
499 | .find(|&(_, discr)| discr.val == value) | |
500 | .expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`"); | |
501 | ||
502 | // Mark all move paths that correspond to variants other than this one as maybe | |
503 | // uninitialized (in reality, they are *definitely* uninitialized). | |
504 | drop_flag_effects::on_all_inactive_variants( | |
505 | self.tcx, | |
506 | self.body, | |
507 | self.move_data(), | |
508 | enum_place, | |
509 | variant, | |
510 | |mpi| trans.gen(mpi), | |
511 | ); | |
512 | }); | |
f035d41b | 513 | } |
3157f602 XL |
514 | } |
515 | ||
74b04a01 | 516 | impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { |
1b1a35ee XL |
517 | /// Use set intersection as the join operator. |
518 | type Domain = lattice::Dual<BitSet<MovePathIndex>>; | |
74b04a01 XL |
519 | |
520 | const NAME: &'static str = "definite_init"; | |
521 | ||
1b1a35ee XL |
522 | fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { |
523 | // bottom = initialized (start_block_effect counters this at outset) | |
524 | lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len())) | |
3157f602 XL |
525 | } |
526 | ||
ff7c6d11 | 527 | // sets on_entry bits for Arg places |
1b1a35ee XL |
528 | fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { |
529 | state.0.clear(); | |
3157f602 | 530 | |
dfeec247 XL |
531 | drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { |
532 | assert!(s == DropFlagState::Present); | |
1b1a35ee | 533 | state.0.insert(path); |
dfeec247 | 534 | }); |
3157f602 | 535 | } |
74b04a01 XL |
536 | } |
537 | ||
538 | impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { | |
1b1a35ee XL |
539 | type Idx = MovePathIndex; |
540 | ||
74b04a01 XL |
541 | fn statement_effect( |
542 | &self, | |
543 | trans: &mut impl GenKill<Self::Idx>, | |
544 | _statement: &mir::Statement<'tcx>, | |
545 | location: Location, | |
546 | ) { | |
dfeec247 XL |
547 | drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { |
548 | Self::update_bits(trans, path, s) | |
549 | }) | |
3157f602 XL |
550 | } |
551 | ||
74b04a01 XL |
552 | fn terminator_effect( |
553 | &self, | |
554 | trans: &mut impl GenKill<Self::Idx>, | |
555 | _terminator: &mir::Terminator<'tcx>, | |
556 | location: Location, | |
557 | ) { | |
dfeec247 XL |
558 | drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { |
559 | Self::update_bits(trans, path, s) | |
560 | }) | |
3157f602 XL |
561 | } |
562 | ||
74b04a01 | 563 | fn call_return_effect( |
0731742a | 564 | &self, |
74b04a01 XL |
565 | trans: &mut impl GenKill<Self::Idx>, |
566 | _block: mir::BasicBlock, | |
567 | _func: &mir::Operand<'tcx>, | |
568 | _args: &[mir::Operand<'tcx>], | |
ba9703b0 | 569 | dest_place: mir::Place<'tcx>, |
0731742a | 570 | ) { |
3157f602 | 571 | // when a call returns successfully, that means we need to set |
ff7c6d11 | 572 | // the bits for that dest_place to 1 (initialized). |
dfeec247 XL |
573 | on_lookup_result_bits( |
574 | self.tcx, | |
575 | self.body, | |
576 | self.move_data(), | |
577 | self.move_data().rev_lookup.find(dest_place.as_ref()), | |
578 | |mpi| { | |
74b04a01 | 579 | trans.gen(mpi); |
dfeec247 XL |
580 | }, |
581 | ); | |
3157f602 XL |
582 | } |
583 | } | |
584 | ||
74b04a01 | 585 | impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { |
1b1a35ee | 586 | type Domain = BitSet<InitIndex>; |
74b04a01 XL |
587 | |
588 | const NAME: &'static str = "ever_init"; | |
589 | ||
1b1a35ee XL |
590 | fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { |
591 | // bottom = no initialized variables by default | |
592 | BitSet::new_empty(self.move_data().inits.len()) | |
ff7c6d11 | 593 | } |
abe05a73 | 594 | |
1b1a35ee | 595 | fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { |
74b04a01 XL |
596 | for arg_init in 0..body.arg_count { |
597 | state.insert(InitIndex::new(arg_init)); | |
ff7c6d11 XL |
598 | } |
599 | } | |
74b04a01 | 600 | } |
ff7c6d11 | 601 | |
74b04a01 | 602 | impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { |
1b1a35ee XL |
603 | type Idx = InitIndex; |
604 | ||
c295e0f8 | 605 | #[instrument(skip(self, trans), level = "debug")] |
74b04a01 XL |
606 | fn statement_effect( |
607 | &self, | |
608 | trans: &mut impl GenKill<Self::Idx>, | |
609 | stmt: &mir::Statement<'tcx>, | |
610 | location: Location, | |
611 | ) { | |
612 | let move_data = self.move_data(); | |
ff7c6d11 XL |
613 | let init_path_map = &move_data.init_path_map; |
614 | let init_loc_map = &move_data.init_loc_map; | |
615 | let rev_lookup = &move_data.rev_lookup; | |
616 | ||
c295e0f8 | 617 | debug!("initializes move_indexes {:?}", &init_loc_map[location]); |
74b04a01 | 618 | trans.gen_all(init_loc_map[location].iter().copied()); |
ff7c6d11 | 619 | |
ba9703b0 XL |
620 | if let mir::StatementKind::StorageDead(local) = stmt.kind { |
621 | // End inits for StorageDead, so that an immutable variable can | |
622 | // be reinitialized on the next iteration of the loop. | |
623 | let move_path_index = rev_lookup.find_local(local); | |
c295e0f8 | 624 | debug!("clears the ever initialized status of {:?}", init_path_map[move_path_index]); |
ba9703b0 | 625 | trans.kill_all(init_path_map[move_path_index].iter().copied()); |
ff7c6d11 XL |
626 | } |
627 | } | |
628 | ||
c295e0f8 | 629 | #[instrument(skip(self, trans, _terminator), level = "debug")] |
74b04a01 XL |
630 | fn terminator_effect( |
631 | &self, | |
632 | trans: &mut impl GenKill<Self::Idx>, | |
633 | _terminator: &mir::Terminator<'tcx>, | |
634 | location: Location, | |
635 | ) { | |
dc9dc135 XL |
636 | let (body, move_data) = (self.body, self.move_data()); |
637 | let term = body[location.block].terminator(); | |
ff7c6d11 | 638 | let init_loc_map = &move_data.init_loc_map; |
c295e0f8 XL |
639 | debug!(?term); |
640 | debug!("initializes move_indexes {:?}", init_loc_map[location]); | |
dc9dc135 | 641 | trans.gen_all( |
74b04a01 XL |
642 | init_loc_map[location] |
643 | .iter() | |
644 | .filter(|init_index| { | |
645 | move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly | |
646 | }) | |
647 | .copied(), | |
ff7c6d11 XL |
648 | ); |
649 | } | |
650 | ||
74b04a01 | 651 | fn call_return_effect( |
0731742a | 652 | &self, |
74b04a01 XL |
653 | trans: &mut impl GenKill<Self::Idx>, |
654 | block: mir::BasicBlock, | |
655 | _func: &mir::Operand<'tcx>, | |
656 | _args: &[mir::Operand<'tcx>], | |
ba9703b0 | 657 | _dest_place: mir::Place<'tcx>, |
0731742a | 658 | ) { |
ff7c6d11 | 659 | let move_data = self.move_data(); |
ff7c6d11 XL |
660 | let init_loc_map = &move_data.init_loc_map; |
661 | ||
74b04a01 | 662 | let call_loc = self.body.terminator_loc(block); |
ff7c6d11 | 663 | for init_index in &init_loc_map[call_loc] { |
74b04a01 | 664 | trans.gen(*init_index); |
ff7c6d11 XL |
665 | } |
666 | } | |
667 | } | |
abe05a73 | 668 | |
1b1a35ee XL |
669 | /// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is |
670 | /// an enum discriminant. | |
671 | /// | |
672 | /// We expect such blocks to have a call to `discriminant` as their last statement like so: | |
673 | /// | |
674 | /// ```text | |
675 | /// ... | |
676 | /// _42 = discriminant(_1) | |
677 | /// SwitchInt(_42, ..) | |
678 | /// ``` | |
679 | /// | |
680 | /// If the basic block matches this pattern, this function returns the place corresponding to the | |
681 | /// enum (`_1` in the example above) as well as the `AdtDef` of that enum. | |
682 | fn switch_on_enum_discriminant( | |
683 | tcx: TyCtxt<'tcx>, | |
684 | body: &'mir mir::Body<'tcx>, | |
685 | block: &'mir mir::BasicBlockData<'tcx>, | |
686 | switch_on: mir::Place<'tcx>, | |
687 | ) -> Option<(mir::Place<'tcx>, &'tcx ty::AdtDef)> { | |
688 | match block.statements.last().map(|stmt| &stmt.kind) { | |
689 | Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated)))) | |
690 | if *lhs == switch_on => | |
691 | { | |
692 | match &discriminated.ty(body, tcx).ty.kind() { | |
693 | ty::Adt(def, _) => Some((*discriminated, def)), | |
694 | ||
695 | // `Rvalue::Discriminant` is also used to get the active yield point for a | |
696 | // generator, but we do not need edge-specific effects in that case. This may | |
697 | // change in the future. | |
698 | ty::Generator(..) => None, | |
699 | ||
700 | t => bug!("`discriminant` called on unexpected type {:?}", t), | |
701 | } | |
702 | } | |
ff7c6d11 | 703 | |
1b1a35ee XL |
704 | _ => None, |
705 | } | |
ff7c6d11 | 706 | } |