]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_dataflow/src/framework/direction.rs
New upstream version 1.64.0+dfsg1
[rustc.git] / compiler / rustc_mir_dataflow / src / framework / direction.rs
CommitLineData
f9f354fc 1use rustc_index::bit_set::BitSet;
29967ef6 2use rustc_middle::mir::{self, BasicBlock, Location, SwitchTargets};
1b1a35ee 3use rustc_middle::ty::TyCtxt;
f9f354fc
XL
4use std::ops::RangeInclusive;
5
6use super::visitor::{ResultsVisitable, ResultsVisitor};
a2a8927a
XL
7use super::{
8 Analysis, CallReturnPlaces, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget,
9};
f9f354fc
XL
10
11pub trait Direction {
923072b8 12 const IS_FORWARD: bool;
f9f354fc 13
923072b8 14 const IS_BACKWARD: bool = !Self::IS_FORWARD;
f9f354fc
XL
15
16 /// Applies all effects between the given `EffectIndex`s.
17 ///
18 /// `effects.start()` must precede or equal `effects.end()` in this direction.
a2a8927a 19 fn apply_effects_in_range<'tcx, A>(
f9f354fc 20 analysis: &A,
1b1a35ee 21 state: &mut A::Domain,
f9f354fc
XL
22 block: BasicBlock,
23 block_data: &mir::BasicBlockData<'tcx>,
24 effects: RangeInclusive<EffectIndex>,
25 ) where
26 A: Analysis<'tcx>;
27
a2a8927a 28 fn apply_effects_in_block<'tcx, A>(
f9f354fc 29 analysis: &A,
1b1a35ee 30 state: &mut A::Domain,
f9f354fc
XL
31 block: BasicBlock,
32 block_data: &mir::BasicBlockData<'tcx>,
33 ) where
34 A: Analysis<'tcx>;
35
a2a8927a 36 fn gen_kill_effects_in_block<'tcx, A>(
f9f354fc
XL
37 analysis: &A,
38 trans: &mut GenKillSet<A::Idx>,
39 block: BasicBlock,
40 block_data: &mir::BasicBlockData<'tcx>,
41 ) where
42 A: GenKillAnalysis<'tcx>;
43
a2a8927a 44 fn visit_results_in_block<'mir, 'tcx, F, R>(
f9f354fc
XL
45 state: &mut F,
46 block: BasicBlock,
47 block_data: &'mir mir::BasicBlockData<'tcx>,
48 results: &R,
49 vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
50 ) where
51 R: ResultsVisitable<'tcx, FlowState = F>;
52
a2a8927a 53 fn join_state_into_successors_of<'tcx, A>(
f9f354fc
XL
54 analysis: &A,
55 tcx: TyCtxt<'tcx>,
56 body: &mir::Body<'tcx>,
57 dead_unwinds: Option<&BitSet<BasicBlock>>,
1b1a35ee 58 exit_state: &mut A::Domain,
f9f354fc 59 block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
1b1a35ee 60 propagate: impl FnMut(BasicBlock, &A::Domain),
f9f354fc
XL
61 ) where
62 A: Analysis<'tcx>;
63}
64
65/// Dataflow that runs from the exit of a block (the terminator), to its entry (the first statement).
66pub struct Backward;
67
68impl Direction for Backward {
923072b8 69 const IS_FORWARD: bool = false;
f9f354fc 70
a2a8927a 71 fn apply_effects_in_block<'tcx, A>(
f9f354fc 72 analysis: &A,
1b1a35ee 73 state: &mut A::Domain,
f9f354fc
XL
74 block: BasicBlock,
75 block_data: &mir::BasicBlockData<'tcx>,
76 ) where
77 A: Analysis<'tcx>,
78 {
79 let terminator = block_data.terminator();
80 let location = Location { block, statement_index: block_data.statements.len() };
81 analysis.apply_before_terminator_effect(state, terminator, location);
82 analysis.apply_terminator_effect(state, terminator, location);
83
84 for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
85 let location = Location { block, statement_index };
86 analysis.apply_before_statement_effect(state, statement, location);
87 analysis.apply_statement_effect(state, statement, location);
88 }
89 }
90
a2a8927a 91 fn gen_kill_effects_in_block<'tcx, A>(
f9f354fc
XL
92 analysis: &A,
93 trans: &mut GenKillSet<A::Idx>,
94 block: BasicBlock,
95 block_data: &mir::BasicBlockData<'tcx>,
96 ) where
97 A: GenKillAnalysis<'tcx>,
98 {
99 let terminator = block_data.terminator();
100 let location = Location { block, statement_index: block_data.statements.len() };
101 analysis.before_terminator_effect(trans, terminator, location);
102 analysis.terminator_effect(trans, terminator, location);
103
104 for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
105 let location = Location { block, statement_index };
106 analysis.before_statement_effect(trans, statement, location);
107 analysis.statement_effect(trans, statement, location);
108 }
109 }
110
a2a8927a 111 fn apply_effects_in_range<'tcx, A>(
f9f354fc 112 analysis: &A,
1b1a35ee 113 state: &mut A::Domain,
f9f354fc
XL
114 block: BasicBlock,
115 block_data: &mir::BasicBlockData<'tcx>,
116 effects: RangeInclusive<EffectIndex>,
117 ) where
118 A: Analysis<'tcx>,
119 {
120 let (from, to) = (*effects.start(), *effects.end());
121 let terminator_index = block_data.statements.len();
122
123 assert!(from.statement_index <= terminator_index);
124 assert!(!to.precedes_in_backward_order(from));
125
126 // Handle the statement (or terminator) at `from`.
127
128 let next_effect = match from.effect {
129 // If we need to apply the terminator effect in all or in part, do so now.
130 _ if from.statement_index == terminator_index => {
131 let location = Location { block, statement_index: from.statement_index };
132 let terminator = block_data.terminator();
133
134 if from.effect == Effect::Before {
135 analysis.apply_before_terminator_effect(state, terminator, location);
136 if to == Effect::Before.at_index(terminator_index) {
137 return;
138 }
139 }
140
141 analysis.apply_terminator_effect(state, terminator, location);
142 if to == Effect::Primary.at_index(terminator_index) {
143 return;
144 }
145
146 // If `from.statement_index` is `0`, we will have hit one of the earlier comparisons
147 // with `to`.
148 from.statement_index - 1
149 }
150
151 Effect::Primary => {
152 let location = Location { block, statement_index: from.statement_index };
153 let statement = &block_data.statements[from.statement_index];
154
155 analysis.apply_statement_effect(state, statement, location);
156 if to == Effect::Primary.at_index(from.statement_index) {
157 return;
158 }
159
160 from.statement_index - 1
161 }
162
163 Effect::Before => from.statement_index,
164 };
165
166 // Handle all statements between `first_unapplied_idx` and `to.statement_index`.
167
168 for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) {
169 let location = Location { block, statement_index };
170 let statement = &block_data.statements[statement_index];
171 analysis.apply_before_statement_effect(state, statement, location);
172 analysis.apply_statement_effect(state, statement, location);
173 }
174
175 // Handle the statement at `to`.
176
177 let location = Location { block, statement_index: to.statement_index };
178 let statement = &block_data.statements[to.statement_index];
179 analysis.apply_before_statement_effect(state, statement, location);
180
181 if to.effect == Effect::Before {
182 return;
183 }
184
185 analysis.apply_statement_effect(state, statement, location);
186 }
187
a2a8927a 188 fn visit_results_in_block<'mir, 'tcx, F, R>(
f9f354fc
XL
189 state: &mut F,
190 block: BasicBlock,
191 block_data: &'mir mir::BasicBlockData<'tcx>,
192 results: &R,
193 vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
194 ) where
195 R: ResultsVisitable<'tcx, FlowState = F>,
196 {
197 results.reset_to_block_entry(state, block);
198
199 vis.visit_block_end(&state, block_data, block);
200
201 // Terminator
202 let loc = Location { block, statement_index: block_data.statements.len() };
203 let term = block_data.terminator();
204 results.reconstruct_before_terminator_effect(state, term, loc);
205 vis.visit_terminator_before_primary_effect(state, term, loc);
206 results.reconstruct_terminator_effect(state, term, loc);
207 vis.visit_terminator_after_primary_effect(state, term, loc);
208
209 for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
210 let loc = Location { block, statement_index };
211 results.reconstruct_before_statement_effect(state, stmt, loc);
212 vis.visit_statement_before_primary_effect(state, stmt, loc);
213 results.reconstruct_statement_effect(state, stmt, loc);
214 vis.visit_statement_after_primary_effect(state, stmt, loc);
215 }
216
217 vis.visit_block_start(state, block_data, block);
218 }
219
a2a8927a 220 fn join_state_into_successors_of<'tcx, A>(
f9f354fc
XL
221 analysis: &A,
222 _tcx: TyCtxt<'tcx>,
223 body: &mir::Body<'tcx>,
224 dead_unwinds: Option<&BitSet<BasicBlock>>,
1b1a35ee 225 exit_state: &mut A::Domain,
f9f354fc 226 (bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
1b1a35ee 227 mut propagate: impl FnMut(BasicBlock, &A::Domain),
f9f354fc
XL
228 ) where
229 A: Analysis<'tcx>,
230 {
064997fb 231 for pred in body.basic_blocks.predecessors()[bb].iter().copied() {
f9f354fc
XL
232 match body[pred].terminator().kind {
233 // Apply terminator-specific edge effects.
234 //
235 // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
923072b8 236 mir::TerminatorKind::Call { destination, target: Some(dest), .. } if dest == bb => {
a2a8927a
XL
237 let mut tmp = exit_state.clone();
238 analysis.apply_call_return_effect(
239 &mut tmp,
240 pred,
923072b8 241 CallReturnPlaces::Call(destination),
a2a8927a
XL
242 );
243 propagate(pred, &tmp);
244 }
5e7ed085 245
a2a8927a
XL
246 mir::TerminatorKind::InlineAsm {
247 destination: Some(dest), ref operands, ..
f9f354fc
XL
248 } if dest == bb => {
249 let mut tmp = exit_state.clone();
a2a8927a
XL
250 analysis.apply_call_return_effect(
251 &mut tmp,
252 pred,
253 CallReturnPlaces::InlineAsm(operands),
254 );
f9f354fc
XL
255 propagate(pred, &tmp);
256 }
257
258 mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => {
259 let mut tmp = exit_state.clone();
260 analysis.apply_yield_resume_effect(&mut tmp, resume, resume_arg);
261 propagate(pred, &tmp);
262 }
263
5e7ed085
FG
264 mir::TerminatorKind::SwitchInt { targets: _, ref discr, switch_ty: _ } => {
265 let mut applier = BackwardSwitchIntEdgeEffectsApplier {
04454e1e 266 body,
5e7ed085
FG
267 pred,
268 exit_state,
5e7ed085
FG
269 bb,
270 propagate: &mut propagate,
271 effects_applied: false,
272 };
273
274 analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
275
276 if !applier.effects_applied {
277 propagate(pred, exit_state)
278 }
279 }
280
f9f354fc
XL
281 // Ignore dead unwinds.
282 mir::TerminatorKind::Call { cleanup: Some(unwind), .. }
283 | mir::TerminatorKind::Assert { cleanup: Some(unwind), .. }
284 | mir::TerminatorKind::Drop { unwind: Some(unwind), .. }
285 | mir::TerminatorKind::DropAndReplace { unwind: Some(unwind), .. }
286 | mir::TerminatorKind::FalseUnwind { unwind: Some(unwind), .. }
a2a8927a 287 | mir::TerminatorKind::InlineAsm { cleanup: Some(unwind), .. }
f9f354fc
XL
288 if unwind == bb =>
289 {
290 if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
291 propagate(pred, exit_state);
292 }
293 }
294
295 _ => propagate(pred, exit_state),
296 }
297 }
298 }
299}
300
923072b8
FG
301struct BackwardSwitchIntEdgeEffectsApplier<'a, 'tcx, D, F> {
302 body: &'a mir::Body<'tcx>,
5e7ed085
FG
303 pred: BasicBlock,
304 exit_state: &'a mut D,
5e7ed085
FG
305 bb: BasicBlock,
306 propagate: &'a mut F,
307
308 effects_applied: bool,
309}
310
923072b8 311impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, '_, D, F>
5e7ed085
FG
312where
313 D: Clone,
314 F: FnMut(BasicBlock, &D),
315{
316 fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
317 assert!(!self.effects_applied);
318
064997fb 319 let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)];
04454e1e 320 let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.bb });
5e7ed085
FG
321
322 let mut tmp = None;
323 for target in targets {
324 let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
325 apply_edge_effect(tmp, target);
326 (self.propagate)(self.pred, tmp);
327 }
328
329 self.effects_applied = true;
330 }
331}
332
f9f354fc
XL
333/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
334pub struct Forward;
335
336impl Direction for Forward {
923072b8 337 const IS_FORWARD: bool = true;
f9f354fc 338
a2a8927a 339 fn apply_effects_in_block<'tcx, A>(
f9f354fc 340 analysis: &A,
1b1a35ee 341 state: &mut A::Domain,
f9f354fc
XL
342 block: BasicBlock,
343 block_data: &mir::BasicBlockData<'tcx>,
344 ) where
345 A: Analysis<'tcx>,
346 {
347 for (statement_index, statement) in block_data.statements.iter().enumerate() {
348 let location = Location { block, statement_index };
349 analysis.apply_before_statement_effect(state, statement, location);
350 analysis.apply_statement_effect(state, statement, location);
351 }
352
353 let terminator = block_data.terminator();
354 let location = Location { block, statement_index: block_data.statements.len() };
355 analysis.apply_before_terminator_effect(state, terminator, location);
356 analysis.apply_terminator_effect(state, terminator, location);
357 }
358
a2a8927a 359 fn gen_kill_effects_in_block<'tcx, A>(
f9f354fc
XL
360 analysis: &A,
361 trans: &mut GenKillSet<A::Idx>,
362 block: BasicBlock,
363 block_data: &mir::BasicBlockData<'tcx>,
364 ) where
365 A: GenKillAnalysis<'tcx>,
366 {
367 for (statement_index, statement) in block_data.statements.iter().enumerate() {
368 let location = Location { block, statement_index };
369 analysis.before_statement_effect(trans, statement, location);
370 analysis.statement_effect(trans, statement, location);
371 }
372
373 let terminator = block_data.terminator();
374 let location = Location { block, statement_index: block_data.statements.len() };
375 analysis.before_terminator_effect(trans, terminator, location);
376 analysis.terminator_effect(trans, terminator, location);
377 }
378
a2a8927a 379 fn apply_effects_in_range<'tcx, A>(
f9f354fc 380 analysis: &A,
1b1a35ee 381 state: &mut A::Domain,
f9f354fc
XL
382 block: BasicBlock,
383 block_data: &mir::BasicBlockData<'tcx>,
384 effects: RangeInclusive<EffectIndex>,
385 ) where
386 A: Analysis<'tcx>,
387 {
388 let (from, to) = (*effects.start(), *effects.end());
389 let terminator_index = block_data.statements.len();
390
391 assert!(to.statement_index <= terminator_index);
392 assert!(!to.precedes_in_forward_order(from));
393
394 // If we have applied the before affect of the statement or terminator at `from` but not its
395 // after effect, do so now and start the loop below from the next statement.
396
397 let first_unapplied_index = match from.effect {
398 Effect::Before => from.statement_index,
399
400 Effect::Primary if from.statement_index == terminator_index => {
401 debug_assert_eq!(from, to);
402
403 let location = Location { block, statement_index: terminator_index };
404 let terminator = block_data.terminator();
405 analysis.apply_terminator_effect(state, terminator, location);
406 return;
407 }
408
409 Effect::Primary => {
410 let location = Location { block, statement_index: from.statement_index };
411 let statement = &block_data.statements[from.statement_index];
412 analysis.apply_statement_effect(state, statement, location);
413
414 // If we only needed to apply the after effect of the statement at `idx`, we are done.
415 if from == to {
416 return;
417 }
418
419 from.statement_index + 1
420 }
421 };
422
423 // Handle all statements between `from` and `to` whose effects must be applied in full.
424
425 for statement_index in first_unapplied_index..to.statement_index {
426 let location = Location { block, statement_index };
427 let statement = &block_data.statements[statement_index];
428 analysis.apply_before_statement_effect(state, statement, location);
429 analysis.apply_statement_effect(state, statement, location);
430 }
431
432 // Handle the statement or terminator at `to`.
433
434 let location = Location { block, statement_index: to.statement_index };
435 if to.statement_index == terminator_index {
436 let terminator = block_data.terminator();
437 analysis.apply_before_terminator_effect(state, terminator, location);
438
439 if to.effect == Effect::Primary {
440 analysis.apply_terminator_effect(state, terminator, location);
441 }
442 } else {
443 let statement = &block_data.statements[to.statement_index];
444 analysis.apply_before_statement_effect(state, statement, location);
445
446 if to.effect == Effect::Primary {
447 analysis.apply_statement_effect(state, statement, location);
448 }
449 }
450 }
451
a2a8927a 452 fn visit_results_in_block<'mir, 'tcx, F, R>(
f9f354fc
XL
453 state: &mut F,
454 block: BasicBlock,
455 block_data: &'mir mir::BasicBlockData<'tcx>,
456 results: &R,
457 vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
458 ) where
459 R: ResultsVisitable<'tcx, FlowState = F>,
460 {
461 results.reset_to_block_entry(state, block);
462
463 vis.visit_block_start(state, block_data, block);
464
465 for (statement_index, stmt) in block_data.statements.iter().enumerate() {
466 let loc = Location { block, statement_index };
467 results.reconstruct_before_statement_effect(state, stmt, loc);
468 vis.visit_statement_before_primary_effect(state, stmt, loc);
469 results.reconstruct_statement_effect(state, stmt, loc);
470 vis.visit_statement_after_primary_effect(state, stmt, loc);
471 }
472
473 let loc = Location { block, statement_index: block_data.statements.len() };
474 let term = block_data.terminator();
475 results.reconstruct_before_terminator_effect(state, term, loc);
476 vis.visit_terminator_before_primary_effect(state, term, loc);
477 results.reconstruct_terminator_effect(state, term, loc);
478 vis.visit_terminator_after_primary_effect(state, term, loc);
479
480 vis.visit_block_end(state, block_data, block);
481 }
482
a2a8927a 483 fn join_state_into_successors_of<'tcx, A>(
f9f354fc 484 analysis: &A,
1b1a35ee
XL
485 _tcx: TyCtxt<'tcx>,
486 _body: &mir::Body<'tcx>,
f9f354fc 487 dead_unwinds: Option<&BitSet<BasicBlock>>,
1b1a35ee 488 exit_state: &mut A::Domain,
f9f354fc 489 (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
1b1a35ee 490 mut propagate: impl FnMut(BasicBlock, &A::Domain),
f9f354fc
XL
491 ) where
492 A: Analysis<'tcx>,
493 {
494 use mir::TerminatorKind::*;
495 match bb_data.terminator().kind {
496 Return | Resume | Abort | GeneratorDrop | Unreachable => {}
497
498 Goto { target } => propagate(target, exit_state),
499
500 Assert { target, cleanup: unwind, expected: _, msg: _, cond: _ }
f035d41b
XL
501 | Drop { target, unwind, place: _ }
502 | DropAndReplace { target, unwind, value: _, place: _ }
f9f354fc
XL
503 | FalseUnwind { real_target: target, unwind } => {
504 if let Some(unwind) = unwind {
505 if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
506 propagate(unwind, exit_state);
507 }
508 }
509
510 propagate(target, exit_state);
511 }
512
f035d41b 513 FalseEdge { real_target, imaginary_target } => {
f9f354fc
XL
514 propagate(real_target, exit_state);
515 propagate(imaginary_target, exit_state);
516 }
517
518 Yield { resume: target, drop, resume_arg, value: _ } => {
519 if let Some(drop) = drop {
520 propagate(drop, exit_state);
521 }
522
523 analysis.apply_yield_resume_effect(exit_state, target, resume_arg);
524 propagate(target, exit_state);
525 }
526
923072b8
FG
527 Call {
528 cleanup,
529 destination,
530 target,
531 func: _,
532 args: _,
533 from_hir_call: _,
534 fn_span: _,
535 } => {
f9f354fc
XL
536 if let Some(unwind) = cleanup {
537 if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
538 propagate(unwind, exit_state);
539 }
540 }
541
923072b8 542 if let Some(target) = target {
f9f354fc
XL
543 // N.B.: This must be done *last*, otherwise the unwind path will see the call
544 // return effect.
a2a8927a
XL
545 analysis.apply_call_return_effect(
546 exit_state,
547 bb,
923072b8 548 CallReturnPlaces::Call(destination),
a2a8927a 549 );
f9f354fc
XL
550 propagate(target, exit_state);
551 }
552 }
553
a2a8927a
XL
554 InlineAsm {
555 template: _,
556 ref operands,
557 options: _,
558 line_spans: _,
559 destination,
560 cleanup,
561 } => {
562 if let Some(unwind) = cleanup {
563 if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
564 propagate(unwind, exit_state);
565 }
566 }
567
f9f354fc 568 if let Some(target) = destination {
a2a8927a
XL
569 // N.B.: This must be done *last*, otherwise the unwind path will see the call
570 // return effect.
571 analysis.apply_call_return_effect(
572 exit_state,
573 bb,
574 CallReturnPlaces::InlineAsm(operands),
575 );
f9f354fc
XL
576 propagate(target, exit_state);
577 }
578 }
579
29967ef6 580 SwitchInt { ref targets, ref discr, switch_ty: _ } => {
5e7ed085 581 let mut applier = ForwardSwitchIntEdgeEffectsApplier {
1b1a35ee 582 exit_state,
29967ef6 583 targets,
1b1a35ee
XL
584 propagate,
585 effects_applied: false,
586 };
587
588 analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
589
5e7ed085
FG
590 let ForwardSwitchIntEdgeEffectsApplier {
591 exit_state,
592 mut propagate,
593 effects_applied,
594 ..
1b1a35ee
XL
595 } = applier;
596
597 if !effects_applied {
29967ef6
XL
598 for target in targets.all_targets() {
599 propagate(*target, exit_state);
f9f354fc
XL
600 }
601 }
602 }
603 }
604 }
605}
606
5e7ed085 607struct ForwardSwitchIntEdgeEffectsApplier<'a, D, F> {
1b1a35ee 608 exit_state: &'a mut D,
29967ef6 609 targets: &'a SwitchTargets,
1b1a35ee
XL
610 propagate: F,
611
612 effects_applied: bool,
613}
614
5e7ed085 615impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
1b1a35ee
XL
616where
617 D: Clone,
618 F: FnMut(BasicBlock, &D),
619{
620 fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
621 assert!(!self.effects_applied);
622
623 let mut tmp = None;
29967ef6 624 for (value, target) in self.targets.iter() {
1b1a35ee
XL
625 let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
626 apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target });
627 (self.propagate)(target, tmp);
f9f354fc
XL
628 }
629
1b1a35ee
XL
630 // Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`,
631 // so pass it directly to `apply_edge_effect` to save a clone of the dataflow state.
29967ef6 632 let otherwise = self.targets.otherwise();
1b1a35ee
XL
633 apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise });
634 (self.propagate)(otherwise, self.exit_state);
635
636 self.effects_applied = true;
637 }
638}
639
640/// An analogue of `Option::get_or_insert_with` that stores a clone of `val` into `opt`, but uses
641/// the more efficient `clone_from` if `opt` was `Some`.
642///
643/// Returns a mutable reference to the new clone that resides in `opt`.
644//
645// FIXME: Figure out how to express this using `Option::clone_from`, or maybe lift it into the
646// standard library?
a2a8927a 647fn opt_clone_from_or_clone<'a, T: Clone>(opt: &'a mut Option<T>, val: &T) -> &'a mut T {
1b1a35ee
XL
648 if opt.is_some() {
649 let ret = opt.as_mut().unwrap();
650 ret.clone_from(val);
651 ret
652 } else {
653 *opt = Some(val.clone());
654 opt.as_mut().unwrap()
f9f354fc
XL
655 }
656}