diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 4c650ad3dcb..f4584e8bbdf 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -1,6 +1,8 @@ use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext}; -use rustc_ast::{Block, BlockCheckMode, Local, Stmt, StmtKind}; +use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind}; use rustc_hir as hir; +use rustc_span::symbol::Ident; +use rustc_span::{sym, DesugaringKind}; use smallvec::SmallVec; @@ -32,13 +34,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut expr = None; while let [s, tail @ ..] = ast_stmts { match s.kind { - StmtKind::Local(ref l) => { - let l = self.lower_local(l); + StmtKind::Local(ref local) => { let hir_id = self.lower_node_id(s.id); - self.alias_attrs(hir_id, l.hir_id); - let kind = hir::StmtKind::Local(self.arena.alloc(l)); - let span = self.lower_span(s.span); - stmts.push(hir::Stmt { hir_id, kind, span }); + match &local.kind { + LocalKind::InitElse(init, els) => { + let (s, e) = self.lower_let_else(hir_id, local, init, els, tail); + stmts.push(s); + expr = Some(e); + // remaining statements are in let-else expression + break; + } + _ => { + let local = self.lower_local(local); + self.alias_attrs(hir_id, local.hir_id); + let kind = hir::StmtKind::Local(local); + let span = self.lower_span(s.span); + stmts.push(hir::Stmt { hir_id, kind, span }); + } + } } StmtKind::Item(ref it) => { stmts.extend(self.lower_item_id(it).into_iter().enumerate().map( @@ -81,22 +94,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (self.arena.alloc_from_iter(stmts), expr) } - fn lower_local(&mut self, l: &Local) -> hir::Local<'hir> { + fn lower_local(&mut self, l: &Local) -> &'hir hir::Local<'hir> { let ty = l .ty .as_ref() .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding))); let init = l.kind.init().map(|init| self.lower_expr(init)); let hir_id = self.lower_node_id(l.id); + let pat = self.lower_pat(&l.pat); + let span = self.lower_span(l.span); + let source = hir::LocalSource::Normal; self.lower_attrs(hir_id, &l.attrs); - hir::Local { - hir_id, - ty, - pat: self.lower_pat(&l.pat), - init, - span: self.lower_span(l.span), - source: hir::LocalSource::Normal, - } + self.arena.alloc(hir::Local { hir_id, ty, pat, init, span, source }) } fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode { @@ -107,4 +116,60 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } + + fn lower_let_else( + &mut self, + stmt_hir_id: hir::HirId, + local: &Local, + init: &Expr, + els: &Block, + tail: &[Stmt], + ) -> (hir::Stmt<'hir>, &'hir hir::Expr<'hir>) { + let ty = local + .ty + .as_ref() + .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding))); + let span = self.lower_span(local.span); + let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None); + let init = Some(self.lower_expr(init)); + let val = Ident::with_dummy_span(sym::val); + let (pat, val_id) = + self.pat_ident_binding_mode(span, val, hir::BindingAnnotation::Unannotated); + let local_hir_id = self.lower_node_id(local.id); + self.lower_attrs(local_hir_id, &local.attrs); + // first statement which basically exists for the type annotation + let stmt = { + let local = self.arena.alloc(hir::Local { + hir_id: local_hir_id, + ty, + pat, + init, + span, + source: hir::LocalSource::Normal, + }); + let kind = hir::StmtKind::Local(local); + hir::Stmt { hir_id: stmt_hir_id, kind, span } + }; + let let_expr = { + let scrutinee = self.expr_ident(span, val, val_id); + let let_kind = hir::ExprKind::Let(self.lower_pat(&local.pat), scrutinee, span); + self.arena.alloc(self.expr(span, let_kind, AttrVec::new())) + }; + let then_expr = { + let (stmts, expr) = self.lower_stmts(tail); + let block = self.block_all(span, stmts, expr); + self.arena.alloc(self.expr_block(block, AttrVec::new())) + }; + let else_expr = { + let block = self.lower_block(els, false); + self.arena.alloc(self.expr_block(block, AttrVec::new())) + }; + self.alias_attrs(else_expr.hir_id, local_hir_id); + let if_expr = self.arena.alloc(hir::Expr { + hir_id: self.next_id(), + span, + kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)), + }); + (stmt, if_expr) + } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index e44a2e96598..c22093c5a42 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1097,6 +1097,7 @@ pub enum DesugaringKind { Async, Await, ForLoop(ForLoopLoc), + LetElse, } /// A location in the desugaring of a `for` loop @@ -1117,6 +1118,7 @@ impl DesugaringKind { DesugaringKind::TryBlock => "`try` block", DesugaringKind::OpaqueTy => "`impl Trait`", DesugaringKind::ForLoop(_) => "`for` loop", + DesugaringKind::LetElse => "`let...else`", } } }