1
Fork 0

Remove ExprKind::While from HIR.

This commit is contained in:
Mazdak Farrokhzad 2019-06-19 17:21:28 +02:00
parent 481068a707
commit f8b32dfb27
21 changed files with 114 additions and 272 deletions

View file

@ -165,48 +165,6 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
self.add_ast_node(expr.hir_id.local_id, &[blk_exit])
}
hir::ExprKind::While(ref cond, ref body, _) => {
//
// [pred]
// |
// v 1
// [loopback] <--+ 5
// | |
// v 2 |
// +-----[cond] |
// | | |
// | v 4 |
// | [body] -----+
// v 3
// [expr]
//
// Note that `break` and `continue` statements
// may cause additional edges.
let loopback = self.add_dummy_node(&[pred]); // 1
// Create expr_exit without pred (cond_exit)
let expr_exit = self.add_ast_node(expr.hir_id.local_id, &[]); // 3
// The LoopScope needs to be on the loop_scopes stack while evaluating the
// condition and the body of the loop (both can break out of the loop)
self.loop_scopes.push(LoopScope {
loop_id: expr.hir_id.local_id,
continue_index: loopback,
break_index: expr_exit
});
let cond_exit = self.expr(&cond, loopback); // 2
// Add pred (cond_exit) to expr_exit
self.add_contained_edge(cond_exit, expr_exit);
let body_exit = self.block(&body, cond_exit); // 4
self.add_contained_edge(body_exit, loopback); // 5
self.loop_scopes.pop();
expr_exit
}
hir::ExprKind::Loop(ref body, _, _) => {
//
// [pred]

View file

@ -1026,11 +1026,6 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
ExprKind::DropTemps(ref subexpression) => {
visitor.visit_expr(subexpression);
}
ExprKind::While(ref subexpression, ref block, ref opt_label) => {
walk_list!(visitor, visit_label, opt_label);
visitor.visit_expr(subexpression);
visitor.visit_block(block);
}
ExprKind::Loop(ref block, ref opt_label, _) => {
walk_list!(visitor, visit_label, opt_label);
visitor.visit_block(block);

View file

@ -4461,11 +4461,11 @@ impl<'a> LoweringContext<'a> {
};
// `match <sub_expr> { ... }`
let arms = hir_vec![pat_arm, break_arm];
let match_expr = self.expr(
let match_expr = self.expr_match(
sub_expr.span,
hir::ExprKind::Match(sub_expr, arms, hir::MatchSource::WhileLetDesugar),
ThinVec::new(),
sub_expr,
hir_vec![pat_arm, break_arm],
hir::MatchSource::WhileLetDesugar,
);
// `[opt_ident]: loop { ... }`
@ -4479,10 +4479,46 @@ impl<'a> LoweringContext<'a> {
loop_expr
} else {
self.with_loop_scope(e.id, |this| {
hir::ExprKind::While(
this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
this.lower_block(body, false),
// We desugar: `'label: while $cond $body` into:
//
// ```
// 'label: loop {
// match DropTemps($cond) {
// true => $block,
// _ => break,
// }
// }
// ```
// `true => then`:
let then_pat = this.pat_bool(e.span, true);
let then_blk = this.lower_block(body, false);
let then_expr = this.expr_block(then_blk, ThinVec::new());
let then_arm = this.arm(hir_vec![then_pat], P(then_expr));
// `_ => break`:
let else_pat = this.pat_wild(e.span);
let else_expr = this.expr_break(e.span, ThinVec::new());
let else_arm = this.arm(hir_vec![else_pat], else_expr);
// Lower condition:
let cond = this.with_loop_condition_scope(|this| this.lower_expr(cond));
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
// to preserve drop semantics since `if cond { ... }` does not
// let temporaries live outside of `cond`.
let cond = this.expr_drop_temps(cond.span, P(cond), ThinVec::new());
let match_expr = this.expr_match(
cond.span,
P(cond),
vec![then_arm, else_arm].into(),
hir::MatchSource::WhileDesugar,
);
hir::ExprKind::Loop(
P(this.block_expr(P(match_expr))),
this.lower_label(opt_label),
hir::LoopSource::While,
)
})
}

View file

@ -731,7 +731,7 @@ impl<'hir> Map<'hir> {
match *node {
Node::Expr(ref expr) => {
match expr.node {
ExprKind::While(..) | ExprKind::Loop(..) | ExprKind::Ret(..) => true,
ExprKind::Loop(..) | ExprKind::Ret(..) => true,
_ => false,
}
}

View file

@ -1405,7 +1405,6 @@ impl Expr {
ExprKind::Lit(_) => ExprPrecedence::Lit,
ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast,
ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
ExprKind::While(..) => ExprPrecedence::While,
ExprKind::Loop(..) => ExprPrecedence::Loop,
ExprKind::Match(..) => ExprPrecedence::Match,
ExprKind::Closure(..) => ExprPrecedence::Closure,
@ -1464,7 +1463,6 @@ impl Expr {
ExprKind::Break(..) |
ExprKind::Continue(..) |
ExprKind::Ret(..) |
ExprKind::While(..) |
ExprKind::Loop(..) |
ExprKind::Assign(..) |
ExprKind::InlineAsm(..) |
@ -1532,10 +1530,6 @@ pub enum ExprKind {
/// This construct only exists to tweak the drop order in HIR lowering.
/// An example of that is the desugaring of `for` loops.
DropTemps(P<Expr>),
/// A while loop, with an optional label
///
/// I.e., `'label: while expr { <block> }`.
While(P<Expr>, P<Block>, Option<Label>),
/// A conditionless loop (can be exited with `break`, `continue`, or `return`).
///
/// I.e., `'label: loop { <block> }`.
@ -1653,6 +1647,8 @@ pub enum MatchSource {
IfLetDesugar {
contains_else_clause: bool,
},
/// A `while _ { .. }` (which was desugared to a `loop { match _ { .. } }`).
WhileDesugar,
/// A `while let _ = _ { .. }` (which was desugared to a
/// `loop { match _ { .. } }`).
WhileLetDesugar,
@ -1669,12 +1665,25 @@ pub enum MatchSource {
pub enum LoopSource {
/// A `loop { .. }` loop.
Loop,
/// A `while _ { .. }` loop.
While,
/// A `while let _ = _ { .. }` loop.
WhileLet,
/// A `for _ in _ { .. }` loop.
ForLoop,
}
impl LoopSource {
pub fn name(self) -> &'static str {
match self {
LoopSource::Loop => "loop",
LoopSource::While => "while",
LoopSource::WhileLet => "while let",
LoopSource::ForLoop => "for",
}
}
}
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
pub enum LoopIdError {
OutsideLoopScope,

View file

@ -1299,16 +1299,6 @@ impl<'a> State<'a> {
// Print `}`:
self.bclose_maybe_open(expr.span, indent_unit, true);
}
hir::ExprKind::While(ref test, ref blk, opt_label) => {
if let Some(label) = opt_label {
self.print_ident(label.ident);
self.word_space(":");
}
self.head("while");
self.print_expr_as_cond(&test);
self.s.space();
self.print_block(&blk);
}
hir::ExprKind::Loop(ref blk, opt_label, _) => {
if let Some(label) = opt_label {
self.print_ident(label.ident);
@ -2289,7 +2279,6 @@ fn expr_requires_semi_to_be_stmt(e: &hir::Expr) -> bool {
match e.node {
hir::ExprKind::Match(..) |
hir::ExprKind::Block(..) |
hir::ExprKind::While(..) |
hir::ExprKind::Loop(..) => false,
_ => true,
}

View file

@ -487,11 +487,6 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
self.walk_block(&blk);
}
hir::ExprKind::While(ref cond_expr, ref blk, _) => {
self.consume_expr(&cond_expr);
self.walk_block(&blk);
}
hir::ExprKind::Unary(_, ref lhs) => {
self.consume_expr(&lhs);
}

View file

@ -93,7 +93,6 @@
//! It is the responsibility of typeck to ensure that there are no
//! `return` expressions in a function declared as diverging.
use self::LoopKind::*;
use self::LiveNodeKind::*;
use self::VarKind::*;
@ -120,14 +119,6 @@ use crate::hir::{Expr, HirId};
use crate::hir::def_id::DefId;
use crate::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
/// For use with `propagate_through_loop`.
enum LoopKind<'a> {
/// An endless `loop` loop.
LoopLoop,
/// A `while` loop, with the given expression as condition.
WhileLoop(&'a Expr),
}
#[derive(Copy, Clone, PartialEq)]
struct Variable(u32);
@ -517,7 +508,6 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr) {
// live nodes required for interesting control flow:
hir::ExprKind::Match(..) |
hir::ExprKind::While(..) |
hir::ExprKind::Loop(..) => {
ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
intravisit::walk_expr(ir, expr);
@ -1055,14 +1045,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
})
}
hir::ExprKind::While(ref cond, ref blk, _) => {
self.propagate_through_loop(expr, WhileLoop(&cond), &blk, succ)
}
// Note that labels have been resolved, so we don't need to look
// at the label ident
hir::ExprKind::Loop(ref blk, _, _) => {
self.propagate_through_loop(expr, LoopLoop, &blk, succ)
self.propagate_through_loop(expr, &blk, succ)
}
hir::ExprKind::Match(ref e, ref arms, _) => {
@ -1353,13 +1339,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}
}
fn propagate_through_loop(&mut self,
expr: &Expr,
kind: LoopKind<'_>,
body: &hir::Block,
succ: LiveNode)
-> LiveNode {
fn propagate_through_loop(
&mut self,
expr: &Expr,
body: &hir::Block,
succ: LiveNode
) -> LiveNode {
/*
FIXME: clean up this description.
We model control flow like this:
@ -1377,50 +1364,26 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
*/
// first iteration:
let mut first_merge = true;
let ln = self.live_node(expr.hir_id, expr.span);
self.init_empty(ln, succ);
match kind {
LoopLoop => {}
_ => {
// If this is not a `loop` loop, then it's possible we bypass
// the body altogether. Otherwise, the only way is via a `break`
// in the loop body.
self.merge_from_succ(ln, succ, first_merge);
first_merge = false;
}
}
debug!("propagate_through_loop: using id for loop body {} {}",
expr.hir_id, self.ir.tcx.hir().hir_to_pretty_string(body.hir_id));
self.break_ln.insert(expr.hir_id, succ);
let cond_ln = match kind {
LoopLoop => ln,
WhileLoop(ref cond) => self.propagate_through_expr(&cond, ln),
};
self.cont_ln.insert(expr.hir_id, ln);
self.cont_ln.insert(expr.hir_id, cond_ln);
let body_ln = self.propagate_through_block(body, cond_ln);
let body_ln = self.propagate_through_block(body, ln);
// repeat until fixed point is reached:
while self.merge_from_succ(ln, body_ln, first_merge) {
first_merge = false;
let new_cond_ln = match kind {
LoopLoop => ln,
WhileLoop(ref cond) => {
self.propagate_through_expr(&cond, ln)
}
};
assert_eq!(cond_ln, new_cond_ln);
assert_eq!(body_ln, self.propagate_through_block(body, cond_ln));
assert_eq!(body_ln, self.propagate_through_block(body, ln));
}
cond_ln
ln
}
}
@ -1520,7 +1483,7 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
// no correctness conditions related to liveness
hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) |
hir::ExprKind::Match(..) | hir::ExprKind::While(..) | hir::ExprKind::Loop(..) |
hir::ExprKind::Match(..) | hir::ExprKind::Loop(..) |
hir::ExprKind::Index(..) | hir::ExprKind::Field(..) |
hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) | hir::ExprKind::Binary(..) |
hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) | hir::ExprKind::Unary(..) |

View file

@ -696,7 +696,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
hir::ExprKind::Unary(..) | hir::ExprKind::Yield(..) |
hir::ExprKind::MethodCall(..) | hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) |
hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) |
hir::ExprKind::Binary(..) | hir::ExprKind::While(..) |
hir::ExprKind::Binary(..) |
hir::ExprKind::Block(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..) |
hir::ExprKind::Lit(..) | hir::ExprKind::Break(..) |
hir::ExprKind::Continue(..) | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) |

View file

@ -915,11 +915,6 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
terminating(body.hir_id.local_id);
}
hir::ExprKind::While(ref expr, ref body, _) => {
terminating(expr.hir_id.local_id);
terminating(body.hir_id.local_id);
}
hir::ExprKind::DropTemps(ref expr) => {
// `DropTemps(expr)` does not denote a conditional scope.
// Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`.

View file

@ -1201,11 +1201,10 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body) {
}
fn expression_label(ex: &hir::Expr) -> Option<ast::Ident> {
match ex.node {
hir::ExprKind::While(.., Some(label)) | hir::ExprKind::Loop(_, Some(label), _) => {
Some(label.ident)
}
_ => None,
if let hir::ExprKind::Loop(_, Some(label), _) = ex.node {
Some(label.ident)
} else {
None
}
}

View file

@ -64,9 +64,25 @@ declare_lint! {
declare_lint_pass!(WhileTrue => [WHILE_TRUE]);
fn as_while_cond(expr: &hir::Expr) -> Option<&hir::Expr> {
if let hir::ExprKind::Loop(blk, ..) = &expr.node {
if let Some(match_expr) = &blk.expr {
if let hir::ExprKind::Match(cond, .., hir::MatchSource::WhileDesugar)
= &match_expr.node
{
if let hir::ExprKind::DropTemps(cond) = &cond.node {
return Some(cond);
}
}
}
}
None
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WhileTrue {
fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) {
if let hir::ExprKind::While(ref cond, ..) = e.node {
if let Some(ref cond) = as_while_cond(e) {
if let hir::ExprKind::Lit(ref lit) = cond.node {
if let ast::LitKind::Bool(true) = lit.node {
if lit.span.ctxt() == SyntaxContext::empty() {

View file

@ -138,19 +138,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
join_block.unit()
}
ExprKind::Loop {
condition: opt_cond_expr,
body,
} => {
// [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:
//
ExprKind::Loop { body } => {
// [block]
// |
// [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
@ -177,33 +165,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
destination.clone(),
move |this| {
// conduct the test, if necessary
let body_block;
if let Some(cond_expr) = opt_cond_expr {
let cond_expr = this.hir.mirror(cond_expr);
let (true_block, false_block)
= this.test_bool(loop_block, cond_expr, source_info);
body_block = true_block;
// if the test is false, there's no `break` to assign `destination`, so
// we have to do it
this.cfg.push_assign_unit(false_block, source_info, destination);
this.cfg.terminate(
false_block,
source_info,
TerminatorKind::Goto { target: exit_block },
);
} else {
body_block = this.cfg.start_new_block();
let diverge_cleanup = this.diverge_cleanup();
this.cfg.terminate(
loop_block,
source_info,
TerminatorKind::FalseUnwind {
real_target: body_block,
unwind: Some(diverge_cleanup),
},
)
}
let body_block = this.cfg.start_new_block();
let diverge_cleanup = this.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
// introduce a unit temporary as the destination for the loop body.

View file

@ -599,15 +599,8 @@ fn make_mirror_unadjusted<'a, 'tcx>(
arms: arms.iter().map(|a| convert_arm(cx, a)).collect(),
}
}
hir::ExprKind::While(ref cond, ref body, _) => {
ExprKind::Loop {
condition: Some(cond.to_ref()),
body: block::to_expr_ref(cx, body),
}
}
hir::ExprKind::Loop(ref body, _, _) => {
ExprKind::Loop {
condition: None,
body: block::to_expr_ref(cx, body),
}
}

View file

@ -173,7 +173,6 @@ pub enum ExprKind<'tcx> {
source: ExprRef<'tcx>,
},
Loop {
condition: Option<ExprRef<'tcx>>,
body: ExprRef<'tcx>,
},
Match {

View file

@ -371,7 +371,8 @@ fn check_arms<'a, 'tcx>(
match is_useful(cx, &seen, &v, LeaveOutWitness) {
NotUseful => {
match source {
hir::MatchSource::IfDesugar { .. } => bug!(),
hir::MatchSource::IfDesugar { .. } |
hir::MatchSource::WhileDesugar => bug!(),
hir::MatchSource::IfLetDesugar { .. } => {
cx.tcx.lint_hir(
lint::builtin::IRREFUTABLE_LET_PATTERNS,

View file

@ -12,27 +12,10 @@ use syntax::struct_span_err;
use syntax_pos::Span;
use errors::Applicability;
#[derive(Clone, Copy, Debug, PartialEq)]
enum LoopKind {
Loop(hir::LoopSource),
WhileLoop,
}
impl LoopKind {
fn name(self) -> &'static str {
match self {
LoopKind::Loop(hir::LoopSource::Loop) => "loop",
LoopKind::Loop(hir::LoopSource::WhileLet) => "while let",
LoopKind::Loop(hir::LoopSource::ForLoop) => "for",
LoopKind::WhileLoop => "while",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
enum Context {
Normal,
Loop(LoopKind),
Loop(hir::LoopSource),
Closure,
LabeledBlock,
AnonConst,
@ -71,14 +54,8 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
fn visit_expr(&mut self, e: &'hir hir::Expr) {
match e.node {
hir::ExprKind::While(ref e, ref b, _) => {
self.with_context(Loop(LoopKind::WhileLoop), |v| {
v.visit_expr(&e);
v.visit_block(&b);
});
}
hir::ExprKind::Loop(ref b, _, source) => {
self.with_context(Loop(LoopKind::Loop(source)), |v| v.visit_block(&b));
self.with_context(Loop(source), |v| v.visit_block(&b));
}
hir::ExprKind::Closure(_, ref function_decl, b, _, _) => {
self.visit_fn_decl(&function_decl);
@ -117,15 +94,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
None
} else {
Some(match self.hir_map.expect_expr(loop_id).node {
hir::ExprKind::While(..) => LoopKind::WhileLoop,
hir::ExprKind::Loop(_, _, source) => LoopKind::Loop(source),
hir::ExprKind::Loop(_, _, source) => source,
ref r => span_bug!(e.span,
"break label resolved to a non-loop: {:?}", r),
})
};
match loop_kind {
None |
Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
Some(hir::LoopSource::Loop) => (),
Some(kind) => {
struct_span_err!(self.sess, e.span, E0571,
"`break` with value from a `{}` loop",

View file

@ -520,13 +520,6 @@ fn check_expr_kind<'a, 'tcx>(
NotPromotable
}
// Loops (not very meaningful in constants).
hir::ExprKind::While(ref expr, ref box_block, ref _option_label) => {
let _ = v.check_expr(expr);
let _ = v.check_block(box_block);
NotPromotable
}
hir::ExprKind::Loop(ref box_block, ref _option_label, ref _loop_source) => {
let _ = v.check_block(box_block);
NotPromotable

View file

@ -159,11 +159,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Warn for non-block expressions with diverging children.
match expr.node {
ExprKind::Block(..) |
ExprKind::Loop(..) | ExprKind::While(..) |
ExprKind::Match(..) => {}
_ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression")
ExprKind::Block(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {},
_ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"),
}
// Any expression that produces a value of type `!` must have diverged
@ -245,9 +242,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::Assign(ref lhs, ref rhs) => {
self.check_expr_assign(expr, expected, lhs, rhs)
}
ExprKind::While(ref cond, ref body, _) => {
self.check_expr_while(cond, body, expr)
}
ExprKind::Loop(ref body, _, source) => {
self.check_expr_loop(body, source, expected, expr)
}
@ -702,36 +696,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
fn check_expr_while(
&self,
cond: &'tcx hir::Expr,
body: &'tcx hir::Block,
expr: &'tcx hir::Expr
) -> Ty<'tcx> {
let ctxt = BreakableCtxt {
// Cannot use break with a value from a while loop.
coerce: None,
may_break: false, // Will get updated if/when we find a `break`.
};
let (ctxt, ()) = self.with_breakable_ctxt(expr.hir_id, ctxt, || {
self.check_expr_has_type_or_error(&cond, self.tcx.types.bool);
let cond_diverging = self.diverges.get();
self.check_block_no_value(&body);
// We may never reach the body so it diverging means nothing.
self.diverges.set(cond_diverging);
});
if ctxt.may_break {
// No way to know whether it's diverging because
// of a `break` or an outer `break` or `return`.
self.diverges.set(Diverges::Maybe);
}
self.tcx.mk_unit()
}
fn check_expr_loop(
&self,
body: &'tcx hir::Block,
@ -746,6 +710,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(CoerceMany::new(coerce_to))
}
hir::LoopSource::While |
hir::LoopSource::WhileLet |
hir::LoopSource::ForLoop => {
None

View file

@ -3865,7 +3865,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match expression.node {
ExprKind::Call(..) |
ExprKind::MethodCall(..) |
ExprKind::While(..) |
ExprKind::Loop(..) |
ExprKind::Match(..) |
ExprKind::Block(..) => {

View file

@ -685,16 +685,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> {
self.set_repeating_scope(repeating_scope);
}
hir::ExprKind::While(ref cond, ref body, _) => {
let repeating_scope = self.set_repeating_scope(cond.hir_id);
self.visit_expr(&cond);
self.set_repeating_scope(body.hir_id);
self.visit_block(&body);
self.set_repeating_scope(repeating_scope);
}
hir::ExprKind::Ret(Some(ref ret_expr)) => {
let call_site_scope = self.call_site_scope;
debug!(