use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; use rustc_ast::attr; use rustc_ast::ptr::P as AstP; use rustc_ast::*; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_session::parse::feature_err; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP}; use rustc_target::asm; use std::collections::hash_map::Entry; use std::fmt::Write; impl<'hir> LoweringContext<'_, 'hir> { fn lower_exprs(&mut self, exprs: &[AstP]) -> &'hir [hir::Expr<'hir>] { self.arena.alloc_from_iter(exprs.iter().map(|x| self.lower_expr_mut(x))) } pub(super) fn lower_expr(&mut self, e: &Expr) -> &'hir hir::Expr<'hir> { self.arena.alloc(self.lower_expr_mut(e)) } pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { ensure_sufficient_stack(|| { let kind = match e.kind { ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)), ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), ExprKind::ConstBlock(ref anon_const) => { let anon_const = self.lower_anon_const(anon_const); hir::ExprKind::ConstBlock(anon_const) } ExprKind::Repeat(ref expr, ref count) => { let expr = self.lower_expr(expr); let count = self.lower_anon_const(count); hir::ExprKind::Repeat(expr, count) } ExprKind::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)), ExprKind::Call(ref f, ref args) => { let f = self.lower_expr(f); hir::ExprKind::Call(f, self.lower_exprs(args)) } ExprKind::MethodCall(ref seg, ref args, span) => { let hir_seg = self.arena.alloc(self.lower_path_segment( e.span, seg, ParamMode::Optional, 0, ParenthesizedGenericArgs::Err, ImplTraitContext::disallowed(), None, )); let args = self.lower_exprs(args); hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args, span) } ExprKind::Binary(binop, ref lhs, ref rhs) => { let binop = self.lower_binop(binop); let lhs = self.lower_expr(lhs); let rhs = self.lower_expr(rhs); hir::ExprKind::Binary(binop, lhs, rhs) } ExprKind::Unary(op, ref ohs) => { let op = self.lower_unop(op); let ohs = self.lower_expr(ohs); hir::ExprKind::Unary(op, ohs) } ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.kind.clone())), ExprKind::Cast(ref expr, ref ty) => { let expr = self.lower_expr(expr); let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); hir::ExprKind::Cast(expr, ty) } ExprKind::Type(ref expr, ref ty) => { let expr = self.lower_expr(expr); let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); hir::ExprKind::Type(expr, ty) } ExprKind::AddrOf(k, m, ref ohs) => { let ohs = self.lower_expr(ohs); hir::ExprKind::AddrOf(k, m, ohs) } ExprKind::Let(ref pat, ref scrutinee) => { self.lower_expr_let(e.span, pat, scrutinee) } ExprKind::If(ref cond, ref then, ref else_opt) => match cond.kind { ExprKind::Let(ref pat, ref scrutinee) => { self.lower_expr_if_let(e.span, pat, scrutinee, then, else_opt.as_deref()) } _ => self.lower_expr_if(cond, then, else_opt.as_deref()), }, ExprKind::While(ref cond, ref body, opt_label) => self .with_loop_scope(e.id, |this| { this.lower_expr_while_in_loop_scope(e.span, cond, body, opt_label) }), ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| { hir::ExprKind::Loop( this.lower_block(body, false), opt_label, hir::LoopSource::Loop, DUMMY_SP, ) }), ExprKind::TryBlock(ref body) => self.lower_expr_try_block(body), ExprKind::Match(ref expr, ref arms) => hir::ExprKind::Match( self.lower_expr(expr), self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))), hir::MatchSource::Normal, ), ExprKind::Async(capture_clause, closure_node_id, ref block) => self .make_async_expr( capture_clause, closure_node_id, None, block.span, hir::AsyncGeneratorKind::Block, |this| this.with_new_scopes(|this| this.lower_block_expr(block)), ), ExprKind::Await(ref expr) => self.lower_expr_await(e.span, expr), ExprKind::Closure( capture_clause, asyncness, movability, ref decl, ref body, fn_decl_span, ) => { if let Async::Yes { closure_id, .. } = asyncness { self.lower_expr_async_closure( capture_clause, closure_id, decl, body, fn_decl_span, ) } else { self.lower_expr_closure( capture_clause, movability, decl, body, fn_decl_span, ) } } ExprKind::Block(ref blk, opt_label) => { hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label) } ExprKind::Assign(ref el, ref er, span) => { self.lower_expr_assign(el, er, span, e.span) } ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp( self.lower_binop(op), self.lower_expr(el), self.lower_expr(er), ), ExprKind::Field(ref el, ident) => hir::ExprKind::Field(self.lower_expr(el), ident), ExprKind::Index(ref el, ref er) => { hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er)) } ExprKind::Range(Some(ref e1), Some(ref e2), RangeLimits::Closed) => { self.lower_expr_range_closed(e.span, e1, e2) } ExprKind::Range(ref e1, ref e2, lims) => { self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims) } ExprKind::Underscore => { self.sess .struct_span_err( e.span, "in expressions, `_` can only be used on the left-hand side of an assignment", ) .span_label(e.span, "`_` not allowed here") .emit(); hir::ExprKind::Err } ExprKind::Path(ref qself, ref path) => { let qpath = self.lower_qpath( e.id, qself, path, ParamMode::Optional, ImplTraitContext::disallowed(), ); hir::ExprKind::Path(qpath) } ExprKind::Break(opt_label, ref opt_expr) => { let opt_expr = opt_expr.as_ref().map(|x| self.lower_expr(x)); hir::ExprKind::Break(self.lower_jump_destination(e.id, opt_label), opt_expr) } ExprKind::Continue(opt_label) => { hir::ExprKind::Continue(self.lower_jump_destination(e.id, opt_label)) } ExprKind::Ret(ref e) => { let e = e.as_ref().map(|x| self.lower_expr(x)); hir::ExprKind::Ret(e) } ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm), ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm), ExprKind::Struct(ref path, ref fields, ref rest) => { let rest = match rest { StructRest::Base(e) => Some(self.lower_expr(e)), StructRest::Rest(sp) => { self.sess .struct_span_err(*sp, "base expression required after `..`") .span_label(*sp, "add a base expression here") .emit(); Some(&*self.arena.alloc(self.expr_err(*sp))) } StructRest::None => None, }; hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( e.id, &None, path, ParamMode::Optional, ImplTraitContext::disallowed(), )), self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))), rest, ) } ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), ExprKind::Err => hir::ExprKind::Err, ExprKind::Try(ref sub_expr) => self.lower_expr_try(e.span, sub_expr), ExprKind::Paren(ref ex) => { let mut ex = self.lower_expr_mut(ex); // Include parens in span, but only if it is a super-span. if e.span.contains(ex.span) { ex.span = e.span; } // Merge attributes into the inner expression. let mut attrs: Vec<_> = e.attrs.iter().map(|a| self.lower_attr(a)).collect(); attrs.extend::>(ex.attrs.into()); ex.attrs = attrs.into(); return ex; } // Desugar `ExprForLoop` // from: `[opt_ident]: for in ` ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => { return self.lower_expr_for(e, pat, head, body, opt_label); } ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), }; hir::Expr { hir_id: self.lower_node_id(e.id), kind, span: e.span, attrs: e.attrs.iter().map(|a| self.lower_attr(a)).collect::>().into(), } }) } fn lower_unop(&mut self, u: UnOp) -> hir::UnOp { match u { UnOp::Deref => hir::UnOp::Deref, UnOp::Not => hir::UnOp::Not, UnOp::Neg => hir::UnOp::Neg, } } fn lower_binop(&mut self, b: BinOp) -> hir::BinOp { Spanned { node: match b.node { BinOpKind::Add => hir::BinOpKind::Add, BinOpKind::Sub => hir::BinOpKind::Sub, BinOpKind::Mul => hir::BinOpKind::Mul, BinOpKind::Div => hir::BinOpKind::Div, BinOpKind::Rem => hir::BinOpKind::Rem, BinOpKind::And => hir::BinOpKind::And, BinOpKind::Or => hir::BinOpKind::Or, BinOpKind::BitXor => hir::BinOpKind::BitXor, BinOpKind::BitAnd => hir::BinOpKind::BitAnd, BinOpKind::BitOr => hir::BinOpKind::BitOr, BinOpKind::Shl => hir::BinOpKind::Shl, BinOpKind::Shr => hir::BinOpKind::Shr, BinOpKind::Eq => hir::BinOpKind::Eq, BinOpKind::Lt => hir::BinOpKind::Lt, BinOpKind::Le => hir::BinOpKind::Le, BinOpKind::Ne => hir::BinOpKind::Ne, BinOpKind::Ge => hir::BinOpKind::Ge, BinOpKind::Gt => hir::BinOpKind::Gt, }, span: b.span, } } /// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into: /// ```rust /// match scrutinee { pats => true, _ => false } /// ``` fn lower_expr_let(&mut self, span: Span, pat: &Pat, scrutinee: &Expr) -> hir::ExprKind<'hir> { // If we got here, the `let` expression is not allowed. if self.sess.opts.unstable_features.is_nightly_build() { self.sess .struct_span_err(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(); } else { self.sess .struct_span_err(span, "expected expression, found statement (`let`)") .note("variable declaration using `let` is a statement") .emit(); } // For better recovery, we emit: // ``` // match scrutinee { pat => true, _ => false } // ``` // While this doesn't fully match the user's intent, it has key advantages: // 1. We can avoid using `abort_if_errors`. // 2. We can typeck both `pat` and `scrutinee`. // 3. `pat` is allowed to be refutable. // 4. The return type of the block is `bool` which seems like what the user wanted. let scrutinee = self.lower_expr(scrutinee); let then_arm = { let pat = self.lower_pat(pat); let expr = self.expr_bool(span, true); self.arm(pat, expr) }; let else_arm = { let pat = self.pat_wild(span); let expr = self.expr_bool(span, false); self.arm(pat, expr) }; hir::ExprKind::Match( scrutinee, arena_vec![self; then_arm, else_arm], hir::MatchSource::Normal, ) } fn lower_expr_if( &mut self, cond: &Expr, then: &Block, else_opt: Option<&Expr>, ) -> hir::ExprKind<'hir> { macro_rules! make_if { ($opt:expr) => {{ let then_expr = self.lower_block_expr(then); hir::ExprKind::If(self.lower_expr(cond), self.arena.alloc(then_expr), $opt) }}; } if let Some(rslt) = else_opt { make_if!(Some(self.lower_expr(rslt))) } else { make_if!(None) } } fn lower_expr_if_let( &mut self, span: Span, pat: &Pat, scrutinee: &Expr, then: &Block, else_opt: Option<&Expr>, ) -> hir::ExprKind<'hir> { // FIXME(#53667): handle lowering of && and parens. // `_ => else_block` where `else_block` is `{}` if there's `None`: let else_pat = self.pat_wild(span); let (else_expr, contains_else_clause) = match else_opt { None => (self.expr_block_empty(span.shrink_to_hi()), false), Some(els) => (self.lower_expr(els), true), }; let else_arm = self.arm(else_pat, else_expr); // Handle then + scrutinee: let scrutinee = self.lower_expr(scrutinee); let then_pat = self.lower_pat(pat); let then_expr = self.lower_block_expr(then); let then_arm = self.arm(then_pat, self.arena.alloc(then_expr)); let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause }; hir::ExprKind::Match(scrutinee, arena_vec![self; then_arm, else_arm], desugar) } fn lower_expr_while_in_loop_scope( &mut self, span: Span, cond: &Expr, body: &Block, opt_label: Option