Skip to content

Commit a093e7d

Browse files
committed
Auto merge of #60861 - Centril:let-chains-ast-intro, r=<try>
[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 + do some AST validation + 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 e7591c1 + f607ff1 commit a093e7d

29 files changed

+1398
-611
lines changed

Diff for: src/librustc/hir/lowering.rs

+109-141
Original file line numberDiff line numberDiff line change
@@ -4121,56 +4121,123 @@ impl<'a> LoweringContext<'a> {
41214121
let ohs = P(self.lower_expr(ohs));
41224122
hir::ExprKind::AddrOf(m, ohs)
41234123
}
4124-
// More complicated than you might expect because the else branch
4125-
// might be `if let`.
4124+
ExprKind::Let(..) => {
4125+
// This should have been caught `ast_validation`!
4126+
self.sess.span_err(e.span, "`let` expressions only supported in `if`");
4127+
// ^-- FIXME(53667): Change to `delay_span_bug` when let_chains handled in lowering.
4128+
self.sess.abort_if_errors();
4129+
hir::ExprKind::Err
4130+
}
4131+
// FIXME(#53667): handle lowering of && and parens.
41264132
ExprKind::If(ref cond, ref then, ref else_opt) => {
4127-
// `true => then`:
4128-
let then_pat = self.pat_bool(e.span, true);
4129-
let then_blk = self.lower_block(then, false);
4130-
let then_expr = self.expr_block(then_blk, ThinVec::new());
4131-
let then_arm = self.arm(hir_vec![then_pat], P(then_expr));
4132-
41334133
// `_ => else_block` where `else_block` is `{}` if there's `None`:
41344134
let else_pat = self.pat_wild(e.span);
4135-
let else_expr = match else_opt {
4136-
None => self.expr_block_empty(e.span),
4137-
Some(els) => match els.node {
4138-
ExprKind::IfLet(..) => {
4139-
// Wrap the `if let` expr in a block.
4140-
let els = self.lower_expr(els);
4141-
let blk = self.block_all(els.span, hir_vec![], Some(P(els)));
4142-
self.expr_block(P(blk), ThinVec::new())
4143-
}
4144-
_ => self.lower_expr(els),
4145-
}
4135+
let (else_expr, contains_else_clause) = match else_opt {
4136+
None => (self.expr_block_empty(e.span), false),
4137+
Some(els) => (self.lower_expr(els), true),
41464138
};
41474139
let else_arm = self.arm(hir_vec![else_pat], P(else_expr));
41484140

4149-
// Lower condition:
4150-
let span_block = self
4151-
.sess
4152-
.source_map()
4153-
.mark_span_with_reason(IfTemporary, cond.span, None);
4154-
let cond = self.lower_expr(cond);
4155-
// Wrap in a construct equivalent to `{ let _t = $cond; _t }` to preserve drop
4156-
// semantics since `if cond { ... }` don't let temporaries live outside of `cond`.
4157-
let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
4141+
// Handle then + scrutinee:
4142+
let then_blk = self.lower_block(then, false);
4143+
let then_expr = self.expr_block(then_blk, ThinVec::new());
4144+
let (then_pats, scrutinee, desugar) = match cond.node {
4145+
// `<pat> => <then>`
4146+
ExprKind::Let(ref pats, ref scrutinee) => {
4147+
let scrutinee = self.lower_expr(scrutinee);
4148+
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
4149+
let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause };
4150+
(pats, scrutinee, desugar)
4151+
}
4152+
// `true => then`:
4153+
_ => {
4154+
// Lower condition:
4155+
let cond = self.lower_expr(cond);
4156+
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
4157+
// to preserve drop semantics since `if cond { ... }`
4158+
// don't let temporaries live outside of `cond`.
4159+
let span_block = self
4160+
.sess
4161+
.source_map()
4162+
.mark_span_with_reason(IfTemporary, cond.span, None);
4163+
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
4164+
// to preserve drop semantics since `if cond { ... }` does not
4165+
// let temporaries live outside of `cond`.
4166+
let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
4167+
4168+
let desugar = hir::MatchSource::IfDesugar { contains_else_clause };
4169+
let pats = hir_vec![self.pat_bool(e.span, true)];
4170+
(pats, cond, desugar)
4171+
}
4172+
};
4173+
let then_arm = self.arm(then_pats, P(then_expr));
41584174

4159-
hir::ExprKind::Match(
4160-
P(cond),
4161-
vec![then_arm, else_arm].into(),
4162-
hir::MatchSource::IfDesugar {
4163-
contains_else_clause: else_opt.is_some()
4164-
},
4165-
)
4175+
hir::ExprKind::Match(P(scrutinee), vec![then_arm, else_arm].into(), desugar)
4176+
}
4177+
// FIXME(#53667): handle lowering of && and parens.
4178+
ExprKind::While(ref cond, ref body, opt_label) => {
4179+
// Desugar `ExprWhileLet`
4180+
// from: `[opt_ident]: while let <pat> = <sub_expr> <body>`
4181+
if let ExprKind::Let(ref pats, ref sub_expr) = cond.node {
4182+
// to:
4183+
//
4184+
// [opt_ident]: loop {
4185+
// match <sub_expr> {
4186+
// <pat> => <body>,
4187+
// _ => break
4188+
// }
4189+
// }
4190+
4191+
// Note that the block AND the condition are evaluated in the loop scope.
4192+
// This is done to allow `break` from inside the condition of the loop.
4193+
let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| {
4194+
(
4195+
this.lower_block(body, false),
4196+
this.expr_break(e.span, ThinVec::new()),
4197+
this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
4198+
)
4199+
});
4200+
4201+
// `<pat> => <body>`
4202+
let pat_arm = {
4203+
let body_expr = P(self.expr_block(body, ThinVec::new()));
4204+
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
4205+
self.arm(pats, body_expr)
4206+
};
4207+
4208+
// `_ => break`
4209+
let break_arm = {
4210+
let pat_under = self.pat_wild(e.span);
4211+
self.arm(hir_vec![pat_under], break_expr)
4212+
};
4213+
4214+
// `match <sub_expr> { ... }`
4215+
let arms = hir_vec![pat_arm, break_arm];
4216+
let match_expr = self.expr(
4217+
sub_expr.span,
4218+
hir::ExprKind::Match(sub_expr, arms, hir::MatchSource::WhileLetDesugar),
4219+
ThinVec::new(),
4220+
);
4221+
4222+
// `[opt_ident]: loop { ... }`
4223+
let loop_block = P(self.block_expr(P(match_expr)));
4224+
let loop_expr = hir::ExprKind::Loop(
4225+
loop_block,
4226+
self.lower_label(opt_label),
4227+
hir::LoopSource::WhileLet,
4228+
);
4229+
// Add attributes to the outer returned expr node.
4230+
loop_expr
4231+
} else {
4232+
self.with_loop_scope(e.id, |this| {
4233+
hir::ExprKind::While(
4234+
this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
4235+
this.lower_block(body, false),
4236+
this.lower_label(opt_label),
4237+
)
4238+
})
4239+
}
41664240
}
4167-
ExprKind::While(ref cond, ref body, opt_label) => self.with_loop_scope(e.id, |this| {
4168-
hir::ExprKind::While(
4169-
this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
4170-
this.lower_block(body, false),
4171-
this.lower_label(opt_label),
4172-
)
4173-
}),
41744241
ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| {
41754242
hir::ExprKind::Loop(
41764243
this.lower_block(body, false),
@@ -4490,105 +4557,6 @@ impl<'a> LoweringContext<'a> {
44904557

44914558
ExprKind::Err => hir::ExprKind::Err,
44924559

4493-
// Desugar `ExprIfLet`
4494-
// from: `if let <pat> = <sub_expr> <body> [<else_opt>]`
4495-
ExprKind::IfLet(ref pats, ref sub_expr, ref body, ref else_opt) => {
4496-
// to:
4497-
//
4498-
// match <sub_expr> {
4499-
// <pat> => <body>,
4500-
// _ => [<else_opt> | ()]
4501-
// }
4502-
4503-
let mut arms = vec![];
4504-
4505-
// `<pat> => <body>`
4506-
{
4507-
let body = self.lower_block(body, false);
4508-
let body_expr = P(self.expr_block(body, ThinVec::new()));
4509-
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
4510-
arms.push(self.arm(pats, body_expr));
4511-
}
4512-
4513-
// _ => [<else_opt>|{}]
4514-
{
4515-
let wildcard_arm: Option<&Expr> = else_opt.as_ref().map(|p| &**p);
4516-
let wildcard_pattern = self.pat_wild(e.span);
4517-
let body = if let Some(else_expr) = wildcard_arm {
4518-
self.lower_expr(else_expr)
4519-
} else {
4520-
self.expr_block_empty(e.span)
4521-
};
4522-
arms.push(self.arm(hir_vec![wildcard_pattern], P(body)));
4523-
}
4524-
4525-
let contains_else_clause = else_opt.is_some();
4526-
4527-
let sub_expr = P(self.lower_expr(sub_expr));
4528-
4529-
hir::ExprKind::Match(
4530-
sub_expr,
4531-
arms.into(),
4532-
hir::MatchSource::IfLetDesugar {
4533-
contains_else_clause,
4534-
},
4535-
)
4536-
}
4537-
4538-
// Desugar `ExprWhileLet`
4539-
// from: `[opt_ident]: while let <pat> = <sub_expr> <body>`
4540-
ExprKind::WhileLet(ref pats, ref sub_expr, ref body, opt_label) => {
4541-
// to:
4542-
//
4543-
// [opt_ident]: loop {
4544-
// match <sub_expr> {
4545-
// <pat> => <body>,
4546-
// _ => break
4547-
// }
4548-
// }
4549-
4550-
// Note that the block AND the condition are evaluated in the loop scope.
4551-
// This is done to allow `break` from inside the condition of the loop.
4552-
let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| {
4553-
(
4554-
this.lower_block(body, false),
4555-
this.expr_break(e.span, ThinVec::new()),
4556-
this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
4557-
)
4558-
});
4559-
4560-
// `<pat> => <body>`
4561-
let pat_arm = {
4562-
let body_expr = P(self.expr_block(body, ThinVec::new()));
4563-
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
4564-
self.arm(pats, body_expr)
4565-
};
4566-
4567-
// `_ => break`
4568-
let break_arm = {
4569-
let pat_under = self.pat_wild(e.span);
4570-
self.arm(hir_vec![pat_under], break_expr)
4571-
};
4572-
4573-
// `match <sub_expr> { ... }`
4574-
let arms = hir_vec![pat_arm, break_arm];
4575-
let match_expr = self.expr(
4576-
sub_expr.span,
4577-
hir::ExprKind::Match(sub_expr, arms, hir::MatchSource::WhileLetDesugar),
4578-
ThinVec::new(),
4579-
);
4580-
4581-
// `[opt_ident]: loop { ... }`
4582-
let loop_block = P(self.block_expr(P(match_expr)));
4583-
let loop_expr = hir::ExprKind::Loop(
4584-
loop_block,
4585-
self.lower_label(opt_label),
4586-
hir::LoopSource::WhileLet,
4587-
);
4588-
// Add attributes to the outer returned expr node.
4589-
loop_expr
4590-
}
4591-
45924560
// Desugar `ExprForLoop`
45934561
// from: `[opt_ident]: for <pat> in <head> <body>`
45944562
ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => {

Diff for: src/librustc_lint/unused.rs

+21-15
Original file line numberDiff line numberDiff line change
@@ -294,20 +294,28 @@ impl UnusedParens {
294294
value: &ast::Expr,
295295
msg: &str,
296296
followed_by_block: bool) {
297-
if let ast::ExprKind::Paren(ref inner) = value.node {
298-
let necessary = followed_by_block && match inner.node {
299-
ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
300-
_ => parser::contains_exterior_struct_lit(&inner),
301-
};
302-
if !necessary {
303-
let expr_text = if let Ok(snippet) = cx.sess().source_map()
304-
.span_to_snippet(value.span) {
305-
snippet
306-
} else {
307-
pprust::expr_to_string(value)
308-
};
309-
Self::remove_outer_parens(cx, value.span, &expr_text, msg);
297+
match value.node {
298+
ast::ExprKind::Paren(ref inner) => {
299+
let necessary = followed_by_block && match inner.node {
300+
ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
301+
_ => parser::contains_exterior_struct_lit(&inner),
302+
};
303+
if !necessary {
304+
let expr_text = if let Ok(snippet) = cx.sess().source_map()
305+
.span_to_snippet(value.span) {
306+
snippet
307+
} else {
308+
pprust::expr_to_string(value)
309+
};
310+
Self::remove_outer_parens(cx, value.span, &expr_text, msg);
311+
}
312+
}
313+
ast::ExprKind::Let(_, ref expr) => {
314+
// FIXME(#60336): Properly handle `let true = (false && true)`
315+
// actually needing the parenthesis.
316+
self.check_unused_parens_expr(cx, expr, "`let` head expression", followed_by_block);
310317
}
318+
_ => {}
311319
}
312320
}
313321

@@ -369,8 +377,6 @@ impl EarlyLintPass for UnusedParens {
369377
let (value, msg, followed_by_block) = match e.node {
370378
If(ref cond, ..) => (cond, "`if` condition", true),
371379
While(ref cond, ..) => (cond, "`while` condition", true),
372-
IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true),
373-
WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true),
374380
ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true),
375381
Match(ref head, _) => (head, "`match` head expression", true),
376382
Ret(Some(ref value)) => (value, "`return` value", false),

0 commit comments

Comments
 (0)