Skip to content

Commit 2a6fb9a

Browse files
committed
Auto merge of #80357 - c410-f3r:new-hir-let, r=matthewjasper
Introduce `hir::ExprKind::Let` - Take 2 Builds on #68577 and depends on #79328. cc #53667
2 parents 2bd17c1 + 6aa9937 commit 2a6fb9a

File tree

129 files changed

+2109
-2157
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

129 files changed

+2109
-2157
lines changed

compiler/rustc_ast/src/ast.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1302,7 +1302,9 @@ pub enum ExprKind {
13021302
Type(P<Expr>, P<Ty>),
13031303
/// A `let pat = expr` expression that is only semantically allowed in the condition
13041304
/// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`).
1305-
Let(P<Pat>, P<Expr>),
1305+
///
1306+
/// `Span` represents the whole `let pat = expr` statement.
1307+
Let(P<Pat>, P<Expr>, Span),
13061308
/// An `if` block, with an optional `else` block.
13071309
///
13081310
/// `if expr { block } else { expr }`

compiler/rustc_ast/src/mut_visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1237,7 +1237,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
12371237
vis.visit_ty(ty);
12381238
}
12391239
ExprKind::AddrOf(_, _, ohs) => vis.visit_expr(ohs),
1240-
ExprKind::Let(pat, scrutinee) => {
1240+
ExprKind::Let(pat, scrutinee, _) => {
12411241
vis.visit_pat(pat);
12421242
vis.visit_expr(scrutinee);
12431243
}

compiler/rustc_ast/src/visit.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -779,9 +779,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
779779
visitor.visit_expr(subexpression);
780780
visitor.visit_ty(typ)
781781
}
782-
ExprKind::Let(ref pat, ref scrutinee) => {
782+
ExprKind::Let(ref pat, ref expr, _) => {
783783
visitor.visit_pat(pat);
784-
visitor.visit_expr(scrutinee);
784+
visitor.visit_expr(expr);
785785
}
786786
ExprKind::If(ref head_expression, ref if_block, ref optional_else) => {
787787
visitor.visit_expr(head_expression);

compiler/rustc_ast_lowering/src/expr.rs

+60-200
Original file line numberDiff line numberDiff line change
@@ -86,32 +86,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
8686
let ohs = self.lower_expr(ohs);
8787
hir::ExprKind::AddrOf(k, m, ohs)
8888
}
89-
ExprKind::Let(ref pat, ref scrutinee) => {
90-
self.lower_expr_let(e.span, pat, scrutinee)
89+
ExprKind::Let(ref pat, ref scrutinee, span) => {
90+
hir::ExprKind::Let(self.lower_pat(pat), self.lower_expr(scrutinee), span)
91+
}
92+
ExprKind::If(ref cond, ref then, ref else_opt) => {
93+
self.lower_expr_if(cond, then, else_opt.as_deref())
9194
}
92-
ExprKind::If(ref cond, ref then, ref else_opt) => match cond.kind {
93-
ExprKind::Let(ref pat, ref scrutinee) => {
94-
self.lower_expr_if_let(e.span, pat, scrutinee, then, else_opt.as_deref())
95-
}
96-
ExprKind::Paren(ref paren) => match paren.peel_parens().kind {
97-
ExprKind::Let(ref pat, ref scrutinee) => {
98-
// A user has written `if (let Some(x) = foo) {`, we want to avoid
99-
// confusing them with mentions of nightly features.
100-
// If this logic is changed, you will also likely need to touch
101-
// `unused::UnusedParens::check_expr`.
102-
self.if_let_expr_with_parens(cond, &paren.peel_parens());
103-
self.lower_expr_if_let(
104-
e.span,
105-
pat,
106-
scrutinee,
107-
then,
108-
else_opt.as_deref(),
109-
)
110-
}
111-
_ => self.lower_expr_if(cond, then, else_opt.as_deref()),
112-
},
113-
_ => self.lower_expr_if(cond, then, else_opt.as_deref()),
114-
},
11595
ExprKind::While(ref cond, ref body, opt_label) => self
11696
.with_loop_scope(e.id, |this| {
11797
this.lower_expr_while_in_loop_scope(e.span, cond, body, opt_label)
@@ -368,188 +348,69 @@ impl<'hir> LoweringContext<'_, 'hir> {
368348
hir::ExprKind::Call(f, self.lower_exprs(&real_args))
369349
}
370350

371-
fn if_let_expr_with_parens(&mut self, cond: &Expr, paren: &Expr) {
372-
let start = cond.span.until(paren.span);
373-
let end = paren.span.shrink_to_hi().until(cond.span.shrink_to_hi());
374-
self.sess
375-
.struct_span_err(
376-
vec![start, end],
377-
"invalid parentheses around `let` expression in `if let`",
378-
)
379-
.multipart_suggestion(
380-
"`if let` needs to be written without parentheses",
381-
vec![(start, String::new()), (end, String::new())],
382-
rustc_errors::Applicability::MachineApplicable,
383-
)
384-
.emit();
385-
// Ideally, we'd remove the feature gating of a `let` expression since we are already
386-
// complaining about it here, but `feature_gate::check_crate` has already run by now:
387-
// self.sess.parse_sess.gated_spans.ungate_last(sym::let_chains, paren.span);
388-
}
389-
390-
/// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into:
391-
/// ```rust
392-
/// match scrutinee { pats => true, _ => false }
393-
/// ```
394-
fn lower_expr_let(&mut self, span: Span, pat: &Pat, scrutinee: &Expr) -> hir::ExprKind<'hir> {
395-
// If we got here, the `let` expression is not allowed.
396-
397-
if self.sess.opts.unstable_features.is_nightly_build() {
398-
self.sess
399-
.struct_span_err(span, "`let` expressions are not supported here")
400-
.note(
401-
"only supported directly without parentheses in conditions of `if`- and \
402-
`while`-expressions, as well as in `let` chains within parentheses",
403-
)
404-
.emit();
405-
} else {
406-
self.sess
407-
.struct_span_err(span, "expected expression, found statement (`let`)")
408-
.note("variable declaration using `let` is a statement")
409-
.emit();
410-
}
411-
412-
// For better recovery, we emit:
413-
// ```
414-
// match scrutinee { pat => true, _ => false }
415-
// ```
416-
// While this doesn't fully match the user's intent, it has key advantages:
417-
// 1. We can avoid using `abort_if_errors`.
418-
// 2. We can typeck both `pat` and `scrutinee`.
419-
// 3. `pat` is allowed to be refutable.
420-
// 4. The return type of the block is `bool` which seems like what the user wanted.
421-
let scrutinee = self.lower_expr(scrutinee);
422-
let then_arm = {
423-
let pat = self.lower_pat(pat);
424-
let expr = self.expr_bool(span, true);
425-
self.arm(pat, expr)
426-
};
427-
let else_arm = {
428-
let pat = self.pat_wild(span);
429-
let expr = self.expr_bool(span, false);
430-
self.arm(pat, expr)
431-
};
432-
hir::ExprKind::Match(
433-
scrutinee,
434-
arena_vec![self; then_arm, else_arm],
435-
hir::MatchSource::Normal,
436-
)
437-
}
438-
439351
fn lower_expr_if(
440352
&mut self,
441353
cond: &Expr,
442354
then: &Block,
443355
else_opt: Option<&Expr>,
444356
) -> hir::ExprKind<'hir> {
445-
let cond = self.lower_expr(cond);
446-
let then = self.arena.alloc(self.lower_block_expr(then));
447-
let els = else_opt.map(|els| self.lower_expr(els));
448-
hir::ExprKind::If(cond, then, els)
449-
}
450-
451-
fn lower_expr_if_let(
452-
&mut self,
453-
span: Span,
454-
pat: &Pat,
455-
scrutinee: &Expr,
456-
then: &Block,
457-
else_opt: Option<&Expr>,
458-
) -> hir::ExprKind<'hir> {
459-
// FIXME(#53667): handle lowering of && and parens.
460-
461-
// `_ => else_block` where `else_block` is `{}` if there's `None`:
462-
let else_pat = self.pat_wild(span);
463-
let (else_expr, contains_else_clause) = match else_opt {
464-
None => (self.expr_block_empty(span.shrink_to_hi()), false),
465-
Some(els) => (self.lower_expr(els), true),
466-
};
467-
let else_arm = self.arm(else_pat, else_expr);
468-
469-
// Handle then + scrutinee:
470-
let scrutinee = self.lower_expr(scrutinee);
471-
let then_pat = self.lower_pat(pat);
472-
357+
let lowered_cond = self.lower_expr(cond);
358+
let new_cond = self.manage_let_cond(lowered_cond);
473359
let then_expr = self.lower_block_expr(then);
474-
let then_arm = self.arm(then_pat, self.arena.alloc(then_expr));
360+
if let Some(rslt) = else_opt {
361+
hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), Some(self.lower_expr(rslt)))
362+
} else {
363+
hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), None)
364+
}
365+
}
475366

476-
let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause };
477-
hir::ExprKind::Match(scrutinee, arena_vec![self; then_arm, else_arm], desugar)
367+
// If `cond` kind is `let`, returns `let`. Otherwise, wraps and returns `cond`
368+
// in a temporary block.
369+
fn manage_let_cond(&mut self, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> {
370+
match cond.kind {
371+
hir::ExprKind::Let(..) => cond,
372+
_ => {
373+
let span_block =
374+
self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None);
375+
self.expr_drop_temps(span_block, cond, AttrVec::new())
376+
}
377+
}
478378
}
479379

380+
// We desugar: `'label: while $cond $body` into:
381+
//
382+
// ```
383+
// 'label: loop {
384+
// if { let _t = $cond; _t } {
385+
// $body
386+
// }
387+
// else {
388+
// break;
389+
// }
390+
// }
391+
// ```
392+
//
393+
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
394+
// to preserve drop semantics since `while $cond { ... }` does not
395+
// let temporaries live outside of `cond`.
480396
fn lower_expr_while_in_loop_scope(
481397
&mut self,
482398
span: Span,
483399
cond: &Expr,
484400
body: &Block,
485401
opt_label: Option<Label>,
486402
) -> hir::ExprKind<'hir> {
487-
// FIXME(#53667): handle lowering of && and parens.
488-
489-
// Note that the block AND the condition are evaluated in the loop scope.
490-
// This is done to allow `break` from inside the condition of the loop.
491-
492-
// `_ => break`:
493-
let else_arm = {
494-
let else_pat = self.pat_wild(span);
495-
let else_expr = self.expr_break(span, ThinVec::new());
496-
self.arm(else_pat, else_expr)
497-
};
498-
499-
// Handle then + scrutinee:
500-
let (then_pat, scrutinee, desugar, source) = match cond.kind {
501-
ExprKind::Let(ref pat, ref scrutinee) => {
502-
// to:
503-
//
504-
// [opt_ident]: loop {
505-
// match <sub_expr> {
506-
// <pat> => <body>,
507-
// _ => break
508-
// }
509-
// }
510-
let scrutinee = self.with_loop_condition_scope(|t| t.lower_expr(scrutinee));
511-
let pat = self.lower_pat(pat);
512-
(pat, scrutinee, hir::MatchSource::WhileLetDesugar, hir::LoopSource::WhileLet)
513-
}
514-
_ => {
515-
// We desugar: `'label: while $cond $body` into:
516-
//
517-
// ```
518-
// 'label: loop {
519-
// match drop-temps { $cond } {
520-
// true => $body,
521-
// _ => break,
522-
// }
523-
// }
524-
// ```
525-
526-
// Lower condition:
527-
let cond = self.with_loop_condition_scope(|this| this.lower_expr(cond));
528-
let span_block =
529-
self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None);
530-
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
531-
// to preserve drop semantics since `while cond { ... }` does not
532-
// let temporaries live outside of `cond`.
533-
let cond = self.expr_drop_temps(span_block, cond, ThinVec::new());
534-
// `true => <then>`:
535-
let pat = self.pat_bool(span, true);
536-
(pat, cond, hir::MatchSource::WhileDesugar, hir::LoopSource::While)
537-
}
538-
};
539-
let then_expr = self.lower_block_expr(body);
540-
let then_arm = self.arm(then_pat, self.arena.alloc(then_expr));
541-
542-
// `match <scrutinee> { ... }`
543-
let match_expr =
544-
self.expr_match(span, scrutinee, arena_vec![self; then_arm, else_arm], desugar);
545-
546-
// `[opt_ident]: loop { ... }`
547-
hir::ExprKind::Loop(
548-
self.block_expr(self.arena.alloc(match_expr)),
549-
opt_label,
550-
source,
551-
span.with_hi(cond.span.hi()),
552-
)
403+
let lowered_cond = self.with_loop_condition_scope(|t| t.lower_expr(cond));
404+
let new_cond = self.manage_let_cond(lowered_cond);
405+
let then = self.lower_block_expr(body);
406+
let expr_break = self.expr_break(span, ThinVec::new());
407+
let stmt_break = self.stmt_expr(span, expr_break);
408+
let else_blk = self.block_all(span, arena_vec![self; stmt_break], None);
409+
let else_expr = self.arena.alloc(self.expr_block(else_blk, ThinVec::new()));
410+
let if_kind = hir::ExprKind::If(new_cond, self.arena.alloc(then), Some(else_expr));
411+
let if_expr = self.expr(span, if_kind, ThinVec::new());
412+
let block = self.block_expr(self.arena.alloc(if_expr));
413+
hir::ExprKind::Loop(block, opt_label, hir::LoopSource::While, span.with_hi(cond.span.hi()))
553414
}
554415

555416
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_output(<expr>) }`,
@@ -609,7 +470,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
609470
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
610471
let pat = self.lower_pat(&arm.pat);
611472
let guard = arm.guard.as_ref().map(|cond| {
612-
if let ExprKind::Let(ref pat, ref scrutinee) = cond.kind {
473+
if let ExprKind::Let(ref pat, ref scrutinee, _) = cond.kind {
613474
hir::Guard::IfLet(self.lower_pat(pat), self.lower_expr(scrutinee))
614475
} else {
615476
hir::Guard::If(self.lower_expr(cond))
@@ -1457,7 +1318,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
14571318
// `::std::option::Option::None => break`
14581319
let break_arm = {
14591320
let break_expr =
1460-
self.with_loop_scope(e.id, |this| this.expr_break(e.span, ThinVec::new()));
1321+
self.with_loop_scope(e.id, |this| this.expr_break_alloc(e.span, ThinVec::new()));
14611322
let pat = self.pat_none(e.span);
14621323
self.arm(pat, break_expr)
14631324
};
@@ -1670,12 +1531,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
16701531
// Helper methods for building HIR.
16711532
// =========================================================================
16721533

1673-
/// Constructs a `true` or `false` literal expression.
1674-
pub(super) fn expr_bool(&mut self, span: Span, val: bool) -> &'hir hir::Expr<'hir> {
1675-
let lit = Spanned { span, node: LitKind::Bool(val) };
1676-
self.arena.alloc(self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new()))
1677-
}
1678-
16791534
/// Wrap the given `expr` in a terminating scope using `hir::ExprKind::DropTemps`.
16801535
///
16811536
/// In terms of drop order, it has the same effect as wrapping `expr` in
@@ -1710,9 +1565,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
17101565
self.expr(span, hir::ExprKind::Match(arg, arms, source), ThinVec::new())
17111566
}
17121567

1713-
fn expr_break(&mut self, span: Span, attrs: AttrVec) -> &'hir hir::Expr<'hir> {
1568+
fn expr_break(&mut self, span: Span, attrs: AttrVec) -> hir::Expr<'hir> {
17141569
let expr_break = hir::ExprKind::Break(self.lower_loop_destination(None), None);
1715-
self.arena.alloc(self.expr(span, expr_break, attrs))
1570+
self.expr(span, expr_break, attrs)
1571+
}
1572+
1573+
fn expr_break_alloc(&mut self, span: Span, attrs: AttrVec) -> &'hir hir::Expr<'hir> {
1574+
let expr_break = self.expr_break(span, attrs);
1575+
self.arena.alloc(expr_break)
17161576
}
17171577

17181578
fn expr_mut_addr_of(&mut self, span: Span, e: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {

compiler/rustc_ast_lowering/src/lib.rs

-10
Original file line numberDiff line numberDiff line change
@@ -2537,12 +2537,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
25372537
self.arena.alloc(blk)
25382538
}
25392539

2540-
/// Constructs a `true` or `false` literal pattern.
2541-
fn pat_bool(&mut self, span: Span, val: bool) -> &'hir hir::Pat<'hir> {
2542-
let expr = self.expr_bool(span, val);
2543-
self.pat(span, hir::PatKind::Lit(expr))
2544-
}
2545-
25462540
fn pat_cf_continue(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
25472541
let field = self.single_pat_field(span, pat);
25482542
self.pat_lang_item_variant(span, hir::LangItem::ControlFlowContinue, field)
@@ -2624,10 +2618,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
26242618
)
26252619
}
26262620

2627-
fn pat_wild(&mut self, span: Span) -> &'hir hir::Pat<'hir> {
2628-
self.pat(span, hir::PatKind::Wild)
2629-
}
2630-
26312621
fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
26322622
self.arena.alloc(hir::Pat {
26332623
hir_id: self.next_id(),

0 commit comments

Comments
 (0)