Skip to content

Commit

Permalink
Auto merge of #61988 - Centril:there-is-only-loop, r=matthewjasper
Browse files Browse the repository at this point in the history
[let_chains, 3/6] And then there was only Loop

Here we remove `hir::ExprKind::While`.
Instead, we desugar: `'label: while $cond $body` into:

```rust
'label: loop {
    match DropTemps($cond) {
        true => $body,
        _ => break,
    }
}
```

Per #53667 (comment).
This is a follow up to #59288 which did the same for `if` expressions.

r? @matthewjasper
  • Loading branch information
bors committed Jul 6, 2019
2 parents b820c76 + 9b1d513 commit 254f201
Show file tree
Hide file tree
Showing 52 changed files with 409 additions and 487 deletions.
42 changes: 0 additions & 42 deletions src/librustc/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
5 changes: 0 additions & 5 deletions src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
138 changes: 72 additions & 66 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ use syntax::errors;
use syntax::ext::hygiene::{Mark, SyntaxContext};
use syntax::print::pprust;
use syntax::source_map::{self, respan, ExpnInfo, CompilerDesugaringKind, Spanned};
use syntax::source_map::CompilerDesugaringKind::IfTemporary;
use syntax::source_map::CompilerDesugaringKind::CondTemporary;
use syntax::std_inject;
use syntax::symbol::{kw, sym, Symbol};
use syntax::tokenstream::{TokenStream, TokenTree};
Expand Down Expand Up @@ -4394,21 +4394,18 @@ impl<'a> LoweringContext<'a> {
let then_blk = self.lower_block(then, false);
let then_expr = self.expr_block(then_blk, ThinVec::new());
let (then_pats, scrutinee, desugar) = match cond.node {
// `<pat> => <then>`
// `<pat> => <then>`:
ExprKind::Let(ref pats, ref scrutinee) => {
let scrutinee = self.lower_expr(scrutinee);
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause };
(pats, scrutinee, desugar)
}
// `true => then`:
// `true => <then>`:
_ => {
// Lower condition:
let cond = self.lower_expr(cond);
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
// to preserve drop semantics since `if cond { ... }`
// don't let temporaries live outside of `cond`.
let span_block = self.mark_span_with_reason(IfTemporary, cond.span, None);
let span_block = self.mark_span_with_reason(CondTemporary, cond.span, None);
// 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`.
Expand All @@ -4424,69 +4421,78 @@ impl<'a> LoweringContext<'a> {
hir::ExprKind::Match(P(scrutinee), vec![then_arm, else_arm].into(), desugar)
}
// FIXME(#53667): handle lowering of && and parens.
ExprKind::While(ref cond, ref body, opt_label) => {
// Desugar `ExprWhileLet`
// from: `[opt_ident]: while let <pat> = <sub_expr> <body>`
if let ExprKind::Let(ref pats, ref sub_expr) = cond.node {
// to:
//
// [opt_ident]: loop {
// match <sub_expr> {
// <pat> => <body>,
// _ => break
// }
// }

// Note that the block AND the condition are evaluated in the loop scope.
// This is done to allow `break` from inside the condition of the loop.
let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| {
(
this.lower_block(body, false),
this.expr_break(e.span, ThinVec::new()),
this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
)
});
ExprKind::While(ref cond, ref body, opt_label) => self.with_loop_scope(e.id, |this| {
// Note that the block AND the condition are evaluated in the loop scope.
// This is done to allow `break` from inside the condition of the loop.

// `<pat> => <body>`
let pat_arm = {
let body_expr = P(self.expr_block(body, ThinVec::new()));
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
self.arm(pats, body_expr)
};
// `_ => break`:
let else_arm = {
let else_pat = this.pat_wild(e.span);
let else_expr = this.expr_break(e.span, ThinVec::new());
this.arm(hir_vec![else_pat], else_expr)
};

// `_ => break`
let break_arm = {
let pat_under = self.pat_wild(e.span);
self.arm(hir_vec![pat_under], break_expr)
};
// Handle then + scrutinee:
let then_blk = this.lower_block(body, false);
let then_expr = this.expr_block(then_blk, ThinVec::new());
let (then_pats, scrutinee, desugar, source) = match cond.node {
ExprKind::Let(ref pats, ref scrutinee) => {
// to:
//
// [opt_ident]: loop {
// match <sub_expr> {
// <pat> => <body>,
// _ => break
// }
// }
let scrutinee = this.with_loop_condition_scope(|t| t.lower_expr(scrutinee));
let pats = pats.iter().map(|pat| this.lower_pat(pat)).collect();
let desugar = hir::MatchSource::WhileLetDesugar;
(pats, scrutinee, desugar, hir::LoopSource::WhileLet)
}
_ => {
// We desugar: `'label: while $cond $body` into:
//
// ```
// 'label: loop {
// match DropTemps($cond) {
// true => $body,
// _ => break,
// }
// }
// ```

// `match <sub_expr> { ... }`
let arms = hir_vec![pat_arm, break_arm];
let match_expr = self.expr(
sub_expr.span,
hir::ExprKind::Match(sub_expr, arms, hir::MatchSource::WhileLetDesugar),
ThinVec::new(),
);
// Lower condition:
let cond = this.with_loop_condition_scope(|this| this.lower_expr(cond));
let span_block = this.mark_span_with_reason(CondTemporary, cond.span, None);
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
// to preserve drop semantics since `while cond { ... }` does not
// let temporaries live outside of `cond`.
let cond = this.expr_drop_temps(span_block, P(cond), ThinVec::new());

// `[opt_ident]: loop { ... }`
let loop_block = P(self.block_expr(P(match_expr)));
let loop_expr = hir::ExprKind::Loop(
loop_block,
self.lower_label(opt_label),
hir::LoopSource::WhileLet,
);
// Add attributes to the outer returned expr node.
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),
this.lower_label(opt_label),
)
})
}
}
let desugar = hir::MatchSource::WhileDesugar;
// `true => <then>`:
let pats = hir_vec![this.pat_bool(e.span, true)];
(pats, cond, desugar, hir::LoopSource::While)
}
};
let then_arm = this.arm(then_pats, P(then_expr));

// `match <scrutinee> { ... }`
let match_expr = this.expr_match(
scrutinee.span,
P(scrutinee),
hir_vec![then_arm, else_arm],
desugar,
);

// `[opt_ident]: loop { ... }`
hir::ExprKind::Loop(
P(this.block_expr(P(match_expr))),
this.lower_label(opt_label),
source
)
}),
ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| {
hir::ExprKind::Loop(
this.lower_block(body, false),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Expand Down
21 changes: 15 additions & 6 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1464,7 +1463,6 @@ impl Expr {
ExprKind::Break(..) |
ExprKind::Continue(..) |
ExprKind::Ret(..) |
ExprKind::While(..) |
ExprKind::Loop(..) |
ExprKind::Assign(..) |
ExprKind::InlineAsm(..) |
Expand Down Expand Up @@ -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> }`.
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
11 changes: 0 additions & 11 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ich/impls_syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ impl_stable_hash_for!(enum ::syntax_pos::hygiene::ExpnFormat {
});

impl_stable_hash_for!(enum ::syntax_pos::hygiene::CompilerDesugaringKind {
IfTemporary,
CondTemporary,
Async,
Await,
QuestionMark,
Expand Down
5 changes: 0 additions & 5 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Loading

0 comments on commit 254f201

Please sign in to comment.