use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext}; use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind}; use rustc_hir as hir; use rustc_session::parse::feature_err; use rustc_span::{sym, DesugaringKind}; use smallvec::SmallVec; impl<'a, 'hir> LoweringContext<'a, 'hir> { pub(super) fn lower_block( &mut self, b: &Block, targeted_by_break: bool, ) -> &'hir hir::Block<'hir> { self.arena.alloc(self.lower_block_noalloc(b, targeted_by_break)) } pub(super) fn lower_block_noalloc( &mut self, b: &Block, targeted_by_break: bool, ) -> hir::Block<'hir> { let (stmts, expr) = self.lower_stmts(&b.stmts); let rules = self.lower_block_check_mode(&b.rules); let hir_id = self.lower_node_id(b.id); hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break } } fn lower_stmts( &mut self, mut ast_stmts: &[Stmt], ) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) { let mut stmts = SmallVec::<[hir::Stmt<'hir>; 8]>::new(); let mut expr = None; while let [s, tail @ ..] = ast_stmts { match s.kind { StmtKind::Local(ref local) => { let hir_id = self.lower_node_id(s.id); match &local.kind { LocalKind::InitElse(init, els) => { let e = self.lower_let_else(hir_id, local, init, els, tail); 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_ref(it).into_iter().enumerate().map( |(i, item_id)| { let hir_id = match i { 0 => self.lower_node_id(s.id), _ => self.next_id(), }; let kind = hir::StmtKind::Item(item_id); let span = self.lower_span(s.span); hir::Stmt { hir_id, kind, span } }, )); } StmtKind::Expr(ref e) => { let e = self.lower_expr(e); if tail.is_empty() { expr = Some(e); } else { let hir_id = self.lower_node_id(s.id); self.alias_attrs(hir_id, e.hir_id); let kind = hir::StmtKind::Expr(e); let span = self.lower_span(s.span); stmts.push(hir::Stmt { hir_id, kind, span }); } } StmtKind::Semi(ref e) => { let e = self.lower_expr(e); let hir_id = self.lower_node_id(s.id); self.alias_attrs(hir_id, e.hir_id); let kind = hir::StmtKind::Semi(e); let span = self.lower_span(s.span); stmts.push(hir::Stmt { hir_id, kind, span }); } StmtKind::Empty => {} StmtKind::MacCall(..) => panic!("shouldn't exist here"), } ast_stmts = &ast_stmts[1..]; } (self.arena.alloc_from_iter(stmts), expr) } 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::Variable))); 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); self.arena.alloc(hir::Local { hir_id, ty, pat, init, span, source }) } fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode { match *b { BlockCheckMode::Default => hir::BlockCheckMode::DefaultBlock, BlockCheckMode::Unsafe(u) => { hir::BlockCheckMode::UnsafeBlock(self.lower_unsafe_source(u)) } } } fn lower_let_else( &mut self, stmt_hir_id: hir::HirId, local: &Local, init: &Expr, els: &Block, tail: &[Stmt], ) -> &'hir hir::Expr<'hir> { let ty = local .ty .as_ref() .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable))); let span = self.lower_span(local.span); let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None); let init = self.lower_expr(init); let local_hir_id = self.lower_node_id(local.id); self.lower_attrs(local_hir_id, &local.attrs); let let_expr = { let lex = self.arena.alloc(hir::Let { hir_id: local_hir_id, pat: self.lower_pat(&local.pat), ty, init, span, }); self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), 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(let_expr.hir_id, local_hir_id); self.alias_attrs(else_expr.hir_id, local_hir_id); let if_expr = self.arena.alloc(hir::Expr { hir_id: stmt_hir_id, span, kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)), }); if !self.tcx.features().let_else { feature_err( &self.tcx.sess.parse_sess, sym::let_else, local.span, "`let...else` statements are unstable", ) .emit(); } if_expr } }