Skip to content

Commit 793461e

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 7158ed9 + d5a1621 commit 793461e

29 files changed

+1375
-604
lines changed

src/librustc/hir/lowering.rs

+109-141
Original file line numberDiff line numberDiff line change
@@ -4114,56 +4114,123 @@ impl<'a> LoweringContext<'a> {
41144114
let ohs = P(self.lower_expr(ohs));
41154115
hir::ExprKind::AddrOf(m, ohs)
41164116
}
4117-
// More complicated than you might expect because the else branch
4118-
// might be `if let`.
4117+
ExprKind::Let(..) => {
4118+
// This should have been caught `ast_validation`!
4119+
self.sess.span_err(e.span, "`let` expressions only supported in `if`");
4120+
// ^-- FIXME(53667): Change to `delay_span_bug` when let_chains handled in lowering.
4121+
self.sess.abort_if_errors();
4122+
hir::ExprKind::Err
4123+
}
4124+
// FIXME(#53667): handle lowering of && and parens.
41194125
ExprKind::If(ref cond, ref then, ref else_opt) => {
4120-
// `true => then`:
4121-
let then_pat = self.pat_bool(e.span, true);
4122-
let then_blk = self.lower_block(then, false);
4123-
let then_expr = self.expr_block(then_blk, ThinVec::new());
4124-
let then_arm = self.arm(hir_vec![then_pat], P(then_expr));
4125-
41264126
// `_ => else_block` where `else_block` is `{}` if there's `None`:
41274127
let else_pat = self.pat_wild(e.span);
4128-
let else_expr = match else_opt {
4129-
None => self.expr_block_empty(e.span),
4130-
Some(els) => match els.node {
4131-
ExprKind::IfLet(..) => {
4132-
// Wrap the `if let` expr in a block.
4133-
let els = self.lower_expr(els);
4134-
let blk = self.block_all(els.span, hir_vec![], Some(P(els)));
4135-
self.expr_block(P(blk), ThinVec::new())
4136-
}
4137-
_ => self.lower_expr(els),
4138-
}
4128+
let (else_expr, contains_else_clause) = match else_opt {
4129+
None => (self.expr_block_empty(e.span), false),
4130+
Some(els) => (self.lower_expr(els), true),
41394131
};
41404132
let else_arm = self.arm(hir_vec![else_pat], P(else_expr));
41414133

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

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

44834550
ExprKind::Err => hir::ExprKind::Err,
44844551

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

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` scrutinee", 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)