use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; use rustc_ast::ast::*; use rustc_ast::attr; use rustc_ast::ptr::P as AstP; 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_span::source_map::{respan, DesugaringKind, Span, Spanned}; use rustc_span::symbol::{sym, Symbol}; 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> { 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::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) => { 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) } 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) => { self.lower_expr_if(e.span, 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) }), 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) => { hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), 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::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::LlvmInlineAsm(ref asm) => self.lower_expr_asm(asm), ExprKind::Struct(ref path, ref fields, ref maybe_expr) => { let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x)); 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))), maybe_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 = e.attrs.clone(); attrs.extend::>(ex.attrs.into()); ex.attrs = attrs; return ex; } ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), ExprKind::Err => hir::ExprKind::Err, // 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::Try(ref sub_expr) => self.lower_expr_try(e.span, sub_expr), ExprKind::MacCall(_) => panic!("Shouldn't exist here"), }; 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::UnDeref, UnOp::Not => hir::UnOp::UnNot, UnOp::Neg => hir::UnOp::UnNeg, } } 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, span: Span, cond: &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), false), Some(els) => (self.lower_expr(els), true), }; let else_arm = self.arm(else_pat, else_expr); // Handle then + scrutinee: let then_expr = self.lower_block_expr(then); let (then_pat, scrutinee, desugar) = match cond.kind { // ` => `: ExprKind::Let(ref pat, ref scrutinee) => { let scrutinee = self.lower_expr(scrutinee); let pat = self.lower_pat(pat); (pat, scrutinee, hir::MatchSource::IfLetDesugar { contains_else_clause }) } // `true => `: _ => { // Lower condition: let cond = self.lower_expr(cond); let span_block = self.mark_span_with_reason(DesugaringKind::CondTemporary, 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, cond, ThinVec::new()); let pat = self.pat_bool(span, true); (pat, cond, hir::MatchSource::IfDesugar { contains_else_clause }) } }; let then_arm = self.arm(then_pat, self.arena.alloc(then_expr)); 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