Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[let_chains, 2/6] Introduce Let(..) in AST, remove IfLet + WhileLet and parse let chains #60861

Merged
merged 35 commits into from
Jun 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
42accc9
let_chains: Remove ast::ExprKind::{IfLet, WhileLet} and introduce ::Let.
Centril May 15, 2019
f0ab633
let_chains: Remove ast::ExprKind::{IfLet, WhileLet} from visitors and…
Centril May 15, 2019
3fc9642
let_chains: Handle it in AST pretty printing.
Centril May 15, 2019
dff1e37
let_chains: Add support for parsing let expressions.
Centril May 15, 2019
70a65e9
let_chains: Handle in resolve.
Centril May 15, 2019
fcffac5
let_chains: Handle in unused parenthesis lint.
Centril May 15, 2019
a7b00f5
let_chains: Handle disallowing of let chains in places lowering won't…
Centril May 15, 2019
c9fb639
let_chains: Adjust lowering logic in lieu of ::Let.
Centril May 15, 2019
ebea1c2
let_chains: Add feature gate.
Centril May 11, 2019
357b499
let_chains: Add tests for places where let expressions aren't allowed.
Centril May 11, 2019
c0c5791
let_chains: Add feature gate tests.
Centril May 11, 2019
6a6b079
let_chains: Adjust unnecessary parens tests.
Centril May 15, 2019
d45dadd
let_chains: Add test protecting the precedence of && in relation to o…
Centril May 15, 2019
b425df0
let_chains: Remove redundant tests in syntax-ambiguity-*.rs.
Centril May 11, 2019
530f954
let_chains: Test pretty output for simple stable if-let.
Centril May 15, 2019
86250a6
let_chains: Comment out Let in ident_can_begin_expr.
Centril May 16, 2019
8b72e4c
let_chains: Improve documentation for ast::ExprKind::Let(..).
Centril May 17, 2019
3b7f0cb
let_chains: Fix outdated doc-comment re. 'parse_if_expr'.
Centril May 17, 2019
1ff947f
let_chains: scrutinee -> head expression.
Centril May 17, 2019
a80aa34
let_chains: Change AST validation strategy slightly.
Centril May 18, 2019
07f37c8
let_chains: Account for const generics in validation tests.
Centril May 21, 2019
a505d9d
let_chains: scrutinee -> condition
Centril May 25, 2019
61fc727
let_chains: Fuse PatternSource::Let & ::LetExpr.
Centril Jun 16, 2019
92587e4
let_chains: readd kw::let to ident_can_begin_expr.
Centril Jun 16, 2019
d551880
let_chains: Inline visit_expr_with_let_maybe_allowed.
Centril Jun 16, 2019
eb4f54a
let_chains: Move feature gating to pre-expansion.
Centril Jun 17, 2019
10234d2
let_chains: Adjust tests for pre-expansion gating.
Centril Jun 17, 2019
2017be4
let_chains: Remove ast_validation logic in favor of lowering with rec…
Centril Jun 17, 2019
5ae5086
let_chains: --bless tests due to recovery in lowering.
Centril Jun 17, 2019
7465eb4
let_chains: Refactor parse_{if,while}_expr a bit.
Centril Jun 17, 2019
851066f
let_chains: Fix bugs in pretty printing.
Centril Jun 17, 2019
bc72ce6
let_chains: Add test cases to pprust-expr-roundtrip.
Centril Jun 17, 2019
90b9e96
let_chains: More accurately describe `ast::ExprKind::Let`.
Centril Jun 22, 2019
7abb235
let_chains: Revert 'fn with' in ast_validation.
Centril Jun 22, 2019
c75f7ec
let_chains: note re. back-compat wrt. expr beginning.
Centril Jun 22, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
280 changes: 140 additions & 140 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4344,53 +4344,147 @@ impl<'a> LoweringContext<'a> {
let ohs = P(self.lower_expr(ohs));
hir::ExprKind::AddrOf(m, ohs)
}
// More complicated than you might expect because the else branch
// might be `if let`.
ExprKind::Let(ref pats, ref scrutinee) => {
// If we got here, the `let` expression is not allowed.
self.sess
.struct_span_err(e.span, "`let` expressions are not supported here")
.note("only supported directly in conditions of `if`- and `while`-expressions")
.note("as well as when nested within `&&` and parenthesis in those conditions")
.emit();

// For better recovery, we emit:
petrochenkov marked this conversation as resolved.
Show resolved Hide resolved
// ```
// match scrutinee { pats => true, _ => false }
// ```
// While this doesn't fully match the user's intent, it has key advantages:
// 1. We can avoid using `abort_if_errors`.
// 2. We can typeck both `pats` and `scrutinee`.
// 3. `pats` is allowed to be refutable.
// 4. The return type of the block is `bool` which seems like what the user wanted.
let scrutinee = self.lower_expr(scrutinee);
let then_arm = {
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
let expr = self.expr_bool(e.span, true);
self.arm(pats, P(expr))
};
let else_arm = {
let pats = hir_vec![self.pat_wild(e.span)];
let expr = self.expr_bool(e.span, false);
self.arm(pats, P(expr))
};
hir::ExprKind::Match(
P(scrutinee),
vec![then_arm, else_arm].into(),
hir::MatchSource::Normal,
)
}
// FIXME(#53667): handle lowering of && and parens.
ExprKind::If(ref cond, ref then, ref else_opt) => {
// `true => then`:
let then_pat = self.pat_bool(e.span, true);
let then_blk = self.lower_block(then, false);
let then_expr = self.expr_block(then_blk, ThinVec::new());
let then_arm = self.arm(hir_vec![then_pat], P(then_expr));

// `_ => else_block` where `else_block` is `{}` if there's `None`:
let else_pat = self.pat_wild(e.span);
let else_expr = match else_opt {
None => self.expr_block_empty(e.span),
Some(els) => match els.node {
ExprKind::IfLet(..) => {
// Wrap the `if let` expr in a block.
let els = self.lower_expr(els);
let blk = self.block_all(els.span, hir_vec![], Some(P(els)));
self.expr_block(P(blk), ThinVec::new())
}
_ => self.lower_expr(els),
}
let (else_expr, contains_else_clause) = match else_opt {
None => (self.expr_block_empty(e.span), false),
Some(els) => (self.lower_expr(els), true),
};
let else_arm = self.arm(hir_vec![else_pat], P(else_expr));

// Lower condition:
let span_block = self.mark_span_with_reason(IfTemporary, cond.span, None);
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 cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
// Handle then + scrutinee:
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>`
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`:
_ => {
// 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);
// 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 = self.expr_drop_temps(span_block, P(cond), ThinVec::new());

let desugar = hir::MatchSource::IfDesugar { contains_else_clause };
let pats = hir_vec![self.pat_bool(e.span, true)];
(pats, cond, desugar)
}
};
let then_arm = self.arm(then_pats, P(then_expr));

hir::ExprKind::Match(
P(cond),
vec![then_arm, else_arm].into(),
hir::MatchSource::IfDesugar {
contains_else_clause: else_opt.is_some()
},
)
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))),
)
});

// `<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 break_arm = {
let pat_under = self.pat_wild(e.span);
self.arm(hir_vec![pat_under], break_expr)
};

// `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(),
);

// `[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(
Centril marked this conversation as resolved.
Show resolved Hide resolved
this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
this.lower_block(body, false),
this.lower_label(opt_label),
)
})
}
}
ExprKind::While(ref cond, ref body, opt_label) => 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),
)
}),
ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| {
hir::ExprKind::Loop(
this.lower_block(body, false),
Expand Down Expand Up @@ -4703,105 +4797,6 @@ impl<'a> LoweringContext<'a> {

ExprKind::Err => hir::ExprKind::Err,

// Desugar `ExprIfLet`
// from: `if let <pat> = <sub_expr> <body> [<else_opt>]`
ExprKind::IfLet(ref pats, ref sub_expr, ref body, ref else_opt) => {
// to:
//
// match <sub_expr> {
// <pat> => <body>,
// _ => [<else_opt> | ()]
// }

let mut arms = vec![];

// `<pat> => <body>`
{
let body = self.lower_block(body, false);
let body_expr = P(self.expr_block(body, ThinVec::new()));
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
arms.push(self.arm(pats, body_expr));
}

// _ => [<else_opt>|{}]
{
let wildcard_arm: Option<&Expr> = else_opt.as_ref().map(|p| &**p);
let wildcard_pattern = self.pat_wild(e.span);
let body = if let Some(else_expr) = wildcard_arm {
self.lower_expr(else_expr)
} else {
self.expr_block_empty(e.span)
};
arms.push(self.arm(hir_vec![wildcard_pattern], P(body)));
}

let contains_else_clause = else_opt.is_some();

let sub_expr = P(self.lower_expr(sub_expr));

hir::ExprKind::Match(
sub_expr,
arms.into(),
hir::MatchSource::IfLetDesugar {
contains_else_clause,
},
)
}

// Desugar `ExprWhileLet`
// from: `[opt_ident]: while let <pat> = <sub_expr> <body>`
ExprKind::WhileLet(ref pats, ref sub_expr, ref body, opt_label) => {
// 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))),
)
});

// `<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 break_arm = {
let pat_under = self.pat_wild(e.span);
self.arm(hir_vec![pat_under], break_expr)
};

// `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(),
);

// `[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
}

// Desugar `ExprForLoop`
// from: `[opt_ident]: for <pat> in <head> <body>`
ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => {
Expand Down Expand Up @@ -5463,10 +5458,15 @@ impl<'a> LoweringContext<'a> {
)
}

/// Constructs a `true` or `false` literal expression.
fn expr_bool(&mut self, span: Span, val: bool) -> hir::Expr {
let lit = Spanned { span, node: LitKind::Bool(val) };
self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new())
}

/// Constructs a `true` or `false` literal pattern.
fn pat_bool(&mut self, span: Span, val: bool) -> P<hir::Pat> {
let lit = Spanned { span, node: LitKind::Bool(val) };
let expr = self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new());
let expr = self.expr_bool(span, val);
self.pat(span, hir::PatKind::Lit(P(expr)))
}

Expand Down
36 changes: 21 additions & 15 deletions src/librustc_lint/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,20 +324,28 @@ impl UnusedParens {
value: &ast::Expr,
msg: &str,
followed_by_block: bool) {
if let ast::ExprKind::Paren(ref inner) = value.node {
let necessary = followed_by_block && match inner.node {
ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
_ => parser::contains_exterior_struct_lit(&inner),
};
if !necessary {
let expr_text = if let Ok(snippet) = cx.sess().source_map()
.span_to_snippet(value.span) {
snippet
} else {
pprust::expr_to_string(value)
};
Self::remove_outer_parens(cx, value.span, &expr_text, msg);
match value.node {
ast::ExprKind::Paren(ref inner) => {
let necessary = followed_by_block && match inner.node {
ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
_ => parser::contains_exterior_struct_lit(&inner),
};
if !necessary {
let expr_text = if let Ok(snippet) = cx.sess().source_map()
.span_to_snippet(value.span) {
snippet
} else {
pprust::expr_to_string(value)
};
Self::remove_outer_parens(cx, value.span, &expr_text, msg);
}
}
ast::ExprKind::Let(_, ref expr) => {
// FIXME(#60336): Properly handle `let true = (false && true)`
// actually needing the parenthesis.
self.check_unused_parens_expr(cx, expr, "`let` head expression", followed_by_block);
}
_ => {}
}
}

Expand Down Expand Up @@ -399,8 +407,6 @@ impl EarlyLintPass for UnusedParens {
let (value, msg, followed_by_block) = match e.node {
If(ref cond, ..) => (cond, "`if` condition", true),
While(ref cond, ..) => (cond, "`while` condition", true),
IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true),
WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true),
ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true),
Match(ref head, _) => (head, "`match` head expression", true),
Ret(Some(ref value)) => (value, "`return` value", false),
Expand Down
Loading