use std::ops::ControlFlow; use std::sync::Arc; use rustc_ast::*; use rustc_ast_pretty::pprust::expr_to_string; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{HirId, Target, find_attr}; use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_session::errors::report_lit_error; use rustc_span::source_map::{Spanned, respan}; use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use visit::{Visitor, walk_expr}; use super::errors::{ AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic, CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign, }; use super::{ GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt, }; use crate::errors::{InvalidLegacyConstGenericArg, UseConstGenericArg, YieldInClosure}; use crate::{AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, fluent_generated}; struct WillCreateDefIdsVisitor {} impl<'v> rustc_ast::visit::Visitor<'v> for WillCreateDefIdsVisitor { type Result = ControlFlow; fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result { ControlFlow::Break(c.value.span) } fn visit_item(&mut self, item: &'v Item) -> Self::Result { ControlFlow::Break(item.span) } fn visit_expr(&mut self, ex: &'v Expr) -> Self::Result { match ex.kind { ExprKind::Gen(..) | ExprKind::ConstBlock(..) | ExprKind::Closure(..) => { ControlFlow::Break(ex.span) } _ => walk_expr(self, ex), } } } impl<'hir> LoweringContext<'_, 'hir> { fn lower_exprs(&mut self, exprs: &[Box]) -> &'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(|| { match &e.kind { // Parenthesis expression does not have a HirId and is handled specially. ExprKind::Paren(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 = self.lower_span(e.span); } // Merge attributes into the inner expression. if !e.attrs.is_empty() { let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]); let new_attrs = self .lower_attrs_vec(&e.attrs, e.span, ex.hir_id, Target::from_expr(e)) .into_iter() .chain(old_attrs.iter().cloned()); let new_attrs = &*self.arena.alloc_from_iter(new_attrs); if new_attrs.is_empty() { return ex; } self.attrs.insert(ex.hir_id.local_id, new_attrs); } return ex; } // Desugar `ExprForLoop` // from: `[opt_ident]: for await? in ` // // This also needs special handling because the HirId of the returned `hir::Expr` will not // correspond to the `e.id`, so `lower_expr_for` handles attribute lowering itself. ExprKind::ForLoop { pat, iter, body, label, kind } => { return self.lower_expr_for(e, pat, iter, body, *label, *kind); } _ => (), } let expr_hir_id = self.lower_node_id(e.id); let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span, Target::from_expr(e)); let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), ExprKind::ConstBlock(c) => hir::ExprKind::ConstBlock(self.lower_const_block(c)), ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); let count = self.lower_array_length_to_const_arg(count); hir::ExprKind::Repeat(expr, count) } ExprKind::Tup(elts) => hir::ExprKind::Tup(self.lower_exprs(elts)), ExprKind::Call(f, args) => { if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f) { self.lower_legacy_const_generics((**f).clone(), args.clone(), &legacy_args) } else { let f = self.lower_expr(f); hir::ExprKind::Call(f, self.lower_exprs(args)) } } ExprKind::MethodCall(box MethodCall { seg, receiver, args, span }) => { let hir_seg = self.arena.alloc(self.lower_path_segment( e.span, seg, ParamMode::Optional, GenericArgsMode::Err, ImplTraitContext::Disallowed(ImplTraitPosition::Path), // Method calls can't have bound modifiers None, )); let receiver = self.lower_expr(receiver); let args = self.arena.alloc_from_iter(args.iter().map(|x| self.lower_expr_mut(x))); hir::ExprKind::MethodCall(hir_seg, receiver, args, self.lower_span(*span)) } ExprKind::Binary(binop, lhs, 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, ohs) => { let op = self.lower_unop(*op); let ohs = self.lower_expr(ohs); hir::ExprKind::Unary(op, ohs) } ExprKind::Lit(token_lit) => hir::ExprKind::Lit(self.lower_lit(token_lit, e.span)), ExprKind::IncludedBytes(byte_sym) => { let lit = respan( self.lower_span(e.span), LitKind::ByteStr(*byte_sym, StrStyle::Cooked), ); hir::ExprKind::Lit(lit) } ExprKind::Cast(expr, ty) => { let expr = self.lower_expr(expr); let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast)); hir::ExprKind::Cast(expr, ty) } ExprKind::Type(expr, ty) => { let expr = self.lower_expr(expr); let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast)); hir::ExprKind::Type(expr, ty) } ExprKind::AddrOf(k, m, ohs) => { let ohs = self.lower_expr(ohs); hir::ExprKind::AddrOf(*k, *m, ohs) } ExprKind::Let(pat, scrutinee, span, recovered) => { hir::ExprKind::Let(self.arena.alloc(hir::LetExpr { span: self.lower_span(*span), pat: self.lower_pat(pat), ty: None, init: self.lower_expr(scrutinee), recovered: *recovered, })) } ExprKind::If(cond, then, else_opt) => { self.lower_expr_if(cond, then, else_opt.as_deref()) } ExprKind::While(cond, body, opt_label) => { self.with_loop_scope(expr_hir_id, |this| { let span = this.mark_span_with_reason(DesugaringKind::WhileLoop, e.span, None); let opt_label = this.lower_label(*opt_label, e.id, expr_hir_id); this.lower_expr_while_in_loop_scope(span, cond, body, opt_label) }) } ExprKind::Loop(body, opt_label, span) => { self.with_loop_scope(expr_hir_id, |this| { let opt_label = this.lower_label(*opt_label, e.id, expr_hir_id); hir::ExprKind::Loop( this.lower_block(body, false), opt_label, hir::LoopSource::Loop, this.lower_span(*span), ) }) } ExprKind::TryBlock(body) => self.lower_expr_try_block(body), ExprKind::Match(expr, arms, kind) => hir::ExprKind::Match( self.lower_expr(expr), self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))), match kind { MatchKind::Prefix => hir::MatchSource::Normal, MatchKind::Postfix => hir::MatchSource::Postfix, }, ), ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr), ExprKind::Use(expr, use_kw_span) => self.lower_expr_use(*use_kw_span, expr), ExprKind::Closure(box Closure { binder, capture_clause, constness, coroutine_kind, movability, fn_decl, body, fn_decl_span, fn_arg_span, }) => match coroutine_kind { Some(coroutine_kind) => self.lower_expr_coroutine_closure( binder, *capture_clause, e.id, expr_hir_id, *coroutine_kind, fn_decl, body, *fn_decl_span, *fn_arg_span, ), None => self.lower_expr_closure( attrs, binder, *capture_clause, e.id, *constness, *movability, fn_decl, body, *fn_decl_span, *fn_arg_span, ), }, ExprKind::Gen(capture_clause, block, genblock_kind, decl_span) => { let desugaring_kind = match genblock_kind { GenBlockKind::Async => hir::CoroutineDesugaring::Async, GenBlockKind::Gen => hir::CoroutineDesugaring::Gen, GenBlockKind::AsyncGen => hir::CoroutineDesugaring::AsyncGen, }; self.make_desugared_coroutine_expr( *capture_clause, e.id, None, *decl_span, e.span, desugaring_kind, hir::CoroutineSource::Block, |this| this.with_new_scopes(e.span, |this| this.lower_block_expr(block)), ) } ExprKind::Block(blk, opt_label) => { // Different from loops, label of block resolves to block id rather than // expr node id. let block_hir_id = self.lower_node_id(blk.id); let opt_label = self.lower_label(*opt_label, blk.id, block_hir_id); let hir_block = self.arena.alloc(self.lower_block_noalloc( block_hir_id, blk, opt_label.is_some(), )); hir::ExprKind::Block(hir_block, opt_label) } ExprKind::Assign(el, er, span) => self.lower_expr_assign(el, er, *span, e.span), ExprKind::AssignOp(op, el, er) => hir::ExprKind::AssignOp( self.lower_assign_op(*op), self.lower_expr(el), self.lower_expr(er), ), ExprKind::Field(el, ident) => { hir::ExprKind::Field(self.lower_expr(el), self.lower_ident(*ident)) } ExprKind::Index(el, er, brackets_span) => hir::ExprKind::Index( self.lower_expr(el), self.lower_expr(er), self.lower_span(*brackets_span), ), ExprKind::Range(e1, e2, lims) => { self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims) } ExprKind::Underscore => { let guar = self.dcx().emit_err(UnderscoreExprLhsAssign { span: e.span }); hir::ExprKind::Err(guar) } ExprKind::Path(qself, path) => { let qpath = self.lower_qpath( e.id, qself, path, ParamMode::Optional, AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); hir::ExprKind::Path(qpath) } ExprKind::Break(opt_label, 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(e) => { let expr = e.as_ref().map(|x| self.lower_expr(x)); self.checked_return(expr) } ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()), ExprKind::Become(sub_expr) => { let sub_expr = self.lower_expr(sub_expr); hir::ExprKind::Become(sub_expr) } ExprKind::InlineAsm(asm) => { hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm)) } ExprKind::FormatArgs(fmt) => self.lower_format_args(e.span, fmt), ExprKind::OffsetOf(container, fields) => hir::ExprKind::OffsetOf( self.lower_ty( container, ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf), ), self.arena.alloc_from_iter(fields.iter().map(|&ident| self.lower_ident(ident))), ), ExprKind::Struct(se) => { let rest = match &se.rest { StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)), StructRest::Rest(sp) => { hir::StructTailExpr::DefaultFields(self.lower_span(*sp)) } StructRest::None => hir::StructTailExpr::None, }; hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( e.id, &se.qself, &se.path, ParamMode::Optional, AllowReturnTypeNotation::No, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, )), self.arena .alloc_from_iter(se.fields.iter().map(|x| self.lower_expr_field(x))), rest, ) } ExprKind::Yield(kind) => self.lower_expr_yield(e.span, kind.expr().map(|x| &**x)), ExprKind::Err(guar) => hir::ExprKind::Err(*guar), ExprKind::UnsafeBinderCast(kind, expr, ty) => hir::ExprKind::UnsafeBinderCast( *kind, self.lower_expr(expr), ty.as_ref().map(|ty| { self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast)) }), ), ExprKind::Dummy => { span_bug!(e.span, "lowered ExprKind::Dummy") } ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr), ExprKind::Paren(_) | ExprKind::ForLoop { .. } => { unreachable!("already handled") } ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), }; hir::Expr { hir_id: expr_hir_id, kind, span: self.lower_span(e.span) } }) } /// Create an `ExprKind::Ret` that is optionally wrapped by a call to check /// a contract ensures clause, if it exists. fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> { let checked_ret = if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures { let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span)); Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id)) } else { opt_expr }; hir::ExprKind::Ret(checked_ret) } /// Wraps an expression with a call to the ensures check before it gets returned. pub(crate) fn inject_ensures_check( &mut self, expr: &'hir hir::Expr<'hir>, span: Span, cond_ident: Ident, cond_hir_id: HirId, ) -> &'hir hir::Expr<'hir> { let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id); let call_expr = self.expr_call_lang_item_fn_mut( span, hir::LangItem::ContractCheckEnsures, arena_vec![self; *cond_fn, *expr], ); self.arena.alloc(call_expr) } pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock { self.with_new_scopes(c.value.span, |this| { let def_id = this.local_def_id(c.id); hir::ConstBlock { def_id, hir_id: this.lower_node_id(c.id), body: this.lower_const_body(c.value.span, Some(&c.value)), } }) } pub(crate) fn lower_lit(&mut self, token_lit: &token::Lit, span: Span) -> hir::Lit { let lit_kind = match LitKind::from_token_lit(*token_lit) { Ok(lit_kind) => lit_kind, Err(err) => { let guar = report_lit_error(&self.tcx.sess.psess, err, *token_lit, span); LitKind::Err(guar) } }; respan(self.lower_span(span), lit_kind) } 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) -> BinOp { Spanned { node: b.node, span: self.lower_span(b.span) } } fn lower_assign_op(&mut self, a: AssignOp) -> AssignOp { Spanned { node: a.node, span: self.lower_span(a.span) } } fn lower_legacy_const_generics( &mut self, mut f: Expr, args: ThinVec>, legacy_args_idx: &[usize], ) -> hir::ExprKind<'hir> { let ExprKind::Path(None, path) = &mut f.kind else { unreachable!(); }; let mut error = None; let mut invalid_expr_error = |tcx: TyCtxt<'_>, span| { // Avoid emitting the error multiple times. if error.is_none() { let mut const_args = vec![]; let mut other_args = vec![]; for (idx, arg) in args.iter().enumerate() { if legacy_args_idx.contains(&idx) { const_args.push(format!("{{ {} }}", expr_to_string(arg))); } else { other_args.push(expr_to_string(arg)); } } let suggestion = UseConstGenericArg { end_of_fn: f.span.shrink_to_hi(), const_args: const_args.join(", "), other_args: other_args.join(", "), call_args: args[0].span.to(args.last().unwrap().span), }; error = Some(tcx.dcx().emit_err(InvalidLegacyConstGenericArg { span, suggestion })); } error.unwrap() }; // Split the arguments into const generics and normal arguments let mut real_args = vec![]; let mut generic_args = ThinVec::new(); for (idx, arg) in args.iter().cloned().enumerate() { if legacy_args_idx.contains(&idx) { let node_id = self.next_node_id(); self.create_def(node_id, None, DefKind::AnonConst, f.span); let mut visitor = WillCreateDefIdsVisitor {}; let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) { Box::new(Expr { id: self.next_node_id(), kind: ExprKind::Err(invalid_expr_error(self.tcx, span)), span: f.span, attrs: [].into(), tokens: None, }) } else { arg }; let anon_const = AnonConst { id: node_id, value: const_value }; generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const))); } else { real_args.push(arg); } } // Add generic args to the last element of the path. let last_segment = path.segments.last_mut().unwrap(); assert!(last_segment.args.is_none()); last_segment.args = Some(Box::new(GenericArgs::AngleBracketed(AngleBracketedArgs { span: DUMMY_SP, args: generic_args, }))); // Now lower everything as normal. let f = self.lower_expr(&f); hir::ExprKind::Call(f, self.lower_exprs(&real_args)) } fn lower_expr_if( &mut self, cond: &Expr, then: &Block, else_opt: Option<&Expr>, ) -> hir::ExprKind<'hir> { let lowered_cond = self.lower_expr(cond); let then_expr = self.lower_block_expr(then); if let Some(rslt) = else_opt { hir::ExprKind::If( lowered_cond, self.arena.alloc(then_expr), Some(self.lower_expr(rslt)), ) } else { hir::ExprKind::If(lowered_cond, self.arena.alloc(then_expr), None) } } // We desugar: `'label: while $cond $body` into: // // ``` // 'label: loop { // if { let _t = $cond; _t } { // $body // } // else { // break; // } // } // ``` // // Wrap in a construct equivalent to `{ let _t = $cond; _t }` // to preserve drop semantics since `while $cond { ... }` does not // let temporaries live outside of `cond`. fn lower_expr_while_in_loop_scope( &mut self, span: Span, cond: &Expr, body: &Block, opt_label: Option