Skip to content

Commit 5d677b2

Browse files
committed
Auto merge of #60861 - Centril:let-chains-ast-intro, r=petrochenkov
[let_chains, 2/6] Introduce `Let(..)` in AST, remove IfLet + WhileLet and parse let chains Here we remove `ast::ExprKind::{IfLet, WhileLet}` and introduce `ast::ExprKind::Let`. Moreover, we also: + connect the parsing logic for let chains + introduce the feature gate + rewire HIR lowering a bit. However, this does not connect the new syntax to semantics in HIR. That will be the subject of a subsequent PR. Per #53667 (comment). Next step after #59288. cc @Manishearth re. Clippy. r? @oli-obk
2 parents a96ba96 + c75f7ec commit 5d677b2

33 files changed

+2334
-625
lines changed

src/librustc/hir/lowering.rs

+140-140
Original file line numberDiff line numberDiff line change
@@ -4344,53 +4344,147 @@ impl<'a> LoweringContext<'a> {
43444344
let ohs = P(self.lower_expr(ohs));
43454345
hir::ExprKind::AddrOf(m, ohs)
43464346
}
4347-
// More complicated than you might expect because the else branch
4348-
// might be `if let`.
4347+
ExprKind::Let(ref pats, ref scrutinee) => {
4348+
// If we got here, the `let` expression is not allowed.
4349+
self.sess
4350+
.struct_span_err(e.span, "`let` expressions are not supported here")
4351+
.note("only supported directly in conditions of `if`- and `while`-expressions")
4352+
.note("as well as when nested within `&&` and parenthesis in those conditions")
4353+
.emit();
4354+
4355+
// For better recovery, we emit:
4356+
// ```
4357+
// match scrutinee { pats => true, _ => false }
4358+
// ```
4359+
// While this doesn't fully match the user's intent, it has key advantages:
4360+
// 1. We can avoid using `abort_if_errors`.
4361+
// 2. We can typeck both `pats` and `scrutinee`.
4362+
// 3. `pats` is allowed to be refutable.
4363+
// 4. The return type of the block is `bool` which seems like what the user wanted.
4364+
let scrutinee = self.lower_expr(scrutinee);
4365+
let then_arm = {
4366+
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
4367+
let expr = self.expr_bool(e.span, true);
4368+
self.arm(pats, P(expr))
4369+
};
4370+
let else_arm = {
4371+
let pats = hir_vec![self.pat_wild(e.span)];
4372+
let expr = self.expr_bool(e.span, false);
4373+
self.arm(pats, P(expr))
4374+
};
4375+
hir::ExprKind::Match(
4376+
P(scrutinee),
4377+
vec![then_arm, else_arm].into(),
4378+
hir::MatchSource::Normal,
4379+
)
4380+
}
4381+
// FIXME(#53667): handle lowering of && and parens.
43494382
ExprKind::If(ref cond, ref then, ref else_opt) => {
4350-
// `true => then`:
4351-
let then_pat = self.pat_bool(e.span, true);
4352-
let then_blk = self.lower_block(then, false);
4353-
let then_expr = self.expr_block(then_blk, ThinVec::new());
4354-
let then_arm = self.arm(hir_vec![then_pat], P(then_expr));
4355-
43564383
// `_ => else_block` where `else_block` is `{}` if there's `None`:
43574384
let else_pat = self.pat_wild(e.span);
4358-
let else_expr = match else_opt {
4359-
None => self.expr_block_empty(e.span),
4360-
Some(els) => match els.node {
4361-
ExprKind::IfLet(..) => {
4362-
// Wrap the `if let` expr in a block.
4363-
let els = self.lower_expr(els);
4364-
let blk = self.block_all(els.span, hir_vec![], Some(P(els)));
4365-
self.expr_block(P(blk), ThinVec::new())
4366-
}
4367-
_ => self.lower_expr(els),
4368-
}
4385+
let (else_expr, contains_else_clause) = match else_opt {
4386+
None => (self.expr_block_empty(e.span), false),
4387+
Some(els) => (self.lower_expr(els), true),
43694388
};
43704389
let else_arm = self.arm(hir_vec![else_pat], P(else_expr));
43714390

4372-
// Lower condition:
4373-
let span_block = self.mark_span_with_reason(IfTemporary, cond.span, None);
4374-
let cond = self.lower_expr(cond);
4375-
// Wrap in a construct equivalent to `{ let _t = $cond; _t }` to preserve drop
4376-
// semantics since `if cond { ... }` don't let temporaries live outside of `cond`.
4377-
let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
4391+
// Handle then + scrutinee:
4392+
let then_blk = self.lower_block(then, false);
4393+
let then_expr = self.expr_block(then_blk, ThinVec::new());
4394+
let (then_pats, scrutinee, desugar) = match cond.node {
4395+
// `<pat> => <then>`
4396+
ExprKind::Let(ref pats, ref scrutinee) => {
4397+
let scrutinee = self.lower_expr(scrutinee);
4398+
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
4399+
let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause };
4400+
(pats, scrutinee, desugar)
4401+
}
4402+
// `true => then`:
4403+
_ => {
4404+
// Lower condition:
4405+
let cond = self.lower_expr(cond);
4406+
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
4407+
// to preserve drop semantics since `if cond { ... }`
4408+
// don't let temporaries live outside of `cond`.
4409+
let span_block = self.mark_span_with_reason(IfTemporary, cond.span, None);
4410+
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
4411+
// to preserve drop semantics since `if cond { ... }` does not
4412+
// let temporaries live outside of `cond`.
4413+
let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
4414+
4415+
let desugar = hir::MatchSource::IfDesugar { contains_else_clause };
4416+
let pats = hir_vec![self.pat_bool(e.span, true)];
4417+
(pats, cond, desugar)
4418+
}
4419+
};
4420+
let then_arm = self.arm(then_pats, P(then_expr));
43784421

4379-
hir::ExprKind::Match(
4380-
P(cond),
4381-
vec![then_arm, else_arm].into(),
4382-
hir::MatchSource::IfDesugar {
4383-
contains_else_clause: else_opt.is_some()
4384-
},
4385-
)
4422+
hir::ExprKind::Match(P(scrutinee), vec![then_arm, else_arm].into(), desugar)
4423+
}
4424+
// FIXME(#53667): handle lowering of && and parens.
4425+
ExprKind::While(ref cond, ref body, opt_label) => {
4426+
// Desugar `ExprWhileLet`
4427+
// from: `[opt_ident]: while let <pat> = <sub_expr> <body>`
4428+
if let ExprKind::Let(ref pats, ref sub_expr) = cond.node {
4429+
// to:
4430+
//
4431+
// [opt_ident]: loop {
4432+
// match <sub_expr> {
4433+
// <pat> => <body>,
4434+
// _ => break
4435+
// }
4436+
// }
4437+
4438+
// Note that the block AND the condition are evaluated in the loop scope.
4439+
// This is done to allow `break` from inside the condition of the loop.
4440+
let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| {
4441+
(
4442+
this.lower_block(body, false),
4443+
this.expr_break(e.span, ThinVec::new()),
4444+
this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
4445+
)
4446+
});
4447+
4448+
// `<pat> => <body>`
4449+
let pat_arm = {
4450+
let body_expr = P(self.expr_block(body, ThinVec::new()));
4451+
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
4452+
self.arm(pats, body_expr)
4453+
};
4454+
4455+
// `_ => break`
4456+
let break_arm = {
4457+
let pat_under = self.pat_wild(e.span);
4458+
self.arm(hir_vec![pat_under], break_expr)
4459+
};
4460+
4461+
// `match <sub_expr> { ... }`
4462+
let arms = hir_vec![pat_arm, break_arm];
4463+
let match_expr = self.expr(
4464+
sub_expr.span,
4465+
hir::ExprKind::Match(sub_expr, arms, hir::MatchSource::WhileLetDesugar),
4466+
ThinVec::new(),
4467+
);
4468+
4469+
// `[opt_ident]: loop { ... }`
4470+
let loop_block = P(self.block_expr(P(match_expr)));
4471+
let loop_expr = hir::ExprKind::Loop(
4472+
loop_block,
4473+
self.lower_label(opt_label),
4474+
hir::LoopSource::WhileLet,
4475+
);
4476+
// Add attributes to the outer returned expr node.
4477+
loop_expr
4478+
} else {
4479+
self.with_loop_scope(e.id, |this| {
4480+
hir::ExprKind::While(
4481+
this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
4482+
this.lower_block(body, false),
4483+
this.lower_label(opt_label),
4484+
)
4485+
})
4486+
}
43864487
}
4387-
ExprKind::While(ref cond, ref body, opt_label) => self.with_loop_scope(e.id, |this| {
4388-
hir::ExprKind::While(
4389-
this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
4390-
this.lower_block(body, false),
4391-
this.lower_label(opt_label),
4392-
)
4393-
}),
43944488
ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| {
43954489
hir::ExprKind::Loop(
43964490
this.lower_block(body, false),
@@ -4703,105 +4797,6 @@ impl<'a> LoweringContext<'a> {
47034797

47044798
ExprKind::Err => hir::ExprKind::Err,
47054799

4706-
// Desugar `ExprIfLet`
4707-
// from: `if let <pat> = <sub_expr> <body> [<else_opt>]`
4708-
ExprKind::IfLet(ref pats, ref sub_expr, ref body, ref else_opt) => {
4709-
// to:
4710-
//
4711-
// match <sub_expr> {
4712-
// <pat> => <body>,
4713-
// _ => [<else_opt> | ()]
4714-
// }
4715-
4716-
let mut arms = vec![];
4717-
4718-
// `<pat> => <body>`
4719-
{
4720-
let body = self.lower_block(body, false);
4721-
let body_expr = P(self.expr_block(body, ThinVec::new()));
4722-
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
4723-
arms.push(self.arm(pats, body_expr));
4724-
}
4725-
4726-
// _ => [<else_opt>|{}]
4727-
{
4728-
let wildcard_arm: Option<&Expr> = else_opt.as_ref().map(|p| &**p);
4729-
let wildcard_pattern = self.pat_wild(e.span);
4730-
let body = if let Some(else_expr) = wildcard_arm {
4731-
self.lower_expr(else_expr)
4732-
} else {
4733-
self.expr_block_empty(e.span)
4734-
};
4735-
arms.push(self.arm(hir_vec![wildcard_pattern], P(body)));
4736-
}
4737-
4738-
let contains_else_clause = else_opt.is_some();
4739-
4740-
let sub_expr = P(self.lower_expr(sub_expr));
4741-
4742-
hir::ExprKind::Match(
4743-
sub_expr,
4744-
arms.into(),
4745-
hir::MatchSource::IfLetDesugar {
4746-
contains_else_clause,
4747-
},
4748-
)
4749-
}
4750-
4751-
// Desugar `ExprWhileLet`
4752-
// from: `[opt_ident]: while let <pat> = <sub_expr> <body>`
4753-
ExprKind::WhileLet(ref pats, ref sub_expr, ref body, opt_label) => {
4754-
// to:
4755-
//
4756-
// [opt_ident]: loop {
4757-
// match <sub_expr> {
4758-
// <pat> => <body>,
4759-
// _ => break
4760-
// }
4761-
// }
4762-
4763-
// Note that the block AND the condition are evaluated in the loop scope.
4764-
// This is done to allow `break` from inside the condition of the loop.
4765-
let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| {
4766-
(
4767-
this.lower_block(body, false),
4768-
this.expr_break(e.span, ThinVec::new()),
4769-
this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
4770-
)
4771-
});
4772-
4773-
// `<pat> => <body>`
4774-
let pat_arm = {
4775-
let body_expr = P(self.expr_block(body, ThinVec::new()));
4776-
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
4777-
self.arm(pats, body_expr)
4778-
};
4779-
4780-
// `_ => break`
4781-
let break_arm = {
4782-
let pat_under = self.pat_wild(e.span);
4783-
self.arm(hir_vec![pat_under], break_expr)
4784-
};
4785-
4786-
// `match <sub_expr> { ... }`
4787-
let arms = hir_vec![pat_arm, break_arm];
4788-
let match_expr = self.expr(
4789-
sub_expr.span,
4790-
hir::ExprKind::Match(sub_expr, arms, hir::MatchSource::WhileLetDesugar),
4791-
ThinVec::new(),
4792-
);
4793-
4794-
// `[opt_ident]: loop { ... }`
4795-
let loop_block = P(self.block_expr(P(match_expr)));
4796-
let loop_expr = hir::ExprKind::Loop(
4797-
loop_block,
4798-
self.lower_label(opt_label),
4799-
hir::LoopSource::WhileLet,
4800-
);
4801-
// Add attributes to the outer returned expr node.
4802-
loop_expr
4803-
}
4804-
48054800
// Desugar `ExprForLoop`
48064801
// from: `[opt_ident]: for <pat> in <head> <body>`
48074802
ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => {
@@ -5463,10 +5458,15 @@ impl<'a> LoweringContext<'a> {
54635458
)
54645459
}
54655460

5461+
/// Constructs a `true` or `false` literal expression.
5462+
fn expr_bool(&mut self, span: Span, val: bool) -> hir::Expr {
5463+
let lit = Spanned { span, node: LitKind::Bool(val) };
5464+
self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new())
5465+
}
5466+
54665467
/// Constructs a `true` or `false` literal pattern.
54675468
fn pat_bool(&mut self, span: Span, val: bool) -> P<hir::Pat> {
5468-
let lit = Spanned { span, node: LitKind::Bool(val) };
5469-
let expr = self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new());
5469+
let expr = self.expr_bool(span, val);
54705470
self.pat(span, hir::PatKind::Lit(P(expr)))
54715471
}
54725472

src/librustc_lint/unused.rs

+21-15
Original file line numberDiff line numberDiff line change
@@ -324,20 +324,28 @@ impl UnusedParens {
324324
value: &ast::Expr,
325325
msg: &str,
326326
followed_by_block: bool) {
327-
if let ast::ExprKind::Paren(ref inner) = value.node {
328-
let necessary = followed_by_block && match inner.node {
329-
ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
330-
_ => parser::contains_exterior_struct_lit(&inner),
331-
};
332-
if !necessary {
333-
let expr_text = if let Ok(snippet) = cx.sess().source_map()
334-
.span_to_snippet(value.span) {
335-
snippet
336-
} else {
337-
pprust::expr_to_string(value)
338-
};
339-
Self::remove_outer_parens(cx, value.span, &expr_text, msg);
327+
match value.node {
328+
ast::ExprKind::Paren(ref inner) => {
329+
let necessary = followed_by_block && match inner.node {
330+
ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
331+
_ => parser::contains_exterior_struct_lit(&inner),
332+
};
333+
if !necessary {
334+
let expr_text = if let Ok(snippet) = cx.sess().source_map()
335+
.span_to_snippet(value.span) {
336+
snippet
337+
} else {
338+
pprust::expr_to_string(value)
339+
};
340+
Self::remove_outer_parens(cx, value.span, &expr_text, msg);
341+
}
342+
}
343+
ast::ExprKind::Let(_, ref expr) => {
344+
// FIXME(#60336): Properly handle `let true = (false && true)`
345+
// actually needing the parenthesis.
346+
self.check_unused_parens_expr(cx, expr, "`let` head expression", followed_by_block);
340347
}
348+
_ => {}
341349
}
342350
}
343351

@@ -399,8 +407,6 @@ impl EarlyLintPass for UnusedParens {
399407
let (value, msg, followed_by_block) = match e.node {
400408
If(ref cond, ..) => (cond, "`if` condition", true),
401409
While(ref cond, ..) => (cond, "`while` condition", true),
402-
IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true),
403-
WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true),
404410
ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true),
405411
Match(ref head, _) => (head, "`match` head expression", true),
406412
Ret(Some(ref value)) => (value, "`return` value", false),

0 commit comments

Comments
 (0)