join_block.unit()
}
ExprKind::Loop { condition: opt_cond_expr, body } => {
- // [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
- // ^ |
- // | 0
- // | |
- // | v
- // [body_block_end] <~~~ [body_block]
+ // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block]
+ // ^ |
+ // | 0
+ // | |
+ // | v
+ // [body_block_end] <-/eval. body/-- [body_block]
//
// If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
//
- // [block] --> [loop_block] ~~> [loop_block_end]
- // | ^ |
- // false link | |
- // | +-------------------+
- // v
- // [cleanup_block]
- //
- // The false link is required in case something results in
- // unwinding through the body.
+ // [block]
+ // |
+ // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
+ // | ^ |
+ // false link | |
+ // | +-----------------------------------------+
+ // +-> [diverge_cleanup]
+ // The false link is required to make sure borrowck considers unwinds through the
+ // body, even when the exact code in the body cannot unwind
let loop_block = this.cfg.start_new_block();
let exit_block = this.cfg.start_new_block();
move |this| {
// conduct the test, if necessary
let body_block;
- let out_terminator;
if let Some(cond_expr) = opt_cond_expr {
let loop_block_end;
let cond = unpack!(
// we have to do it; this overwrites any `break`-assigned value but it's
// always `()` anyway
this.cfg.push_assign_unit(exit_block, source_info, destination);
-
- out_terminator = TerminatorKind::Goto { target: loop_block };
} else {
- body_block = loop_block;
+ body_block = this.cfg.start_new_block();
let diverge_cleanup = this.diverge_cleanup();
- out_terminator = TerminatorKind::FalseUnwind {
- real_target: loop_block,
- unwind: Some(diverge_cleanup)
- }
+ this.cfg.terminate(loop_block, source_info,
+ TerminatorKind::FalseUnwind {
+ real_target: body_block,
+ unwind: Some(diverge_cleanup)
+ })
}
// The “return” value of the loop body must always be an unit. We therefore
// Execute the body, branching back to the test.
let body_block_end = unpack!(this.into(&tmp, body_block, body));
this.cfg.terminate(body_block_end, source_info,
- out_terminator);
+ TerminatorKind::Goto { target: loop_block });
}
);
exit_block.unit()
// goto -> bb1;
// }
// bb1: {
+// falseUnwind -> [real: bb2, cleanup: bb3];
+// }
+// bb2: {
// StorageLive(_2);
// _2 = const true;
// StorageLive(_3);
// _3 = &'23_1rs _2;
// StorageLive(_5);
// _5 = _2;
-// switchInt(move _5) -> [0u8: bb4, otherwise: bb3];
+// switchInt(move _5) -> [0u8: bb5, otherwise: bb4];
// }
-// bb2: {
+// bb3: {
// ...
// }
-// bb3: {
+// bb4: {
// _0 = ();
// StorageDead(_5);
// EndRegion('23_1rs);
// StorageDead(_2);
// return;
// }
-// bb4: {
+// bb5: {
// _4 = ();
// StorageDead(_5);
// StorageLive(_7);
// EndRegion('23_1rs);
// StorageDead(_3);
// StorageDead(_2);
-// falseUnwind -> [real: bb1, cleanup: bb2];
+// goto -> bb1;
// }
// END rustc.main.SimplifyCfg-qualify-consts.after.mir
// goto -> bb1;
// }
// bb1: {
+// falseUnwind -> [real: bb2, cleanup: bb3];
+// }
+// bb2: {
// _1 = const true;
// StorageLive(_3);
// _3 = &'26_1rs _1;
// StorageLive(_5);
// _5 = _1;
-// switchInt(move _5) -> [0u8: bb4, otherwise: bb3];
+// switchInt(move _5) -> [0u8: bb5, otherwise: bb4];
// }
-// bb2: {
+// bb3: {
// ...
// }
-// bb3: {
+// bb4: {
// _0 = ();
// StorageDead(_5);
// EndRegion('26_1rs);
// StorageDead(_1);
// return;
// }
-// bb4: {
+// bb5: {
// _4 = ();
// StorageDead(_5);
// StorageLive(_7);
// StorageDead(_7);
// EndRegion('26_1rs);
// StorageDead(_3);
-// falseUnwind -> [real: bb1, cleanup: bb2];
+// goto -> bb1;
// }
// END rustc.main.SimplifyCfg-qualify-consts.after.mir
// _1 = const false;
// StorageLive(_2);
// _2 = const 3i32;
-// StorageLive(_4);
-// goto -> bb2;
+// falseUnwind -> [real: bb2, cleanup: bb1];
// }
// bb1: {
// ...
// }
// bb2: {
+// StorageLive(_4);
+// goto -> bb3;
+// }
+// bb3: {
+// falseUnwind -> [real: bb4, cleanup: bb1];
+// }
+// bb4: {
// StorageLive(_7);
// _7 = _1;
-// switchInt(move _7) -> [0u8: bb4, otherwise: bb3];
+// switchInt(move _7) -> [0u8: bb6, otherwise: bb5];
// }
-// bb3: {
+// bb5: {
// _0 = ();
// StorageDead(_7);
// EndRegion('33_0rs);
// StorageDead(_1);
// return;
// }
-// bb4: {
+// bb6: {
// _4 = &'33_0rs _2;
// _6 = ();
// StorageDead(_7);
// _1 = const true;
// _3 = ();
-// falseUnwind -> [real: bb2, cleanup: bb1];
+// goto -> bb3;
// }
// }
// END rustc.main.SimplifyCfg-qualify-consts.after.mir
// goto -> bb1;
// }
// bb1: {
+// falseUnwind -> [real: bb2, cleanup: bb3];
+// }
+// bb2: {
// StorageLive(_2);
// StorageLive(_3);
// StorageLive(_4);
// _4 = std::option::Option<&'35_0rs S<'35_0rs>>::None;
-// _3 = const <std::cell::Cell<T>>::new(move _4) -> [return: bb3, unwind: bb2];
+// _3 = const <std::cell::Cell<T>>::new(move _4) -> [return: bb4, unwind: bb3];
// }
-// bb2: {
+// bb3: {
// resume;
// }
-// bb3: {
+// bb4: {
// StorageDead(_4);
// _2 = S<'35_0rs> { r: move _3 };
// StorageDead(_3);
// _8 = &'35_0rs (*_9);
// _7 = std::option::Option<&'35_0rs S<'35_0rs>>::Some(move _8,);
// StorageDead(_8);
-// _5 = const <std::cell::Cell<T>>::set(move _6, move _7) -> [return: bb4, unwind: bb2];
+// _5 = const <std::cell::Cell<T>>::set(move _6, move _7) -> [return: bb5, unwind: bb3];
// }
-// bb4: {
+// bb5: {
// EndRegion('16s);
// StorageDead(_7);
// StorageDead(_6);
// StorageDead(_9);
// StorageLive(_11);
-// _11 = const query() -> [return: bb5, unwind: bb2];
-// }
-// bb5: {
-// switchInt(move _11) -> [0u8: bb7, otherwise: bb6];
+// _11 = const query() -> [return: bb6, unwind: bb3];
// }
// bb6: {
+// switchInt(move _11) -> [0u8: bb8, otherwise: bb7];
+// }
+// bb7: {
// _0 = ();
// StorageDead(_11);
// EndRegion('35_0rs);
// StorageDead(_2);
// return;
// }
-// bb7: {
+// bb8: {
// _10 = ();
// StorageDead(_11);
// StorageLive(_14);
// _16 = &'35_0rs (*_17);
// _15 = std::option::Option<&'35_0rs S<'35_0rs>>::Some(move _16,);
// StorageDead(_16);
-// _13 = const <std::cell::Cell<T>>::set(move _14, move _15) -> [return: bb8, unwind: bb2];
+// _13 = const <std::cell::Cell<T>>::set(move _14, move _15) -> [return: bb9, unwind: bb3];
// }
-// bb8: {
+// bb9: {
// EndRegion('33s);
// StorageDead(_15);
// StorageDead(_14);
// _1 = ();
// EndRegion('35_0rs);
// StorageDead(_2);
-// falseUnwind -> [real: bb1, cleanup: bb2];
+// goto -> bb1;
// }
// }
// END rustc.main.SimplifyCfg-qualify-consts.after.mir
// _1 = const false;
// goto -> bb2;
// }
-//
// bb1: {
// resume;
// }
// bb2: {
+// falseUnwind -> [real: bb3, cleanup: bb1];
+// }
+// bb3: {
// StorageLive(_4);
// _4 = _1;
-// switchInt(move _4) -> [0u8: bb4, otherwise: bb3];
+// switchInt(move _4) -> [0u8: bb5, otherwise: bb4];
// }
-// bb3: {
+// bb4: {
// _0 = ();
// StorageDead(_4);
// StorageDead(_1);
// return;
// }
-//
-// bb4: {
+// bb5: {
// _3 = ();
// StorageDead(_4);
// _1 = const true;
// _2 = ();
-// falseUnwind -> [real: bb2, cleanup: bb1];
+// goto -> bb2;
// }
// END rustc.main.SimplifyCfg-initial.after.mir
// END RUST SOURCE
// START rustc.main.nll.0.mir
-// | Live variables on entry to bb2: []
-// bb2: {
-// | Live variables on entry to bb2[0]: []
+// | Live variables on entry to bb3: []
+// bb3: {
+// | Live variables on entry to bb3[0]: []
// _1 = const 55usize;
-// | Live variables on entry to bb2[1]: [_1]
+// | Live variables on entry to bb3[1]: [_1]
// StorageLive(_3);
-// | Live variables on entry to bb2[2]: [_1]
+// | Live variables on entry to bb3[2]: [_1]
// StorageLive(_4);
-// | Live variables on entry to bb2[3]: [_1]
+// | Live variables on entry to bb3[3]: [_1]
// _4 = _1;
-// | Live variables on entry to bb2[4]: [_4]
-// _3 = const use_x(move _4) -> [return: bb3, unwind: bb1];
+// | Live variables on entry to bb3[4]: [_4]
+// _3 = const use_x(move _4) -> [return: bb4, unwind: bb1];
// }
// END rustc.main.nll.0.mir