Skip to content

Commit 6aa9937

Browse files
committed
Introduce hir::ExprKind::Let - Take 2
1 parent 2d9f2ea commit 6aa9937

File tree

128 files changed

+2054
-2170
lines changed

Some content is hidden

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

128 files changed

+2054
-2170
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

+56-207
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,199 +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 wrapped_cond = match cond.kind {
447-
hir::ExprKind::Let(..) => cond,
448-
_ => self.expr_drop_temps(cond.span, cond, AttrVec::new()),
449-
};
357+
let lowered_cond = self.lower_expr(cond);
358+
let new_cond = self.manage_let_cond(lowered_cond);
450359
let then_expr = self.lower_block_expr(then);
451360
if let Some(rslt) = else_opt {
452-
hir::ExprKind::If(
453-
wrapped_cond,
454-
self.arena.alloc(then_expr),
455-
Some(self.lower_expr(rslt)),
456-
)
361+
hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), Some(self.lower_expr(rslt)))
457362
} else {
458-
hir::ExprKind::If(wrapped_cond, self.arena.alloc(then_expr), None)
363+
hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), None)
459364
}
460365
}
461366

462-
fn lower_expr_if_let(
463-
&mut self,
464-
span: Span,
465-
pat: &Pat,
466-
scrutinee: &Expr,
467-
then: &Block,
468-
else_opt: Option<&Expr>,
469-
) -> hir::ExprKind<'hir> {
470-
// FIXME(#53667): handle lowering of && and parens.
471-
472-
// `_ => else_block` where `else_block` is `{}` if there's `None`:
473-
let else_pat = self.pat_wild(span);
474-
let (else_expr, contains_else_clause) = match else_opt {
475-
None => (self.expr_block_empty(span.shrink_to_hi()), false),
476-
Some(els) => (self.lower_expr(els), true),
477-
};
478-
let else_arm = self.arm(else_pat, else_expr);
479-
480-
// Handle then + scrutinee:
481-
let scrutinee = self.lower_expr(scrutinee);
482-
let then_pat = self.lower_pat(pat);
483-
484-
let then_expr = self.lower_block_expr(then);
485-
let then_arm = self.arm(then_pat, self.arena.alloc(then_expr));
486-
487-
let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause };
488-
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+
}
489378
}
490379

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`.
491396
fn lower_expr_while_in_loop_scope(
492397
&mut self,
493398
span: Span,
494399
cond: &Expr,
495400
body: &Block,
496401
opt_label: Option<Label>,
497402
) -> hir::ExprKind<'hir> {
498-
// FIXME(#53667): handle lowering of && and parens.
499-
500-
// Note that the block AND the condition are evaluated in the loop scope.
501-
// This is done to allow `break` from inside the condition of the loop.
502-
503-
// `_ => break`:
504-
let else_arm = {
505-
let else_pat = self.pat_wild(span);
506-
let else_expr = self.expr_break(span, ThinVec::new());
507-
self.arm(else_pat, else_expr)
508-
};
509-
510-
// Handle then + scrutinee:
511-
let (then_pat, scrutinee, desugar, source) = match cond.kind {
512-
ExprKind::Let(ref pat, ref scrutinee) => {
513-
// to:
514-
//
515-
// [opt_ident]: loop {
516-
// match <sub_expr> {
517-
// <pat> => <body>,
518-
// _ => break
519-
// }
520-
// }
521-
let scrutinee = self.with_loop_condition_scope(|t| t.lower_expr(scrutinee));
522-
let pat = self.lower_pat(pat);
523-
(pat, scrutinee, hir::MatchSource::WhileLetDesugar, hir::LoopSource::WhileLet)
524-
}
525-
_ => {
526-
// We desugar: `'label: while $cond $body` into:
527-
//
528-
// ```
529-
// 'label: loop {
530-
// match drop-temps { $cond } {
531-
// true => $body,
532-
// _ => break,
533-
// }
534-
// }
535-
// ```
536-
537-
// Lower condition:
538-
let cond = self.with_loop_condition_scope(|this| this.lower_expr(cond));
539-
let span_block =
540-
self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None);
541-
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
542-
// to preserve drop semantics since `while cond { ... }` does not
543-
// let temporaries live outside of `cond`.
544-
let cond = self.expr_drop_temps(span_block, cond, ThinVec::new());
545-
// `true => <then>`:
546-
let pat = self.pat_bool(span, true);
547-
(pat, cond, hir::MatchSource::WhileDesugar, hir::LoopSource::While)
548-
}
549-
};
550-
let then_expr = self.lower_block_expr(body);
551-
let then_arm = self.arm(then_pat, self.arena.alloc(then_expr));
552-
553-
// `match <scrutinee> { ... }`
554-
let match_expr =
555-
self.expr_match(span, scrutinee, arena_vec![self; then_arm, else_arm], desugar);
556-
557-
// `[opt_ident]: loop { ... }`
558-
hir::ExprKind::Loop(
559-
self.block_expr(self.arena.alloc(match_expr)),
560-
opt_label,
561-
source,
562-
span.with_hi(cond.span.hi()),
563-
)
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()))
564414
}
565415

566416
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_output(<expr>) }`,
@@ -620,7 +470,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
620470
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
621471
let pat = self.lower_pat(&arm.pat);
622472
let guard = arm.guard.as_ref().map(|cond| {
623-
if let ExprKind::Let(ref pat, ref scrutinee) = cond.kind {
473+
if let ExprKind::Let(ref pat, ref scrutinee, _) = cond.kind {
624474
hir::Guard::IfLet(self.lower_pat(pat), self.lower_expr(scrutinee))
625475
} else {
626476
hir::Guard::If(self.lower_expr(cond))
@@ -1468,7 +1318,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
14681318
// `::std::option::Option::None => break`
14691319
let break_arm = {
14701320
let break_expr =
1471-
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()));
14721322
let pat = self.pat_none(e.span);
14731323
self.arm(pat, break_expr)
14741324
};
@@ -1681,12 +1531,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
16811531
// Helper methods for building HIR.
16821532
// =========================================================================
16831533

1684-
/// Constructs a `true` or `false` literal expression.
1685-
pub(super) fn expr_bool(&mut self, span: Span, val: bool) -> &'hir hir::Expr<'hir> {
1686-
let lit = Spanned { span, node: LitKind::Bool(val) };
1687-
self.arena.alloc(self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new()))
1688-
}
1689-
16901534
/// Wrap the given `expr` in a terminating scope using `hir::ExprKind::DropTemps`.
16911535
///
16921536
/// In terms of drop order, it has the same effect as wrapping `expr` in
@@ -1721,9 +1565,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
17211565
self.expr(span, hir::ExprKind::Match(arg, arms, source), ThinVec::new())
17221566
}
17231567

1724-
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> {
17251569
let expr_break = hir::ExprKind::Break(self.lower_loop_destination(None), None);
1726-
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)
17271576
}
17281577

17291578
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)