diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 970f6ba017735..4f941cb7f3d33 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -4121,56 +4121,123 @@ impl<'a> LoweringContext<'a> { let ohs = P(self.lower_expr(ohs)); hir::ExprKind::AddrOf(m, ohs) } - // More complicated than you might expect because the else branch - // might be `if let`. + ExprKind::Let(..) => { + // This should have been caught `ast_validation`! + self.sess.span_err(e.span, "`let` expressions only supported in `if`"); + // ^-- FIXME(53667): Change to `delay_span_bug` when let_chains handled in lowering. + self.sess.abort_if_errors(); + hir::ExprKind::Err + } + // FIXME(#53667): handle lowering of && and parens. ExprKind::If(ref cond, ref then, ref else_opt) => { - // `true => then`: - let then_pat = self.pat_bool(e.span, true); - let then_blk = self.lower_block(then, false); - let then_expr = self.expr_block(then_blk, ThinVec::new()); - let then_arm = self.arm(hir_vec![then_pat], P(then_expr)); - // `_ => else_block` where `else_block` is `{}` if there's `None`: let else_pat = self.pat_wild(e.span); - let else_expr = match else_opt { - None => self.expr_block_empty(e.span), - Some(els) => match els.node { - ExprKind::IfLet(..) => { - // Wrap the `if let` expr in a block. - let els = self.lower_expr(els); - let blk = self.block_all(els.span, hir_vec![], Some(P(els))); - self.expr_block(P(blk), ThinVec::new()) - } - _ => self.lower_expr(els), - } + let (else_expr, contains_else_clause) = match else_opt { + None => (self.expr_block_empty(e.span), false), + Some(els) => (self.lower_expr(els), true), }; let else_arm = self.arm(hir_vec![else_pat], P(else_expr)); - // Lower condition: - let span_block = self - .sess - .source_map() - .mark_span_with_reason(IfTemporary, cond.span, None); - let cond = self.lower_expr(cond); - // Wrap in a construct equivalent to `{ let _t = $cond; _t }` to preserve drop - // semantics since `if cond { ... }` don't let temporaries live outside of `cond`. - let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new()); + // Handle then + scrutinee: + let then_blk = self.lower_block(then, false); + let then_expr = self.expr_block(then_blk, ThinVec::new()); + let (then_pats, scrutinee, desugar) = match cond.node { + // ` => ` + ExprKind::Let(ref pats, ref scrutinee) => { + let scrutinee = self.lower_expr(scrutinee); + let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect(); + let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause }; + (pats, scrutinee, desugar) + } + // `true => then`: + _ => { + // Lower condition: + let cond = self.lower_expr(cond); + // Wrap in a construct equivalent to `{ let _t = $cond; _t }` + // to preserve drop semantics since `if cond { ... }` + // don't let temporaries live outside of `cond`. + let span_block = self + .sess + .source_map() + .mark_span_with_reason(IfTemporary, cond.span, None); + // Wrap in a construct equivalent to `{ let _t = $cond; _t }` + // to preserve drop semantics since `if cond { ... }` does not + // let temporaries live outside of `cond`. + let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new()); + + let desugar = hir::MatchSource::IfDesugar { contains_else_clause }; + let pats = hir_vec![self.pat_bool(e.span, true)]; + (pats, cond, desugar) + } + }; + let then_arm = self.arm(then_pats, P(then_expr)); - hir::ExprKind::Match( - P(cond), - vec![then_arm, else_arm].into(), - hir::MatchSource::IfDesugar { - contains_else_clause: else_opt.is_some() - }, - ) + hir::ExprKind::Match(P(scrutinee), vec![then_arm, else_arm].into(), desugar) + } + // FIXME(#53667): handle lowering of && and parens. + ExprKind::While(ref cond, ref body, opt_label) => { + // Desugar `ExprWhileLet` + // from: `[opt_ident]: while let = ` + if let ExprKind::Let(ref pats, ref sub_expr) = cond.node { + // to: + // + // [opt_ident]: loop { + // match { + // => , + // _ => break + // } + // } + + // Note that the block AND the condition are evaluated in the loop scope. + // This is done to allow `break` from inside the condition of the loop. + let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| { + ( + this.lower_block(body, false), + this.expr_break(e.span, ThinVec::new()), + this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))), + ) + }); + + // ` => ` + let pat_arm = { + let body_expr = P(self.expr_block(body, ThinVec::new())); + let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect(); + self.arm(pats, body_expr) + }; + + // `_ => break` + let break_arm = { + let pat_under = self.pat_wild(e.span); + self.arm(hir_vec![pat_under], break_expr) + }; + + // `match { ... }` + let arms = hir_vec![pat_arm, break_arm]; + let match_expr = self.expr( + sub_expr.span, + hir::ExprKind::Match(sub_expr, arms, hir::MatchSource::WhileLetDesugar), + ThinVec::new(), + ); + + // `[opt_ident]: loop { ... }` + let loop_block = P(self.block_expr(P(match_expr))); + let loop_expr = hir::ExprKind::Loop( + loop_block, + self.lower_label(opt_label), + hir::LoopSource::WhileLet, + ); + // Add attributes to the outer returned expr node. + loop_expr + } else { + self.with_loop_scope(e.id, |this| { + hir::ExprKind::While( + this.with_loop_condition_scope(|this| P(this.lower_expr(cond))), + this.lower_block(body, false), + this.lower_label(opt_label), + ) + }) + } } - ExprKind::While(ref cond, ref body, opt_label) => self.with_loop_scope(e.id, |this| { - hir::ExprKind::While( - this.with_loop_condition_scope(|this| P(this.lower_expr(cond))), - this.lower_block(body, false), - this.lower_label(opt_label), - ) - }), ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| { hir::ExprKind::Loop( this.lower_block(body, false), @@ -4490,105 +4557,6 @@ impl<'a> LoweringContext<'a> { ExprKind::Err => hir::ExprKind::Err, - // Desugar `ExprIfLet` - // from: `if let = []` - ExprKind::IfLet(ref pats, ref sub_expr, ref body, ref else_opt) => { - // to: - // - // match { - // => , - // _ => [ | ()] - // } - - let mut arms = vec![]; - - // ` => ` - { - let body = self.lower_block(body, false); - let body_expr = P(self.expr_block(body, ThinVec::new())); - let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect(); - arms.push(self.arm(pats, body_expr)); - } - - // _ => [|{}] - { - let wildcard_arm: Option<&Expr> = else_opt.as_ref().map(|p| &**p); - let wildcard_pattern = self.pat_wild(e.span); - let body = if let Some(else_expr) = wildcard_arm { - self.lower_expr(else_expr) - } else { - self.expr_block_empty(e.span) - }; - arms.push(self.arm(hir_vec![wildcard_pattern], P(body))); - } - - let contains_else_clause = else_opt.is_some(); - - let sub_expr = P(self.lower_expr(sub_expr)); - - hir::ExprKind::Match( - sub_expr, - arms.into(), - hir::MatchSource::IfLetDesugar { - contains_else_clause, - }, - ) - } - - // Desugar `ExprWhileLet` - // from: `[opt_ident]: while let = ` - ExprKind::WhileLet(ref pats, ref sub_expr, ref body, opt_label) => { - // to: - // - // [opt_ident]: loop { - // match { - // => , - // _ => break - // } - // } - - // Note that the block AND the condition are evaluated in the loop scope. - // This is done to allow `break` from inside the condition of the loop. - let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| { - ( - this.lower_block(body, false), - this.expr_break(e.span, ThinVec::new()), - this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))), - ) - }); - - // ` => ` - let pat_arm = { - let body_expr = P(self.expr_block(body, ThinVec::new())); - let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect(); - self.arm(pats, body_expr) - }; - - // `_ => break` - let break_arm = { - let pat_under = self.pat_wild(e.span); - self.arm(hir_vec![pat_under], break_expr) - }; - - // `match { ... }` - let arms = hir_vec![pat_arm, break_arm]; - let match_expr = self.expr( - sub_expr.span, - hir::ExprKind::Match(sub_expr, arms, hir::MatchSource::WhileLetDesugar), - ThinVec::new(), - ); - - // `[opt_ident]: loop { ... }` - let loop_block = P(self.block_expr(P(match_expr))); - let loop_expr = hir::ExprKind::Loop( - loop_block, - self.lower_label(opt_label), - hir::LoopSource::WhileLet, - ); - // Add attributes to the outer returned expr node. - loop_expr - } - // Desugar `ExprForLoop` // from: `[opt_ident]: for in ` ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => { diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index c3dfd44ad8572..7975a1042d45a 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -294,20 +294,28 @@ impl UnusedParens { value: &ast::Expr, msg: &str, followed_by_block: bool) { - if let ast::ExprKind::Paren(ref inner) = value.node { - let necessary = followed_by_block && match inner.node { - ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true, - _ => parser::contains_exterior_struct_lit(&inner), - }; - if !necessary { - let expr_text = if let Ok(snippet) = cx.sess().source_map() - .span_to_snippet(value.span) { - snippet - } else { - pprust::expr_to_string(value) - }; - Self::remove_outer_parens(cx, value.span, &expr_text, msg); + match value.node { + ast::ExprKind::Paren(ref inner) => { + let necessary = followed_by_block && match inner.node { + ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true, + _ => parser::contains_exterior_struct_lit(&inner), + }; + if !necessary { + let expr_text = if let Ok(snippet) = cx.sess().source_map() + .span_to_snippet(value.span) { + snippet + } else { + pprust::expr_to_string(value) + }; + Self::remove_outer_parens(cx, value.span, &expr_text, msg); + } + } + ast::ExprKind::Let(_, ref expr) => { + // FIXME(#60336): Properly handle `let true = (false && true)` + // actually needing the parenthesis. + self.check_unused_parens_expr(cx, expr, "`let` head expression", followed_by_block); } + _ => {} } } @@ -369,8 +377,6 @@ impl EarlyLintPass for UnusedParens { let (value, msg, followed_by_block) = match e.node { If(ref cond, ..) => (cond, "`if` condition", true), While(ref cond, ..) => (cond, "`while` condition", true), - IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true), - WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true), ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true), Match(ref head, _) => (head, "`match` head expression", true), Ret(Some(ref value)) => (value, "`return` value", false), diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 2bea1db841ae9..844763d6c13eb 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -16,13 +16,11 @@ use syntax::ast::*; use syntax::attr; use syntax::source_map::Spanned; use syntax::symbol::{keywords, sym}; -use syntax::ptr::P; use syntax::visit::{self, Visitor}; use syntax::{span_err, struct_span_err, walk_list}; use syntax_ext::proc_macro_decls::is_proc_macro_attr; use syntax_pos::{Span, MultiSpan}; use errors::{Applicability, FatalError}; -use log::debug; #[derive(Copy, Clone, Debug)] struct OuterImplTrait { @@ -71,26 +69,44 @@ struct AstValidator<'a> { /// these booleans. warning_period_57979_didnt_record_next_impl_trait: bool, warning_period_57979_impl_trait_in_proj: bool, + + /// Used to ban `let` expressions in inappropriate places. + is_let_allowed: bool, +} + +/// With the `new` value in `store`, +/// runs and returns the `scoped` computation, +/// resetting the old value of `store` after, +/// and returning the result of `scoped`. +fn with( + this: &mut C, + new: S, + store: impl Fn(&mut C) -> &mut S, + scoped: impl FnOnce(&mut C) -> T +) -> T { + let old = mem::replace(store(this), new); + let ret = scoped(this); + *store(this) = old; + ret } impl<'a> AstValidator<'a> { fn with_impl_trait_in_proj_warning(&mut self, v: bool, f: impl FnOnce(&mut Self) -> T) -> T { - let old = mem::replace(&mut self.warning_period_57979_impl_trait_in_proj, v); - let ret = f(self); - self.warning_period_57979_impl_trait_in_proj = old; - ret + with(self, v, |this| &mut this.warning_period_57979_impl_trait_in_proj, f) } fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) { - let old = mem::replace(&mut self.is_impl_trait_banned, true); - f(self); - self.is_impl_trait_banned = old; + with(self, true, |this| &mut this.is_impl_trait_banned, f) } fn with_impl_trait(&mut self, outer: Option, f: impl FnOnce(&mut Self)) { - let old = mem::replace(&mut self.outer_impl_trait, outer); - f(self); - self.outer_impl_trait = old; + with(self, outer, |this| &mut this.outer_impl_trait, f) + } + + fn with_let_allowed(&mut self, v: bool, f: impl FnOnce(&mut Self, bool)) { + let old = mem::replace(&mut self.is_let_allowed, v); + f(self, old); + self.is_let_allowed = old; } fn visit_assoc_type_binding_from_generic_args(&mut self, type_binding: &'a TypeBinding) { @@ -297,52 +313,71 @@ impl<'a> AstValidator<'a> { } } - /// With eRFC 2497, we need to check whether an expression is ambiguous and warn or error - /// depending on the edition, this function handles that. - fn while_if_let_ambiguity(&self, expr: &P) { - if let Some((span, op_kind)) = self.while_if_let_expr_ambiguity(&expr) { - let mut err = self.err_handler().struct_span_err( - span, &format!("ambiguous use of `{}`", op_kind.to_string()) - ); - - err.note( - "this will be a error until the `let_chains` feature is stabilized" - ); - err.note( - "see rust-lang/rust#53668 for more information" - ); - - if let Ok(snippet) = self.session.source_map().span_to_snippet(span) { + fn obsolete_in_place(&self, expr: &Expr, place: &Expr, val: &Expr) { + let mut err = self.err_handler().struct_span_err( + expr.span, + "emplacement syntax is obsolete (for now, anyway)", + ); + err.note( + "for more information, see \ + " + ); + match val.node { + ExprKind::Lit(ref v) if v.node.is_numeric() => { err.span_suggestion( - span, "consider adding parentheses", format!("({})", snippet), - Applicability::MachineApplicable, + place.span.between(val.span), + "if you meant to write a comparison against a negative value, add a \ + space in between `<` and `-`", + "< -".to_string(), + Applicability::MaybeIncorrect ); } - - err.emit(); + _ => {} } + err.emit(); } - /// With eRFC 2497 adding if-let chains, there is a requirement that the parsing of - /// `&&` and `||` in a if-let statement be unambiguous. This function returns a span and - /// a `BinOpKind` (either `&&` or `||` depending on what was ambiguous) if it is determined - /// that the current expression parsed is ambiguous and will break in future. - fn while_if_let_expr_ambiguity(&self, expr: &P) -> Option<(Span, BinOpKind)> { - debug!("while_if_let_expr_ambiguity: expr.node: {:?}", expr.node); + /// Visits the `expr` and adjusts whether `let $pat = $expr` is allowed in decendants. + /// Returns whether we walked into `expr` or not. + /// If we did, walking should not happen again. + fn visit_expr_with_let_maybe_allowed(&mut self, expr: &'a Expr, let_allowed: bool) -> bool { match &expr.node { - ExprKind::Binary(op, _, _) if op.node == BinOpKind::And || op.node == BinOpKind::Or => { - Some((expr.span, op.node)) - }, - ExprKind::Range(ref lhs, ref rhs, _) => { - let lhs_ambiguous = lhs.as_ref() - .and_then(|lhs| self.while_if_let_expr_ambiguity(lhs)); - let rhs_ambiguous = rhs.as_ref() - .and_then(|rhs| self.while_if_let_expr_ambiguity(rhs)); - - lhs_ambiguous.or(rhs_ambiguous) + // Assuming the context permits, `($expr)` does not impose additional constraints. + ExprKind::Paren(_) => { + self.with_let_allowed(let_allowed, |this, _| visit::walk_expr(this, expr)); } - _ => None, + // Assuming the context permits, + // l && r` allows decendants in `l` and `r` to be `let` expressions. + ExprKind::Binary(op, ..) if op.node == BinOpKind::And => { + self.with_let_allowed(let_allowed, |this, _| visit::walk_expr(this, expr)); + } + // However, we do allow it in the condition of the `if` expression. + // We do not allow `let` in `then` and `opt_else` directly. + ExprKind::If(cond, then, opt_else) => { + self.visit_block(then); + walk_list!(self, visit_expr, opt_else); + self.with_let_allowed(true, |this, _| this.visit_expr(cond)); + } + // The same logic applies to `While`. + ExprKind::While(cond, then, opt_label) => { + walk_list!(self, visit_label, opt_label); + self.visit_block(then); + self.with_let_allowed(true, |this, _| this.visit_expr(cond)); + } + // Don't walk into `expr` and defer further checks to the caller. + _ => return false, } + + true + } + + /// Emits an error banning the `let` expression provided. + fn ban_let_expr(&self, expr: &'a Expr) { + self.err_handler() + .struct_span_err(expr.span, "`let` expressions are not supported here") + .note("only supported directly in conditions of `if`- and `while`-expressions") + .note("as well as when nested within `&&` and parenthesis in those conditions") + .emit(); } } @@ -448,39 +483,26 @@ fn validate_generics_order<'a>( impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_expr(&mut self, expr: &'a Expr) { - match expr.node { - ExprKind::IfLet(_, ref expr, _, _) | ExprKind::WhileLet(_, ref expr, _, _) => - self.while_if_let_ambiguity(&expr), - ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => { - span_err!(self.session, expr.span, E0472, "asm! is unsupported on this target"); - } - ExprKind::ObsoleteInPlace(ref place, ref val) => { - let mut err = self.err_handler().struct_span_err( - expr.span, - "emplacement syntax is obsolete (for now, anyway)", - ); - err.note( - "for more information, see \ - " - ); - match val.node { - ExprKind::Lit(ref v) if v.node.is_numeric() => { - err.span_suggestion( - place.span.between(val.span), - "if you meant to write a comparison against a negative value, add a \ - space in between `<` and `-`", - "< -".to_string(), - Applicability::MaybeIncorrect - ); - } - _ => {} + self.with_let_allowed(false, |this, let_allowed| { + match &expr.node { + ExprKind::Let(_, _) if !let_allowed => { + this.ban_let_expr(expr); } - err.emit(); + _ if this.visit_expr_with_let_maybe_allowed(&expr, let_allowed) => { + // Prevent `walk_expr` to happen since we've already done that. + return; + } + ExprKind::InlineAsm(..) if !this.session.target.target.options.allow_asm => { + span_err!(this.session, expr.span, E0472, "asm! is unsupported on this target"); + } + ExprKind::ObsoleteInPlace(place, val) => { + this.obsolete_in_place(expr, place, val); + } + _ => {} } - _ => {} - } - visit::walk_expr(self, expr) + visit::walk_expr(this, expr); + }); } fn visit_ty(&mut self, ty: &'a Ty) { @@ -862,6 +884,7 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) { is_impl_trait_banned: false, warning_period_57979_didnt_record_next_impl_trait: false, warning_period_57979_impl_trait_in_proj: false, + is_let_allowed: false, }; visit::walk_crate(&mut validator, krate); diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 7c48feecb2110..0a13f27737a25 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -7,6 +7,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(nll)] +#![feature(bind_by_move_pattern_guards)] #![feature(rustc_diagnostic_macros)] #![recursion_limit="256"] diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 77e8cc3272cc3..403bb9eec2980 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -484,8 +484,8 @@ type BindingMap = FxHashMap; #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum PatternSource { Match, - IfLet, - WhileLet, + // FIXME(54883): Consider fusing with `Let` below once let-statements support or-patterns. + LetExpr, Let, For, FnParam, @@ -495,9 +495,7 @@ impl PatternSource { fn descr(self) -> &'static str { match self { PatternSource::Match => "match binding", - PatternSource::IfLet => "if let binding", - PatternSource::WhileLet => "while let binding", - PatternSource::Let => "let binding", + PatternSource::Let | PatternSource::LetExpr => "let binding", PatternSource::For => "for binding", PatternSource::FnParam => "function parameter", } @@ -3093,13 +3091,7 @@ impl<'a> Resolver<'a> { fn resolve_arm(&mut self, arm: &Arm) { self.ribs[ValueNS].push(Rib::new(NormalRibKind)); - let mut bindings_list = FxHashMap::default(); - for pattern in &arm.pats { - self.resolve_pattern(&pattern, PatternSource::Match, &mut bindings_list); - } - - // This has to happen *after* we determine which pat_idents are variants. - self.check_consistent_bindings(&arm.pats); + self.resolve_pats(&arm.pats, PatternSource::Match); if let Some(ast::Guard::If(ref expr)) = arm.guard { self.visit_expr(expr) @@ -3109,6 +3101,16 @@ impl<'a> Resolver<'a> { self.ribs[ValueNS].pop(); } + /// Arising from `source`, resolve a sequence of patterns (top level or-patterns). + fn resolve_pats(&mut self, pats: &[P], source: PatternSource) { + let mut bindings_list = FxHashMap::default(); + for pat in pats { + self.resolve_pattern(pat, source, &mut bindings_list); + } + // This has to happen *after* we determine which pat_idents are variants + self.check_consistent_bindings(pats); + } + fn resolve_block(&mut self, block: &Block) { debug!("(resolving block) entering block"); // Move down in the graph, if there's an anonymous module rooted here. @@ -3187,8 +3189,7 @@ impl<'a> Resolver<'a> { ); } Some(..) if pat_src == PatternSource::Match || - pat_src == PatternSource::IfLet || - pat_src == PatternSource::WhileLet => { + pat_src == PatternSource::LetExpr => { // `Variant1(a) | Variant2(a)`, ok // Reuse definition from the first `a`. res = self.ribs[ValueNS].last_mut().unwrap().bindings[&ident]; @@ -4409,41 +4410,26 @@ impl<'a> Resolver<'a> { visit::walk_expr(self, expr); } - ExprKind::IfLet(ref pats, ref subexpression, ref if_block, ref optional_else) => { - self.visit_expr(subexpression); + ExprKind::Let(ref pats, ref scrutinee) => { + self.visit_expr(scrutinee); + self.resolve_pats(pats, PatternSource::LetExpr); + } + ExprKind::If(ref cond, ref then, ref opt_else) => { self.ribs[ValueNS].push(Rib::new(NormalRibKind)); - let mut bindings_list = FxHashMap::default(); - for pat in pats { - self.resolve_pattern(pat, PatternSource::IfLet, &mut bindings_list); - } - // This has to happen *after* we determine which pat_idents are variants - self.check_consistent_bindings(pats); - self.visit_block(if_block); + self.visit_expr(cond); + self.visit_block(then); self.ribs[ValueNS].pop(); - optional_else.as_ref().map(|expr| self.visit_expr(expr)); + opt_else.as_ref().map(|expr| self.visit_expr(expr)); } ExprKind::Loop(ref block, label) => self.resolve_labeled_block(label, expr.id, &block), ExprKind::While(ref subexpression, ref block, label) => { self.with_resolved_label(label, expr.id, |this| { - this.visit_expr(subexpression); - this.visit_block(block); - }); - } - - ExprKind::WhileLet(ref pats, ref subexpression, ref block, label) => { - self.with_resolved_label(label, expr.id, |this| { - this.visit_expr(subexpression); this.ribs[ValueNS].push(Rib::new(NormalRibKind)); - let mut bindings_list = FxHashMap::default(); - for pat in pats { - this.resolve_pattern(pat, PatternSource::WhileLet, &mut bindings_list); - } - // This has to happen *after* we determine which pat_idents are variants. - this.check_consistent_bindings(pats); + this.visit_expr(subexpression); this.visit_block(block); this.ribs[ValueNS].pop(); }); diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 1e65f868ebac0..d2546fd86deb8 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -1579,17 +1579,9 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc self.visit_expr(subexpression); visit::walk_block(self, block); } - ast::ExprKind::WhileLet(ref pats, ref subexpression, ref block, _) => { + ast::ExprKind::Let(ref pats, ref scrutinee) => { self.process_var_decl_multi(pats); - debug!("for loop, walk sub-expr: {:?}", subexpression.node); - self.visit_expr(subexpression); - visit::walk_block(self, block); - } - ast::ExprKind::IfLet(ref pats, ref subexpression, ref block, ref opt_else) => { - self.process_var_decl_multi(pats); - self.visit_expr(subexpression); - visit::walk_block(self, block); - opt_else.as_ref().map(|el| self.visit_expr(el)); + self.visit_expr(scrutinee); } ast::ExprKind::Repeat(ref element, ref count) => { self.visit_expr(element); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index d12240655e628..abae13cc9f378 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1049,10 +1049,9 @@ impl Expr { ExprKind::Unary(..) => ExprPrecedence::Unary, ExprKind::Lit(_) => ExprPrecedence::Lit, ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast, + ExprKind::Let(..) => ExprPrecedence::Let, ExprKind::If(..) => ExprPrecedence::If, - ExprKind::IfLet(..) => ExprPrecedence::IfLet, ExprKind::While(..) => ExprPrecedence::While, - ExprKind::WhileLet(..) => ExprPrecedence::WhileLet, ExprKind::ForLoop(..) => ExprPrecedence::ForLoop, ExprKind::Loop(..) => ExprPrecedence::Loop, ExprKind::Match(..) => ExprPrecedence::Match, @@ -1135,26 +1134,20 @@ pub enum ExprKind { Cast(P, P), /// A type ascription (e.g., `42: usize`). Type(P, P), + /// A `let pats = expr` pseudo-expression that only occurs in the scrutinee + /// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`). + /// + /// The `Vec>` is for or-patterns at the top level. + /// FIXME(54883): Change this to just `P`. + Let(Vec>, P), /// An `if` block, with an optional `else` block. /// /// `if expr { block } else { expr }` If(P, P, Option>), - /// An `if let` expression with an optional else block - /// - /// `if let pat = expr { block } else { expr }` - /// - /// This is desugared to a `match` expression. - IfLet(Vec>, P, P, Option>), - /// A while loop, with an optional label + /// A while loop, with an optional label. /// /// `'label: while expr { block }` While(P, P, Option