Skip to content

Commit 2017be4

Browse files
committed
let_chains: Remove ast_validation logic in favor of lowering with recovery.
1 parent 10234d2 commit 2017be4

File tree

2 files changed

+49
-67
lines changed

2 files changed

+49
-67
lines changed

src/librustc/hir/lowering.rs

+40-8
Original file line numberDiff line numberDiff line change
@@ -4344,12 +4344,39 @@ impl<'a> LoweringContext<'a> {
43444344
let ohs = P(self.lower_expr(ohs));
43454345
hir::ExprKind::AddrOf(m, ohs)
43464346
}
4347-
ExprKind::Let(..) => {
4348-
// This should have been caught `ast_validation`!
4349-
self.sess.span_err(e.span, "`let` expressions only supported in `if`");
4350-
// ^-- FIXME(53667): Change to `delay_span_bug` when let_chains handled in lowering.
4351-
self.sess.abort_if_errors();
4352-
hir::ExprKind::Err
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+
)
43534380
}
43544381
// FIXME(#53667): handle lowering of && and parens.
43554382
ExprKind::If(ref cond, ref then, ref else_opt) => {
@@ -5431,10 +5458,15 @@ impl<'a> LoweringContext<'a> {
54315458
)
54325459
}
54335460

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+
54345467
/// Constructs a `true` or `false` literal pattern.
54355468
fn pat_bool(&mut self, span: Span, val: bool) -> P<hir::Pat> {
5436-
let lit = Spanned { span, node: LitKind::Bool(val) };
5437-
let expr = self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new());
5469+
let expr = self.expr_bool(span, val);
54385470
self.pat(span, hir::PatKind::Lit(P(expr)))
54395471
}
54405472

src/librustc_passes/ast_validation.rs

+9-59
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,6 @@ struct AstValidator<'a> {
7474
/// these booleans.
7575
warning_period_57979_didnt_record_next_impl_trait: bool,
7676
warning_period_57979_impl_trait_in_proj: bool,
77-
78-
/// Used to ban `let` expressions in inappropriate places.
79-
is_let_allowed: bool,
8077
}
8178

8279
/// With the `new` value in `store`,
@@ -114,12 +111,6 @@ impl<'a> AstValidator<'a> {
114111
with(self, outer, |this| &mut this.outer_impl_trait, f)
115112
}
116113

117-
fn with_let_allowed(&mut self, v: bool, f: impl FnOnce(&mut Self, bool)) {
118-
let old = mem::replace(&mut self.is_let_allowed, v);
119-
f(self, old);
120-
self.is_let_allowed = old;
121-
}
122-
123114
fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) {
124115
match constraint.kind {
125116
AssocTyConstraintKind::Equality { ref ty } => {
@@ -335,15 +326,6 @@ impl<'a> AstValidator<'a> {
335326
}
336327
}
337328

338-
/// Emits an error banning the `let` expression provided.
339-
fn ban_let_expr(&self, expr: &'a Expr) {
340-
self.err_handler()
341-
.struct_span_err(expr.span, "`let` expressions are not supported here")
342-
.note("only supported directly in conditions of `if`- and `while`-expressions")
343-
.note("as well as when nested within `&&` and parenthesis in those conditions")
344-
.emit();
345-
}
346-
347329
fn check_fn_decl(&self, fn_decl: &FnDecl) {
348330
fn_decl
349331
.inputs
@@ -470,48 +452,17 @@ fn validate_generics_order<'a>(
470452

471453
impl<'a> Visitor<'a> for AstValidator<'a> {
472454
fn visit_expr(&mut self, expr: &'a Expr) {
473-
self.with_let_allowed(false, |this, let_allowed| {
474-
match &expr.node {
475-
ExprKind::Let(_, _) if !let_allowed => {
476-
this.ban_let_expr(expr);
477-
}
478-
// Assuming the context permits, `($expr)` does not impose additional constraints.
479-
ExprKind::Paren(_) => {
480-
this.with_let_allowed(let_allowed, |this, _| visit::walk_expr(this, expr));
481-
return; // We've already walked into `expr`.
482-
}
483-
// Assuming the context permits,
484-
// l && r` allows decendants in `l` and `r` to be `let` expressions.
485-
ExprKind::Binary(op, ..) if op.node == BinOpKind::And => {
486-
this.with_let_allowed(let_allowed, |this, _| visit::walk_expr(this, expr));
487-
return; // We've already walked into `expr`.
488-
}
489-
// However, we do allow it in the condition of the `if` expression.
490-
// We do not allow `let` in `then` and `opt_else` directly.
491-
ExprKind::If(cond, then, opt_else) => {
492-
this.visit_block(then);
493-
walk_list!(this, visit_expr, opt_else);
494-
this.with_let_allowed(true, |this, _| this.visit_expr(cond));
495-
return; // We've already walked into `expr`.
496-
}
497-
// The same logic applies to `While`.
498-
ExprKind::While(cond, then, opt_label) => {
499-
walk_list!(this, visit_label, opt_label);
500-
this.visit_block(then);
501-
this.with_let_allowed(true, |this, _| this.visit_expr(cond));
502-
return; // We've already walked into `expr`.
503-
}
504-
ExprKind::Closure(_, _, _, fn_decl, _, _) => {
505-
this.check_fn_decl(fn_decl);
506-
}
507-
ExprKind::InlineAsm(..) if !this.session.target.target.options.allow_asm => {
508-
span_err!(this.session, expr.span, E0472, "asm! is unsupported on this target");
509-
}
510-
_ => {}
455+
match &expr.node {
456+
ExprKind::Closure(_, _, _, fn_decl, _, _) => {
457+
self.check_fn_decl(fn_decl);
511458
}
459+
ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => {
460+
span_err!(self.session, expr.span, E0472, "asm! is unsupported on this target");
461+
}
462+
_ => {}
463+
}
512464

513-
visit::walk_expr(this, expr);
514-
});
465+
visit::walk_expr(self, expr);
515466
}
516467

517468
fn visit_ty(&mut self, ty: &'a Ty) {
@@ -923,7 +874,6 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
923874
is_assoc_ty_bound_banned: false,
924875
warning_period_57979_didnt_record_next_impl_trait: false,
925876
warning_period_57979_impl_trait_in_proj: false,
926-
is_let_allowed: false,
927877
};
928878
visit::walk_crate(&mut validator, krate);
929879

0 commit comments

Comments
 (0)