]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_dataflow/src/impls/mod.rs
New upstream version 1.57.0+dfsg1
[rustc.git] / compiler / rustc_mir_dataflow / src / impls / mod.rs
CommitLineData
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
5use rustc_index::bit_set::BitSet;
6use rustc_index::vec::Idx;
ba9703b0
XL
7use rustc_middle::mir::{self, Body, Location};
8use rustc_middle::ty::{self, TyCtxt};
3157f602 9
c295e0f8
XL
10use crate::drop_flag_effects;
11use crate::drop_flag_effects_for_function_entry;
12use crate::drop_flag_effects_for_location;
13use crate::elaborate_drops::DropFlagState;
14use crate::framework::SwitchIntEdgeEffects;
15use crate::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex};
16use crate::on_lookup_result_bits;
17use crate::MoveDataParamEnv;
18use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis};
3157f602 19
2c00a5a8 20mod borrowed_locals;
f035d41b 21mod init_locals;
f9f354fc 22mod liveness;
e74abb32 23mod storage_liveness;
2c00a5a8 24
f9f354fc 25pub use self::borrowed_locals::{MaybeBorrowedLocals, MaybeMutBorrowedLocals};
f035d41b 26pub use self::init_locals::MaybeInitializedLocals;
f9f354fc
XL
27pub use self::liveness::MaybeLiveLocals;
28pub 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
65pub struct MaybeInitializedPlaces<'a, 'tcx> {
66 tcx: TyCtxt<'tcx>,
67 body: &'a Body<'tcx>,
68 mdpe: &'a MoveDataParamEnv<'tcx>,
3157f602
XL
69}
70
dc9dc135
XL
71impl<'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 77impl<'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
118pub 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
126impl<'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 142impl<'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
182pub struct DefinitelyInitializedPlaces<'a, 'tcx> {
183 tcx: TyCtxt<'tcx>,
184 body: &'a Body<'tcx>,
185 mdpe: &'a MoveDataParamEnv<'tcx>,
3157f602
XL
186}
187
dc9dc135
XL
188impl<'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 194impl<'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 229pub 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
236impl<'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 242impl<'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 248impl<'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 261impl<'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 274impl<'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 287impl<'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
304impl<'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 397impl<'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
419impl<'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 516impl<'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
538impl<'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 585impl<'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 602impl<'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.
682fn 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}