From cf20d8c3887f96f990276cf6a0e6cee2687b1048 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sat, 10 Aug 2019 13:50:48 +0200 Subject: [PATCH 01/25] lowering: move lower_expr -> expr.rs --- src/librustc/hir/lowering.rs | 802 +---------------------------- src/librustc/hir/lowering/expr.rs | 813 ++++++++++++++++++++++++++++++ 2 files changed, 815 insertions(+), 800 deletions(-) create mode 100644 src/librustc/hir/lowering/expr.rs diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 591ceaf28a6..2abcbc9cfdc 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -32,6 +32,8 @@ //! get confused if the spans from leaf AST nodes occur in multiple places //! in the HIR, especially for multiple identifiers. +mod expr; + use crate::dep_graph::DepGraph; use crate::hir::{self, ParamName}; use crate::hir::HirVec; @@ -4440,806 +4442,6 @@ impl<'a> LoweringContext<'a> { }) } - fn lower_exprs(&mut self, exprs: &[AstP]) -> HirVec { - exprs.iter().map(|x| self.lower_expr(x)).collect() - } - - fn lower_expr(&mut self, e: &Expr) -> hir::Expr { - let kind = match e.node { - ExprKind::Box(ref inner) => hir::ExprKind::Box(P(self.lower_expr(inner))), - ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::Repeat(ref expr, ref count) => { - let expr = P(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 = P(self.lower_expr(f)); - hir::ExprKind::Call(f, self.lower_exprs(args)) - } - ExprKind::MethodCall(ref seg, ref args) => { - let hir_seg = P(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 = P(self.lower_expr(lhs)); - let rhs = P(self.lower_expr(rhs)); - hir::ExprKind::Binary(binop, lhs, rhs) - } - ExprKind::Unary(op, ref ohs) => { - let op = self.lower_unop(op); - let ohs = P(self.lower_expr(ohs)); - hir::ExprKind::Unary(op, ohs) - } - ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.node.clone())), - ExprKind::Cast(ref expr, ref ty) => { - let expr = P(self.lower_expr(expr)); - hir::ExprKind::Cast(expr, self.lower_ty(ty, ImplTraitContext::disallowed())) - } - ExprKind::Type(ref expr, ref ty) => { - let expr = P(self.lower_expr(expr)); - hir::ExprKind::Type(expr, self.lower_ty(ty, ImplTraitContext::disallowed())) - } - ExprKind::AddrOf(m, ref ohs) => { - let m = self.lower_mutability(m); - let ohs = P(self.lower_expr(ohs)); - hir::ExprKind::AddrOf(m, ohs) - } - ExprKind::Let(ref pats, ref scrutinee) => { - // If we got here, the `let` expression is not allowed. - self.sess - .struct_span_err(e.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(); - - // For better recovery, we emit: - // ``` - // match scrutinee { pats => 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 `pats` and `scrutinee`. - // 3. `pats` 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 pats = pats.iter().map(|pat| self.lower_pat(pat)).collect(); - let expr = self.expr_bool(e.span, true); - self.arm(pats, P(expr)) - }; - let else_arm = { - let pats = hir_vec![self.pat_wild(e.span)]; - let expr = self.expr_bool(e.span, false); - self.arm(pats, P(expr)) - }; - hir::ExprKind::Match( - P(scrutinee), - vec![then_arm, else_arm].into(), - hir::MatchSource::Normal, - ) - } - // FIXME(#53667): handle lowering of && and parens. - ExprKind::If(ref cond, ref then, ref else_opt) => { - // `_ => else_block` where `else_block` is `{}` if there's `None`: - let else_pat = self.pat_wild(e.span); - let (else_expr, contains_else_clause) = match else_opt { - None => (self.expr_block_empty(e.span), false), - Some(els) => (self.lower_expr(els), true), - }; - let else_arm = self.arm(hir_vec![else_pat], P(else_expr)); - - // Handle then + scrutinee: - let then_blk = self.lower_block(then, false); - let then_expr = self.expr_block(then_blk, ThinVec::new()); - let (then_pats, scrutinee, desugar) = match cond.node { - // ` => `: - ExprKind::Let(ref pats, ref scrutinee) => { - let scrutinee = self.lower_expr(scrutinee); - let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect(); - let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause }; - (pats, scrutinee, desugar) - } - // `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, P(cond), ThinVec::new()); - - let desugar = hir::MatchSource::IfDesugar { contains_else_clause }; - let pats = hir_vec![self.pat_bool(e.span, true)]; - (pats, cond, desugar) - } - }; - let then_arm = self.arm(then_pats, P(then_expr)); - - hir::ExprKind::Match(P(scrutinee), vec![then_arm, else_arm].into(), desugar) - } - // FIXME(#53667): handle lowering of && and parens. - ExprKind::While(ref cond, ref body, opt_label) => self.with_loop_scope(e.id, |this| { - // Note that the block AND the condition are evaluated in the loop scope. - // This is done to allow `break` from inside the condition of the loop. - - // `_ => break`: - let else_arm = { - let else_pat = this.pat_wild(e.span); - let else_expr = this.expr_break(e.span, ThinVec::new()); - this.arm(hir_vec![else_pat], else_expr) - }; - - // Handle then + scrutinee: - let then_blk = this.lower_block(body, false); - let then_expr = this.expr_block(then_blk, ThinVec::new()); - let (then_pats, scrutinee, desugar, source) = match cond.node { - ExprKind::Let(ref pats, ref scrutinee) => { - // to: - // - // [opt_ident]: loop { - // match { - // => , - // _ => break - // } - // } - let scrutinee = this.with_loop_condition_scope(|t| t.lower_expr(scrutinee)); - let pats = pats.iter().map(|pat| this.lower_pat(pat)).collect(); - let desugar = hir::MatchSource::WhileLetDesugar; - (pats, scrutinee, desugar, hir::LoopSource::WhileLet) - } - _ => { - // We desugar: `'label: while $cond $body` into: - // - // ``` - // 'label: loop { - // match DropTemps($cond) { - // true => $body, - // _ => break, - // } - // } - // ``` - - // Lower condition: - let cond = this.with_loop_condition_scope(|this| this.lower_expr(cond)); - let span_block = this.mark_span_with_reason( - DesugaringKind::CondTemporary, cond.span, None - ); - // 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`. - let cond = this.expr_drop_temps(span_block, P(cond), ThinVec::new()); - - let desugar = hir::MatchSource::WhileDesugar; - // `true => `: - let pats = hir_vec![this.pat_bool(e.span, true)]; - (pats, cond, desugar, hir::LoopSource::While) - } - }; - let then_arm = this.arm(then_pats, P(then_expr)); - - // `match { ... }` - let match_expr = this.expr_match( - scrutinee.span, - P(scrutinee), - hir_vec![then_arm, else_arm], - desugar, - ); - - // `[opt_ident]: loop { ... }` - hir::ExprKind::Loop( - P(this.block_expr(P(match_expr))), - this.lower_label(opt_label), - source - ) - }), - ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| { - hir::ExprKind::Loop( - this.lower_block(body, false), - this.lower_label(opt_label), - hir::LoopSource::Loop, - ) - }), - ExprKind::TryBlock(ref body) => { - self.with_catch_scope(body.id, |this| { - let unstable_span = this.mark_span_with_reason( - DesugaringKind::TryBlock, - body.span, - this.allow_try_trait.clone(), - ); - let mut block = this.lower_block(body, true).into_inner(); - let tail = block.expr.take().map_or_else( - || { - let span = this.sess.source_map().end_point(unstable_span); - hir::Expr { - span, - node: hir::ExprKind::Tup(hir_vec![]), - attrs: ThinVec::new(), - hir_id: this.next_id(), - } - }, - |x: P| x.into_inner(), - ); - block.expr = Some(this.wrap_in_try_constructor( - sym::from_ok, tail, unstable_span)); - hir::ExprKind::Block(P(block), None) - }) - } - ExprKind::Match(ref expr, ref arms) => hir::ExprKind::Match( - P(self.lower_expr(expr)), - arms.iter().map(|x| self.lower_arm(x)).collect(), - hir::MatchSource::Normal, - ), - ExprKind::Async(capture_clause, closure_node_id, ref block) => { - self.make_async_expr(capture_clause, closure_node_id, None, block.span, |this| { - this.with_new_scopes(|this| { - let block = this.lower_block(block, false); - this.expr_block(block, ThinVec::new()) - }) - }) - } - ExprKind::Await(ref expr) => self.lower_await(e.span, expr), - ExprKind::Closure( - capture_clause, asyncness, movability, ref decl, ref body, fn_decl_span - ) => { - if let IsAsync::Async { closure_id, .. } = asyncness { - let outer_decl = FnDecl { - inputs: decl.inputs.clone(), - output: FunctionRetTy::Default(fn_decl_span), - c_variadic: false, - }; - // We need to lower the declaration outside the new scope, because we - // have to conserve the state of being inside a loop condition for the - // closure argument types. - let fn_decl = self.lower_fn_decl(&outer_decl, None, false, None); - - self.with_new_scopes(|this| { - // FIXME(cramertj): allow `async` non-`move` closures with arguments. - if capture_clause == CaptureBy::Ref && - !decl.inputs.is_empty() - { - struct_span_err!( - this.sess, - fn_decl_span, - E0708, - "`async` non-`move` closures with arguments \ - are not currently supported", - ) - .help("consider using `let` statements to manually capture \ - variables by reference before entering an \ - `async move` closure") - .emit(); - } - - // Transform `async |x: u8| -> X { ... }` into - // `|x: u8| future_from_generator(|| -> X { ... })`. - let body_id = this.lower_fn_body(&outer_decl, |this| { - let async_ret_ty = if let FunctionRetTy::Ty(ty) = &decl.output { - Some(ty.clone()) - } else { None }; - let async_body = this.make_async_expr( - capture_clause, closure_id, async_ret_ty, body.span, - |this| { - this.with_new_scopes(|this| this.lower_expr(body)) - }); - this.expr(fn_decl_span, async_body, ThinVec::new()) - }); - hir::ExprKind::Closure( - this.lower_capture_clause(capture_clause), - fn_decl, - body_id, - fn_decl_span, - None, - ) - }) - } else { - // Lower outside new scope to preserve `is_in_loop_condition`. - let fn_decl = self.lower_fn_decl(decl, None, false, None); - - self.with_new_scopes(|this| { - this.current_item = Some(fn_decl_span); - let mut generator_kind = None; - let body_id = this.lower_fn_body(decl, |this| { - let e = this.lower_expr(body); - generator_kind = this.generator_kind; - e - }); - let generator_option = this.generator_movability_for_fn( - &decl, - fn_decl_span, - generator_kind, - movability, - ); - hir::ExprKind::Closure( - this.lower_capture_clause(capture_clause), - fn_decl, - body_id, - fn_decl_span, - generator_option, - ) - }) - } - } - ExprKind::Block(ref blk, opt_label) => { - hir::ExprKind::Block(self.lower_block(blk, - opt_label.is_some()), - self.lower_label(opt_label)) - } - ExprKind::Assign(ref el, ref er) => { - hir::ExprKind::Assign(P(self.lower_expr(el)), P(self.lower_expr(er))) - } - ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp( - self.lower_binop(op), - P(self.lower_expr(el)), - P(self.lower_expr(er)), - ), - ExprKind::Field(ref el, ident) => hir::ExprKind::Field(P(self.lower_expr(el)), ident), - ExprKind::Index(ref el, ref er) => { - hir::ExprKind::Index(P(self.lower_expr(el)), P(self.lower_expr(er))) - } - // Desugar `..=` into `std::ops::RangeInclusive::new(, )`. - ExprKind::Range(Some(ref e1), Some(ref e2), RangeLimits::Closed) => { - let id = self.next_id(); - let e1 = self.lower_expr(e1); - let e2 = self.lower_expr(e2); - self.expr_call_std_assoc_fn( - id, - e.span, - &[sym::ops, sym::RangeInclusive], - "new", - hir_vec![e1, e2], - ) - } - ExprKind::Range(ref e1, ref e2, lims) => { - use syntax::ast::RangeLimits::*; - - let path = match (e1, e2, lims) { - (&None, &None, HalfOpen) => sym::RangeFull, - (&Some(..), &None, HalfOpen) => sym::RangeFrom, - (&None, &Some(..), HalfOpen) => sym::RangeTo, - (&Some(..), &Some(..), HalfOpen) => sym::Range, - (&None, &Some(..), Closed) => sym::RangeToInclusive, - (&Some(..), &Some(..), Closed) => unreachable!(), - (_, &None, Closed) => self.diagnostic() - .span_fatal(e.span, "inclusive range with no end") - .raise(), - }; - - let fields = e1.iter() - .map(|e| ("start", e)) - .chain(e2.iter().map(|e| ("end", e))) - .map(|(s, e)| { - let expr = P(self.lower_expr(&e)); - let ident = Ident::new(Symbol::intern(s), e.span); - self.field(ident, expr, e.span) - }) - .collect::>(); - - let is_unit = fields.is_empty(); - let struct_path = [sym::ops, path]; - let struct_path = self.std_path(e.span, &struct_path, None, is_unit); - let struct_path = hir::QPath::Resolved(None, P(struct_path)); - - return hir::Expr { - hir_id: self.lower_node_id(e.id), - node: if is_unit { - hir::ExprKind::Path(struct_path) - } else { - hir::ExprKind::Struct(P(struct_path), fields, None) - }, - span: e.span, - attrs: e.attrs.clone(), - }; - } - 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 destination = if self.is_in_loop_condition && opt_label.is_none() { - hir::Destination { - label: None, - target_id: Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into(), - } - } else { - self.lower_loop_destination(opt_label.map(|label| (e.id, label))) - }; - hir::ExprKind::Break( - destination, - opt_expr.as_ref().map(|x| P(self.lower_expr(x))), - ) - } - ExprKind::Continue(opt_label) => { - hir::ExprKind::Continue(if self.is_in_loop_condition && opt_label.is_none() { - hir::Destination { - label: None, - target_id: Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into(), - } - } else { - self.lower_loop_destination(opt_label.map(|label| (e.id, label))) - }) - } - ExprKind::Ret(ref e) => hir::ExprKind::Ret(e.as_ref().map(|x| P(self.lower_expr(x)))), - ExprKind::InlineAsm(ref asm) => { - let hir_asm = hir::InlineAsm { - inputs: asm.inputs.iter().map(|&(ref c, _)| c.clone()).collect(), - outputs: asm.outputs - .iter() - .map(|out| hir::InlineAsmOutput { - constraint: out.constraint.clone(), - is_rw: out.is_rw, - is_indirect: out.is_indirect, - span: out.expr.span, - }) - .collect(), - asm: asm.asm.clone(), - asm_str_style: asm.asm_str_style, - clobbers: asm.clobbers.clone().into(), - volatile: asm.volatile, - alignstack: asm.alignstack, - dialect: asm.dialect, - ctxt: asm.ctxt, - }; - let outputs = asm.outputs - .iter() - .map(|out| self.lower_expr(&out.expr)) - .collect(); - let inputs = asm.inputs - .iter() - .map(|&(_, ref input)| self.lower_expr(input)) - .collect(); - hir::ExprKind::InlineAsm(P(hir_asm), outputs, inputs) - } - ExprKind::Struct(ref path, ref fields, ref maybe_expr) => hir::ExprKind::Struct( - P(self.lower_qpath( - e.id, - &None, - path, - ParamMode::Optional, - ImplTraitContext::disallowed(), - )), - fields.iter().map(|x| self.lower_field(x)).collect(), - maybe_expr.as_ref().map(|x| P(self.lower_expr(x))), - ), - ExprKind::Paren(ref ex) => { - let mut ex = self.lower_expr(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) => { - match self.generator_kind { - Some(hir::GeneratorKind::Gen) => {}, - Some(hir::GeneratorKind::Async) => { - span_err!( - self.sess, - e.span, - E0727, - "`async` generators are not yet supported", - ); - self.sess.abort_if_errors(); - }, - None => { - self.generator_kind = Some(hir::GeneratorKind::Gen); - } - } - let expr = opt_expr - .as_ref() - .map(|x| self.lower_expr(x)) - .unwrap_or_else(|| self.expr_unit(e.span)); - hir::ExprKind::Yield(P(expr), hir::YieldSource::Yield) - } - - ExprKind::Err => hir::ExprKind::Err, - - // Desugar `ExprForLoop` - // from: `[opt_ident]: for in ` - ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => { - // to: - // - // { - // let result = match ::std::iter::IntoIterator::into_iter() { - // mut iter => { - // [opt_ident]: loop { - // let mut __next; - // match ::std::iter::Iterator::next(&mut iter) { - // ::std::option::Option::Some(val) => __next = val, - // ::std::option::Option::None => break - // }; - // let = __next; - // StmtKind::Expr(); - // } - // } - // }; - // result - // } - - // expand - let mut head = self.lower_expr(head); - let head_sp = head.span; - let desugared_span = self.mark_span_with_reason( - DesugaringKind::ForLoop, - head_sp, - None, - ); - head.span = desugared_span; - - let iter = Ident::with_empty_ctxt(sym::iter); - - let next_ident = Ident::with_empty_ctxt(sym::__next); - let (next_pat, next_pat_hid) = self.pat_ident_binding_mode( - desugared_span, - next_ident, - hir::BindingAnnotation::Mutable, - ); - - // `::std::option::Option::Some(val) => __next = val` - let pat_arm = { - let val_ident = Ident::with_empty_ctxt(sym::val); - let (val_pat, val_pat_hid) = self.pat_ident(pat.span, val_ident); - let val_expr = P(self.expr_ident(pat.span, val_ident, val_pat_hid)); - let next_expr = P(self.expr_ident(pat.span, next_ident, next_pat_hid)); - let assign = P(self.expr( - pat.span, - hir::ExprKind::Assign(next_expr, val_expr), - ThinVec::new(), - )); - let some_pat = self.pat_some(pat.span, val_pat); - self.arm(hir_vec![some_pat], assign) - }; - - // `::std::option::Option::None => break` - let break_arm = { - let break_expr = - self.with_loop_scope(e.id, |this| this.expr_break(e.span, ThinVec::new())); - let pat = self.pat_none(e.span); - self.arm(hir_vec![pat], break_expr) - }; - - // `mut iter` - let (iter_pat, iter_pat_nid) = self.pat_ident_binding_mode( - desugared_span, - iter, - hir::BindingAnnotation::Mutable - ); - - // `match ::std::iter::Iterator::next(&mut iter) { ... }` - let match_expr = { - let iter = P(self.expr_ident(head_sp, iter, iter_pat_nid)); - let ref_mut_iter = self.expr_mut_addr_of(head_sp, iter); - let next_path = &[sym::iter, sym::Iterator, sym::next]; - let next_expr = P(self.expr_call_std_path( - head_sp, - next_path, - hir_vec![ref_mut_iter], - )); - let arms = hir_vec![pat_arm, break_arm]; - - self.expr_match(head_sp, next_expr, arms, hir::MatchSource::ForLoopDesugar) - }; - let match_stmt = self.stmt_expr(head_sp, match_expr); - - let next_expr = P(self.expr_ident(head_sp, next_ident, next_pat_hid)); - - // `let mut __next` - let next_let = self.stmt_let_pat( - ThinVec::new(), - desugared_span, - None, - next_pat, - hir::LocalSource::ForLoopDesugar, - ); - - // `let = __next` - let pat = self.lower_pat(pat); - let pat_let = self.stmt_let_pat( - ThinVec::new(), - head_sp, - Some(next_expr), - pat, - hir::LocalSource::ForLoopDesugar, - ); - - let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false)); - let body_expr = self.expr_block(body_block, ThinVec::new()); - let body_stmt = self.stmt_expr(body.span, body_expr); - - let loop_block = P(self.block_all( - e.span, - hir_vec![next_let, match_stmt, pat_let, body_stmt], - None, - )); - - // `[opt_ident]: loop { ... }` - let loop_expr = hir::ExprKind::Loop( - loop_block, - self.lower_label(opt_label), - hir::LoopSource::ForLoop, - ); - let loop_expr = P(hir::Expr { - hir_id: self.lower_node_id(e.id), - node: loop_expr, - span: e.span, - attrs: ThinVec::new(), - }); - - // `mut iter => { ... }` - let iter_arm = self.arm(hir_vec![iter_pat], loop_expr); - - // `match ::std::iter::IntoIterator::into_iter() { ... }` - let into_iter_expr = { - let into_iter_path = - &[sym::iter, sym::IntoIterator, sym::into_iter]; - P(self.expr_call_std_path( - head_sp, - into_iter_path, - hir_vec![head], - )) - }; - - let match_expr = P(self.expr_match( - head_sp, - into_iter_expr, - hir_vec![iter_arm], - hir::MatchSource::ForLoopDesugar, - )); - - // This is effectively `{ let _result = ...; _result }`. - // The construct was introduced in #21984 and is necessary to make sure that - // temporaries in the `head` expression are dropped and do not leak to the - // surrounding scope of the `match` since the `match` is not a terminating scope. - // - // Also, add the attributes to the outer returned expr node. - return self.expr_drop_temps(head_sp, match_expr, e.attrs.clone()) - } - - // Desugar `ExprKind::Try` - // from: `?` - ExprKind::Try(ref sub_expr) => { - // into: - // - // match Try::into_result() { - // Ok(val) => #[allow(unreachable_code)] val, - // Err(err) => #[allow(unreachable_code)] - // // If there is an enclosing `catch {...}` - // break 'catch_target Try::from_error(From::from(err)), - // // Otherwise - // return Try::from_error(From::from(err)), - // } - - let unstable_span = self.mark_span_with_reason( - DesugaringKind::QuestionMark, - e.span, - self.allow_try_trait.clone(), - ); - let try_span = self.sess.source_map().end_point(e.span); - let try_span = self.mark_span_with_reason( - DesugaringKind::QuestionMark, - try_span, - self.allow_try_trait.clone(), - ); - - // `Try::into_result()` - let discr = { - // expand - let sub_expr = self.lower_expr(sub_expr); - - let path = &[sym::ops, sym::Try, sym::into_result]; - P(self.expr_call_std_path( - unstable_span, - path, - hir_vec![sub_expr], - )) - }; - - // `#[allow(unreachable_code)]` - let attr = { - // `allow(unreachable_code)` - let allow = { - let allow_ident = Ident::new(sym::allow, e.span); - let uc_ident = Ident::new(sym::unreachable_code, e.span); - let uc_nested = attr::mk_nested_word_item(uc_ident); - attr::mk_list_item(allow_ident, vec![uc_nested]) - }; - attr::mk_attr_outer(allow) - }; - let attrs = vec![attr]; - - // `Ok(val) => #[allow(unreachable_code)] val,` - let ok_arm = { - let val_ident = Ident::with_empty_ctxt(sym::val); - let (val_pat, val_pat_nid) = self.pat_ident(e.span, val_ident); - let val_expr = P(self.expr_ident_with_attrs( - e.span, - val_ident, - val_pat_nid, - ThinVec::from(attrs.clone()), - )); - let ok_pat = self.pat_ok(e.span, val_pat); - - self.arm(hir_vec![ok_pat], val_expr) - }; - - // `Err(err) => #[allow(unreachable_code)] - // return Try::from_error(From::from(err)),` - let err_arm = { - let err_ident = Ident::with_empty_ctxt(sym::err); - let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident); - let from_expr = { - let from_path = &[sym::convert, sym::From, sym::from]; - let err_expr = self.expr_ident(try_span, err_ident, err_local_nid); - self.expr_call_std_path(try_span, from_path, hir_vec![err_expr]) - }; - let from_err_expr = - self.wrap_in_try_constructor(sym::from_error, from_expr, unstable_span); - let thin_attrs = ThinVec::from(attrs); - let catch_scope = self.catch_scopes.last().map(|x| *x); - let ret_expr = if let Some(catch_node) = catch_scope { - let target_id = Ok(self.lower_node_id(catch_node)); - P(self.expr( - try_span, - hir::ExprKind::Break( - hir::Destination { - label: None, - target_id, - }, - Some(from_err_expr), - ), - thin_attrs, - )) - } else { - P(self.expr(try_span, hir::ExprKind::Ret(Some(from_err_expr)), thin_attrs)) - }; - - let err_pat = self.pat_err(try_span, err_local); - self.arm(hir_vec![err_pat], ret_expr) - }; - - hir::ExprKind::Match( - discr, - hir_vec![err_arm, ok_arm], - hir::MatchSource::TryDesugar, - ) - } - - ExprKind::Mac(_) => panic!("Shouldn't exist here"), - }; - - hir::Expr { - hir_id: self.lower_node_id(e.id), - node: kind, - span: e.span, - attrs: e.attrs.clone(), - } - } - fn lower_stmt(&mut self, s: &Stmt) -> SmallVec<[hir::Stmt; 1]> { let node = match s.node { StmtKind::Local(ref l) => { diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs new file mode 100644 index 00000000000..089fec51ca2 --- /dev/null +++ b/src/librustc/hir/lowering/expr.rs @@ -0,0 +1,813 @@ +use super::{LoweringContext, ParamMode, ParenthesizedGenericArgs, ImplTraitContext}; +use crate::hir::{self, HirVec}; +use crate::hir::ptr::P; + +use rustc_data_structures::thin_vec::ThinVec; + +use syntax::attr; +use syntax::ptr::P as AstP; +use syntax::ast::*; +use syntax::source_map::{respan, DesugaringKind}; +use syntax::symbol::{sym, Symbol}; + +impl LoweringContext<'_> { + fn lower_exprs(&mut self, exprs: &[AstP]) -> HirVec { + exprs.iter().map(|x| self.lower_expr(x)).collect() + } + + pub(super) fn lower_expr(&mut self, e: &Expr) -> hir::Expr { + let kind = match e.node { + ExprKind::Box(ref inner) => hir::ExprKind::Box(P(self.lower_expr(inner))), + ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), + ExprKind::Repeat(ref expr, ref count) => { + let expr = P(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 = P(self.lower_expr(f)); + hir::ExprKind::Call(f, self.lower_exprs(args)) + } + ExprKind::MethodCall(ref seg, ref args) => { + let hir_seg = P(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 = P(self.lower_expr(lhs)); + let rhs = P(self.lower_expr(rhs)); + hir::ExprKind::Binary(binop, lhs, rhs) + } + ExprKind::Unary(op, ref ohs) => { + let op = self.lower_unop(op); + let ohs = P(self.lower_expr(ohs)); + hir::ExprKind::Unary(op, ohs) + } + ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.node.clone())), + ExprKind::Cast(ref expr, ref ty) => { + let expr = P(self.lower_expr(expr)); + hir::ExprKind::Cast(expr, self.lower_ty(ty, ImplTraitContext::disallowed())) + } + ExprKind::Type(ref expr, ref ty) => { + let expr = P(self.lower_expr(expr)); + hir::ExprKind::Type(expr, self.lower_ty(ty, ImplTraitContext::disallowed())) + } + ExprKind::AddrOf(m, ref ohs) => { + let m = self.lower_mutability(m); + let ohs = P(self.lower_expr(ohs)); + hir::ExprKind::AddrOf(m, ohs) + } + ExprKind::Let(ref pats, ref scrutinee) => { + // If we got here, the `let` expression is not allowed. + self.sess + .struct_span_err(e.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(); + + // For better recovery, we emit: + // ``` + // match scrutinee { pats => 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 `pats` and `scrutinee`. + // 3. `pats` 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 pats = pats.iter().map(|pat| self.lower_pat(pat)).collect(); + let expr = self.expr_bool(e.span, true); + self.arm(pats, P(expr)) + }; + let else_arm = { + let pats = hir_vec![self.pat_wild(e.span)]; + let expr = self.expr_bool(e.span, false); + self.arm(pats, P(expr)) + }; + hir::ExprKind::Match( + P(scrutinee), + vec![then_arm, else_arm].into(), + hir::MatchSource::Normal, + ) + } + // FIXME(#53667): handle lowering of && and parens. + ExprKind::If(ref cond, ref then, ref else_opt) => { + // `_ => else_block` where `else_block` is `{}` if there's `None`: + let else_pat = self.pat_wild(e.span); + let (else_expr, contains_else_clause) = match else_opt { + None => (self.expr_block_empty(e.span), false), + Some(els) => (self.lower_expr(els), true), + }; + let else_arm = self.arm(hir_vec![else_pat], P(else_expr)); + + // Handle then + scrutinee: + let then_blk = self.lower_block(then, false); + let then_expr = self.expr_block(then_blk, ThinVec::new()); + let (then_pats, scrutinee, desugar) = match cond.node { + // ` => `: + ExprKind::Let(ref pats, ref scrutinee) => { + let scrutinee = self.lower_expr(scrutinee); + let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect(); + let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause }; + (pats, scrutinee, desugar) + } + // `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, P(cond), ThinVec::new()); + + let desugar = hir::MatchSource::IfDesugar { contains_else_clause }; + let pats = hir_vec![self.pat_bool(e.span, true)]; + (pats, cond, desugar) + } + }; + let then_arm = self.arm(then_pats, P(then_expr)); + + hir::ExprKind::Match(P(scrutinee), vec![then_arm, else_arm].into(), desugar) + } + // FIXME(#53667): handle lowering of && and parens. + ExprKind::While(ref cond, ref body, opt_label) => self.with_loop_scope(e.id, |this| { + // Note that the block AND the condition are evaluated in the loop scope. + // This is done to allow `break` from inside the condition of the loop. + + // `_ => break`: + let else_arm = { + let else_pat = this.pat_wild(e.span); + let else_expr = this.expr_break(e.span, ThinVec::new()); + this.arm(hir_vec![else_pat], else_expr) + }; + + // Handle then + scrutinee: + let then_blk = this.lower_block(body, false); + let then_expr = this.expr_block(then_blk, ThinVec::new()); + let (then_pats, scrutinee, desugar, source) = match cond.node { + ExprKind::Let(ref pats, ref scrutinee) => { + // to: + // + // [opt_ident]: loop { + // match { + // => , + // _ => break + // } + // } + let scrutinee = this.with_loop_condition_scope(|t| t.lower_expr(scrutinee)); + let pats = pats.iter().map(|pat| this.lower_pat(pat)).collect(); + let desugar = hir::MatchSource::WhileLetDesugar; + (pats, scrutinee, desugar, hir::LoopSource::WhileLet) + } + _ => { + // We desugar: `'label: while $cond $body` into: + // + // ``` + // 'label: loop { + // match DropTemps($cond) { + // true => $body, + // _ => break, + // } + // } + // ``` + + // Lower condition: + let cond = this.with_loop_condition_scope(|this| this.lower_expr(cond)); + let span_block = this.mark_span_with_reason( + DesugaringKind::CondTemporary, cond.span, None + ); + // 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`. + let cond = this.expr_drop_temps(span_block, P(cond), ThinVec::new()); + + let desugar = hir::MatchSource::WhileDesugar; + // `true => `: + let pats = hir_vec![this.pat_bool(e.span, true)]; + (pats, cond, desugar, hir::LoopSource::While) + } + }; + let then_arm = this.arm(then_pats, P(then_expr)); + + // `match { ... }` + let match_expr = this.expr_match( + scrutinee.span, + P(scrutinee), + hir_vec![then_arm, else_arm], + desugar, + ); + + // `[opt_ident]: loop { ... }` + hir::ExprKind::Loop( + P(this.block_expr(P(match_expr))), + this.lower_label(opt_label), + source + ) + }), + ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| { + hir::ExprKind::Loop( + this.lower_block(body, false), + this.lower_label(opt_label), + hir::LoopSource::Loop, + ) + }), + ExprKind::TryBlock(ref body) => { + self.with_catch_scope(body.id, |this| { + let unstable_span = this.mark_span_with_reason( + DesugaringKind::TryBlock, + body.span, + this.allow_try_trait.clone(), + ); + let mut block = this.lower_block(body, true).into_inner(); + let tail = block.expr.take().map_or_else( + || { + let span = this.sess.source_map().end_point(unstable_span); + hir::Expr { + span, + node: hir::ExprKind::Tup(hir_vec![]), + attrs: ThinVec::new(), + hir_id: this.next_id(), + } + }, + |x: P| x.into_inner(), + ); + block.expr = Some(this.wrap_in_try_constructor( + sym::from_ok, tail, unstable_span)); + hir::ExprKind::Block(P(block), None) + }) + } + ExprKind::Match(ref expr, ref arms) => hir::ExprKind::Match( + P(self.lower_expr(expr)), + arms.iter().map(|x| self.lower_arm(x)).collect(), + hir::MatchSource::Normal, + ), + ExprKind::Async(capture_clause, closure_node_id, ref block) => { + self.make_async_expr(capture_clause, closure_node_id, None, block.span, |this| { + this.with_new_scopes(|this| { + let block = this.lower_block(block, false); + this.expr_block(block, ThinVec::new()) + }) + }) + } + ExprKind::Await(ref expr) => self.lower_await(e.span, expr), + ExprKind::Closure( + capture_clause, asyncness, movability, ref decl, ref body, fn_decl_span + ) => { + if let IsAsync::Async { closure_id, .. } = asyncness { + let outer_decl = FnDecl { + inputs: decl.inputs.clone(), + output: FunctionRetTy::Default(fn_decl_span), + c_variadic: false, + }; + // We need to lower the declaration outside the new scope, because we + // have to conserve the state of being inside a loop condition for the + // closure argument types. + let fn_decl = self.lower_fn_decl(&outer_decl, None, false, None); + + self.with_new_scopes(|this| { + // FIXME(cramertj): allow `async` non-`move` closures with arguments. + if capture_clause == CaptureBy::Ref && + !decl.inputs.is_empty() + { + struct_span_err!( + this.sess, + fn_decl_span, + E0708, + "`async` non-`move` closures with arguments \ + are not currently supported", + ) + .help("consider using `let` statements to manually capture \ + variables by reference before entering an \ + `async move` closure") + .emit(); + } + + // Transform `async |x: u8| -> X { ... }` into + // `|x: u8| future_from_generator(|| -> X { ... })`. + let body_id = this.lower_fn_body(&outer_decl, |this| { + let async_ret_ty = if let FunctionRetTy::Ty(ty) = &decl.output { + Some(ty.clone()) + } else { None }; + let async_body = this.make_async_expr( + capture_clause, closure_id, async_ret_ty, body.span, + |this| { + this.with_new_scopes(|this| this.lower_expr(body)) + }); + this.expr(fn_decl_span, async_body, ThinVec::new()) + }); + hir::ExprKind::Closure( + this.lower_capture_clause(capture_clause), + fn_decl, + body_id, + fn_decl_span, + None, + ) + }) + } else { + // Lower outside new scope to preserve `is_in_loop_condition`. + let fn_decl = self.lower_fn_decl(decl, None, false, None); + + self.with_new_scopes(|this| { + this.current_item = Some(fn_decl_span); + let mut generator_kind = None; + let body_id = this.lower_fn_body(decl, |this| { + let e = this.lower_expr(body); + generator_kind = this.generator_kind; + e + }); + let generator_option = this.generator_movability_for_fn( + &decl, + fn_decl_span, + generator_kind, + movability, + ); + hir::ExprKind::Closure( + this.lower_capture_clause(capture_clause), + fn_decl, + body_id, + fn_decl_span, + generator_option, + ) + }) + } + } + ExprKind::Block(ref blk, opt_label) => { + hir::ExprKind::Block(self.lower_block(blk, + opt_label.is_some()), + self.lower_label(opt_label)) + } + ExprKind::Assign(ref el, ref er) => { + hir::ExprKind::Assign(P(self.lower_expr(el)), P(self.lower_expr(er))) + } + ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp( + self.lower_binop(op), + P(self.lower_expr(el)), + P(self.lower_expr(er)), + ), + ExprKind::Field(ref el, ident) => hir::ExprKind::Field(P(self.lower_expr(el)), ident), + ExprKind::Index(ref el, ref er) => { + hir::ExprKind::Index(P(self.lower_expr(el)), P(self.lower_expr(er))) + } + // Desugar `..=` into `std::ops::RangeInclusive::new(, )`. + ExprKind::Range(Some(ref e1), Some(ref e2), RangeLimits::Closed) => { + let id = self.next_id(); + let e1 = self.lower_expr(e1); + let e2 = self.lower_expr(e2); + self.expr_call_std_assoc_fn( + id, + e.span, + &[sym::ops, sym::RangeInclusive], + "new", + hir_vec![e1, e2], + ) + } + ExprKind::Range(ref e1, ref e2, lims) => { + use syntax::ast::RangeLimits::*; + + let path = match (e1, e2, lims) { + (&None, &None, HalfOpen) => sym::RangeFull, + (&Some(..), &None, HalfOpen) => sym::RangeFrom, + (&None, &Some(..), HalfOpen) => sym::RangeTo, + (&Some(..), &Some(..), HalfOpen) => sym::Range, + (&None, &Some(..), Closed) => sym::RangeToInclusive, + (&Some(..), &Some(..), Closed) => unreachable!(), + (_, &None, Closed) => self.diagnostic() + .span_fatal(e.span, "inclusive range with no end") + .raise(), + }; + + let fields = e1.iter() + .map(|e| ("start", e)) + .chain(e2.iter().map(|e| ("end", e))) + .map(|(s, e)| { + let expr = P(self.lower_expr(&e)); + let ident = Ident::new(Symbol::intern(s), e.span); + self.field(ident, expr, e.span) + }) + .collect::>(); + + let is_unit = fields.is_empty(); + let struct_path = [sym::ops, path]; + let struct_path = self.std_path(e.span, &struct_path, None, is_unit); + let struct_path = hir::QPath::Resolved(None, P(struct_path)); + + return hir::Expr { + hir_id: self.lower_node_id(e.id), + node: if is_unit { + hir::ExprKind::Path(struct_path) + } else { + hir::ExprKind::Struct(P(struct_path), fields, None) + }, + span: e.span, + attrs: e.attrs.clone(), + }; + } + 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 destination = if self.is_in_loop_condition && opt_label.is_none() { + hir::Destination { + label: None, + target_id: Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into(), + } + } else { + self.lower_loop_destination(opt_label.map(|label| (e.id, label))) + }; + hir::ExprKind::Break( + destination, + opt_expr.as_ref().map(|x| P(self.lower_expr(x))), + ) + } + ExprKind::Continue(opt_label) => { + hir::ExprKind::Continue(if self.is_in_loop_condition && opt_label.is_none() { + hir::Destination { + label: None, + target_id: Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into(), + } + } else { + self.lower_loop_destination(opt_label.map(|label| (e.id, label))) + }) + } + ExprKind::Ret(ref e) => hir::ExprKind::Ret(e.as_ref().map(|x| P(self.lower_expr(x)))), + ExprKind::InlineAsm(ref asm) => { + let hir_asm = hir::InlineAsm { + inputs: asm.inputs.iter().map(|&(ref c, _)| c.clone()).collect(), + outputs: asm.outputs + .iter() + .map(|out| hir::InlineAsmOutput { + constraint: out.constraint.clone(), + is_rw: out.is_rw, + is_indirect: out.is_indirect, + span: out.expr.span, + }) + .collect(), + asm: asm.asm.clone(), + asm_str_style: asm.asm_str_style, + clobbers: asm.clobbers.clone().into(), + volatile: asm.volatile, + alignstack: asm.alignstack, + dialect: asm.dialect, + ctxt: asm.ctxt, + }; + let outputs = asm.outputs + .iter() + .map(|out| self.lower_expr(&out.expr)) + .collect(); + let inputs = asm.inputs + .iter() + .map(|&(_, ref input)| self.lower_expr(input)) + .collect(); + hir::ExprKind::InlineAsm(P(hir_asm), outputs, inputs) + } + ExprKind::Struct(ref path, ref fields, ref maybe_expr) => hir::ExprKind::Struct( + P(self.lower_qpath( + e.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + )), + fields.iter().map(|x| self.lower_field(x)).collect(), + maybe_expr.as_ref().map(|x| P(self.lower_expr(x))), + ), + ExprKind::Paren(ref ex) => { + let mut ex = self.lower_expr(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) => { + match self.generator_kind { + Some(hir::GeneratorKind::Gen) => {}, + Some(hir::GeneratorKind::Async) => { + span_err!( + self.sess, + e.span, + E0727, + "`async` generators are not yet supported", + ); + self.sess.abort_if_errors(); + }, + None => { + self.generator_kind = Some(hir::GeneratorKind::Gen); + } + } + let expr = opt_expr + .as_ref() + .map(|x| self.lower_expr(x)) + .unwrap_or_else(|| self.expr_unit(e.span)); + hir::ExprKind::Yield(P(expr), hir::YieldSource::Yield) + } + + ExprKind::Err => hir::ExprKind::Err, + + // Desugar `ExprForLoop` + // from: `[opt_ident]: for in ` + ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => { + // to: + // + // { + // let result = match ::std::iter::IntoIterator::into_iter() { + // mut iter => { + // [opt_ident]: loop { + // let mut __next; + // match ::std::iter::Iterator::next(&mut iter) { + // ::std::option::Option::Some(val) => __next = val, + // ::std::option::Option::None => break + // }; + // let = __next; + // StmtKind::Expr(); + // } + // } + // }; + // result + // } + + // expand + let mut head = self.lower_expr(head); + let head_sp = head.span; + let desugared_span = self.mark_span_with_reason( + DesugaringKind::ForLoop, + head_sp, + None, + ); + head.span = desugared_span; + + let iter = Ident::with_empty_ctxt(sym::iter); + + let next_ident = Ident::with_empty_ctxt(sym::__next); + let (next_pat, next_pat_hid) = self.pat_ident_binding_mode( + desugared_span, + next_ident, + hir::BindingAnnotation::Mutable, + ); + + // `::std::option::Option::Some(val) => __next = val` + let pat_arm = { + let val_ident = Ident::with_empty_ctxt(sym::val); + let (val_pat, val_pat_hid) = self.pat_ident(pat.span, val_ident); + let val_expr = P(self.expr_ident(pat.span, val_ident, val_pat_hid)); + let next_expr = P(self.expr_ident(pat.span, next_ident, next_pat_hid)); + let assign = P(self.expr( + pat.span, + hir::ExprKind::Assign(next_expr, val_expr), + ThinVec::new(), + )); + let some_pat = self.pat_some(pat.span, val_pat); + self.arm(hir_vec![some_pat], assign) + }; + + // `::std::option::Option::None => break` + let break_arm = { + let break_expr = + self.with_loop_scope(e.id, |this| this.expr_break(e.span, ThinVec::new())); + let pat = self.pat_none(e.span); + self.arm(hir_vec![pat], break_expr) + }; + + // `mut iter` + let (iter_pat, iter_pat_nid) = self.pat_ident_binding_mode( + desugared_span, + iter, + hir::BindingAnnotation::Mutable + ); + + // `match ::std::iter::Iterator::next(&mut iter) { ... }` + let match_expr = { + let iter = P(self.expr_ident(head_sp, iter, iter_pat_nid)); + let ref_mut_iter = self.expr_mut_addr_of(head_sp, iter); + let next_path = &[sym::iter, sym::Iterator, sym::next]; + let next_expr = P(self.expr_call_std_path( + head_sp, + next_path, + hir_vec![ref_mut_iter], + )); + let arms = hir_vec![pat_arm, break_arm]; + + self.expr_match(head_sp, next_expr, arms, hir::MatchSource::ForLoopDesugar) + }; + let match_stmt = self.stmt_expr(head_sp, match_expr); + + let next_expr = P(self.expr_ident(head_sp, next_ident, next_pat_hid)); + + // `let mut __next` + let next_let = self.stmt_let_pat( + ThinVec::new(), + desugared_span, + None, + next_pat, + hir::LocalSource::ForLoopDesugar, + ); + + // `let = __next` + let pat = self.lower_pat(pat); + let pat_let = self.stmt_let_pat( + ThinVec::new(), + head_sp, + Some(next_expr), + pat, + hir::LocalSource::ForLoopDesugar, + ); + + let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false)); + let body_expr = self.expr_block(body_block, ThinVec::new()); + let body_stmt = self.stmt_expr(body.span, body_expr); + + let loop_block = P(self.block_all( + e.span, + hir_vec![next_let, match_stmt, pat_let, body_stmt], + None, + )); + + // `[opt_ident]: loop { ... }` + let loop_expr = hir::ExprKind::Loop( + loop_block, + self.lower_label(opt_label), + hir::LoopSource::ForLoop, + ); + let loop_expr = P(hir::Expr { + hir_id: self.lower_node_id(e.id), + node: loop_expr, + span: e.span, + attrs: ThinVec::new(), + }); + + // `mut iter => { ... }` + let iter_arm = self.arm(hir_vec![iter_pat], loop_expr); + + // `match ::std::iter::IntoIterator::into_iter() { ... }` + let into_iter_expr = { + let into_iter_path = + &[sym::iter, sym::IntoIterator, sym::into_iter]; + P(self.expr_call_std_path( + head_sp, + into_iter_path, + hir_vec![head], + )) + }; + + let match_expr = P(self.expr_match( + head_sp, + into_iter_expr, + hir_vec![iter_arm], + hir::MatchSource::ForLoopDesugar, + )); + + // This is effectively `{ let _result = ...; _result }`. + // The construct was introduced in #21984 and is necessary to make sure that + // temporaries in the `head` expression are dropped and do not leak to the + // surrounding scope of the `match` since the `match` is not a terminating scope. + // + // Also, add the attributes to the outer returned expr node. + return self.expr_drop_temps(head_sp, match_expr, e.attrs.clone()) + } + + // Desugar `ExprKind::Try` + // from: `?` + ExprKind::Try(ref sub_expr) => { + // into: + // + // match Try::into_result() { + // Ok(val) => #[allow(unreachable_code)] val, + // Err(err) => #[allow(unreachable_code)] + // // If there is an enclosing `catch {...}` + // break 'catch_target Try::from_error(From::from(err)), + // // Otherwise + // return Try::from_error(From::from(err)), + // } + + let unstable_span = self.mark_span_with_reason( + DesugaringKind::QuestionMark, + e.span, + self.allow_try_trait.clone(), + ); + let try_span = self.sess.source_map().end_point(e.span); + let try_span = self.mark_span_with_reason( + DesugaringKind::QuestionMark, + try_span, + self.allow_try_trait.clone(), + ); + + // `Try::into_result()` + let discr = { + // expand + let sub_expr = self.lower_expr(sub_expr); + + let path = &[sym::ops, sym::Try, sym::into_result]; + P(self.expr_call_std_path( + unstable_span, + path, + hir_vec![sub_expr], + )) + }; + + // `#[allow(unreachable_code)]` + let attr = { + // `allow(unreachable_code)` + let allow = { + let allow_ident = Ident::new(sym::allow, e.span); + let uc_ident = Ident::new(sym::unreachable_code, e.span); + let uc_nested = attr::mk_nested_word_item(uc_ident); + attr::mk_list_item(allow_ident, vec![uc_nested]) + }; + attr::mk_attr_outer(allow) + }; + let attrs = vec![attr]; + + // `Ok(val) => #[allow(unreachable_code)] val,` + let ok_arm = { + let val_ident = Ident::with_empty_ctxt(sym::val); + let (val_pat, val_pat_nid) = self.pat_ident(e.span, val_ident); + let val_expr = P(self.expr_ident_with_attrs( + e.span, + val_ident, + val_pat_nid, + ThinVec::from(attrs.clone()), + )); + let ok_pat = self.pat_ok(e.span, val_pat); + + self.arm(hir_vec![ok_pat], val_expr) + }; + + // `Err(err) => #[allow(unreachable_code)] + // return Try::from_error(From::from(err)),` + let err_arm = { + let err_ident = Ident::with_empty_ctxt(sym::err); + let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident); + let from_expr = { + let from_path = &[sym::convert, sym::From, sym::from]; + let err_expr = self.expr_ident(try_span, err_ident, err_local_nid); + self.expr_call_std_path(try_span, from_path, hir_vec![err_expr]) + }; + let from_err_expr = + self.wrap_in_try_constructor(sym::from_error, from_expr, unstable_span); + let thin_attrs = ThinVec::from(attrs); + let catch_scope = self.catch_scopes.last().map(|x| *x); + let ret_expr = if let Some(catch_node) = catch_scope { + let target_id = Ok(self.lower_node_id(catch_node)); + P(self.expr( + try_span, + hir::ExprKind::Break( + hir::Destination { + label: None, + target_id, + }, + Some(from_err_expr), + ), + thin_attrs, + )) + } else { + P(self.expr(try_span, hir::ExprKind::Ret(Some(from_err_expr)), thin_attrs)) + }; + + let err_pat = self.pat_err(try_span, err_local); + self.arm(hir_vec![err_pat], ret_expr) + }; + + hir::ExprKind::Match( + discr, + hir_vec![err_arm, ok_arm], + hir::MatchSource::TryDesugar, + ) + } + + ExprKind::Mac(_) => panic!("Shouldn't exist here"), + }; + + hir::Expr { + hir_id: self.lower_node_id(e.id), + node: kind, + span: e.span, + attrs: e.attrs.clone(), + } + } +} From c817596c694579667d3e2ee207064ae273e84940 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sat, 10 Aug 2019 14:14:53 +0200 Subject: [PATCH 02/25] lowering: extract lower_expr_try --- src/librustc/hir/lowering/expr.rs | 215 +++++++++++++++--------------- 1 file changed, 105 insertions(+), 110 deletions(-) diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs index 089fec51ca2..90bc6a3ad77 100644 --- a/src/librustc/hir/lowering/expr.rs +++ b/src/librustc/hir/lowering/expr.rs @@ -7,7 +7,7 @@ use rustc_data_structures::thin_vec::ThinVec; use syntax::attr; use syntax::ptr::P as AstP; use syntax::ast::*; -use syntax::source_map::{respan, DesugaringKind}; +use syntax::source_map::{respan, DesugaringKind, Span}; use syntax::symbol::{sym, Symbol}; impl LoweringContext<'_> { @@ -691,115 +691,7 @@ impl LoweringContext<'_> { return self.expr_drop_temps(head_sp, match_expr, e.attrs.clone()) } - // Desugar `ExprKind::Try` - // from: `?` - ExprKind::Try(ref sub_expr) => { - // into: - // - // match Try::into_result() { - // Ok(val) => #[allow(unreachable_code)] val, - // Err(err) => #[allow(unreachable_code)] - // // If there is an enclosing `catch {...}` - // break 'catch_target Try::from_error(From::from(err)), - // // Otherwise - // return Try::from_error(From::from(err)), - // } - - let unstable_span = self.mark_span_with_reason( - DesugaringKind::QuestionMark, - e.span, - self.allow_try_trait.clone(), - ); - let try_span = self.sess.source_map().end_point(e.span); - let try_span = self.mark_span_with_reason( - DesugaringKind::QuestionMark, - try_span, - self.allow_try_trait.clone(), - ); - - // `Try::into_result()` - let discr = { - // expand - let sub_expr = self.lower_expr(sub_expr); - - let path = &[sym::ops, sym::Try, sym::into_result]; - P(self.expr_call_std_path( - unstable_span, - path, - hir_vec![sub_expr], - )) - }; - - // `#[allow(unreachable_code)]` - let attr = { - // `allow(unreachable_code)` - let allow = { - let allow_ident = Ident::new(sym::allow, e.span); - let uc_ident = Ident::new(sym::unreachable_code, e.span); - let uc_nested = attr::mk_nested_word_item(uc_ident); - attr::mk_list_item(allow_ident, vec![uc_nested]) - }; - attr::mk_attr_outer(allow) - }; - let attrs = vec![attr]; - - // `Ok(val) => #[allow(unreachable_code)] val,` - let ok_arm = { - let val_ident = Ident::with_empty_ctxt(sym::val); - let (val_pat, val_pat_nid) = self.pat_ident(e.span, val_ident); - let val_expr = P(self.expr_ident_with_attrs( - e.span, - val_ident, - val_pat_nid, - ThinVec::from(attrs.clone()), - )); - let ok_pat = self.pat_ok(e.span, val_pat); - - self.arm(hir_vec![ok_pat], val_expr) - }; - - // `Err(err) => #[allow(unreachable_code)] - // return Try::from_error(From::from(err)),` - let err_arm = { - let err_ident = Ident::with_empty_ctxt(sym::err); - let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident); - let from_expr = { - let from_path = &[sym::convert, sym::From, sym::from]; - let err_expr = self.expr_ident(try_span, err_ident, err_local_nid); - self.expr_call_std_path(try_span, from_path, hir_vec![err_expr]) - }; - let from_err_expr = - self.wrap_in_try_constructor(sym::from_error, from_expr, unstable_span); - let thin_attrs = ThinVec::from(attrs); - let catch_scope = self.catch_scopes.last().map(|x| *x); - let ret_expr = if let Some(catch_node) = catch_scope { - let target_id = Ok(self.lower_node_id(catch_node)); - P(self.expr( - try_span, - hir::ExprKind::Break( - hir::Destination { - label: None, - target_id, - }, - Some(from_err_expr), - ), - thin_attrs, - )) - } else { - P(self.expr(try_span, hir::ExprKind::Ret(Some(from_err_expr)), thin_attrs)) - }; - - let err_pat = self.pat_err(try_span, err_local); - self.arm(hir_vec![err_pat], ret_expr) - }; - - hir::ExprKind::Match( - discr, - hir_vec![err_arm, ok_arm], - hir::MatchSource::TryDesugar, - ) - } - + ExprKind::Try(ref sub_expr) => self.lower_expr_try(e.span, sub_expr), ExprKind::Mac(_) => panic!("Shouldn't exist here"), }; @@ -810,4 +702,107 @@ impl LoweringContext<'_> { attrs: e.attrs.clone(), } } + + /// Desugar `ExprKind::Try` from: `?` into: + /// ```rust + /// match Try::into_result() { + /// Ok(val) => #[allow(unreachable_code)] val, + /// Err(err) => #[allow(unreachable_code)] + /// // If there is an enclosing `try {...}`: + /// break 'catch_target Try::from_error(From::from(err)), + /// // Otherwise: + /// return Try::from_error(From::from(err)), + /// } + /// ``` + fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind { + let unstable_span = self.mark_span_with_reason( + DesugaringKind::QuestionMark, + span, + self.allow_try_trait.clone(), + ); + let try_span = self.sess.source_map().end_point(span); + let try_span = self.mark_span_with_reason( + DesugaringKind::QuestionMark, + try_span, + self.allow_try_trait.clone(), + ); + + // `Try::into_result()` + let scrutinee = { + // expand + let sub_expr = self.lower_expr(sub_expr); + + let path = &[sym::ops, sym::Try, sym::into_result]; + P(self.expr_call_std_path(unstable_span, path, hir_vec![sub_expr])) + }; + + // `#[allow(unreachable_code)]` + let attr = { + // `allow(unreachable_code)` + let allow = { + let allow_ident = Ident::new(sym::allow, span); + let uc_ident = Ident::new(sym::unreachable_code, span); + let uc_nested = attr::mk_nested_word_item(uc_ident); + attr::mk_list_item(allow_ident, vec![uc_nested]) + }; + attr::mk_attr_outer(allow) + }; + let attrs = vec![attr]; + + // `Ok(val) => #[allow(unreachable_code)] val,` + let ok_arm = { + let val_ident = Ident::with_empty_ctxt(sym::val); + let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident); + let val_expr = P(self.expr_ident_with_attrs( + span, + val_ident, + val_pat_nid, + ThinVec::from(attrs.clone()), + )); + let ok_pat = self.pat_ok(span, val_pat); + + self.arm(hir_vec![ok_pat], val_expr) + }; + + // `Err(err) => #[allow(unreachable_code)] + // return Try::from_error(From::from(err)),` + let err_arm = { + let err_ident = Ident::with_empty_ctxt(sym::err); + let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident); + let from_expr = { + let from_path = &[sym::convert, sym::From, sym::from]; + let err_expr = self.expr_ident(try_span, err_ident, err_local_nid); + self.expr_call_std_path(try_span, from_path, hir_vec![err_expr]) + }; + let from_err_expr = + self.wrap_in_try_constructor(sym::from_error, from_expr, unstable_span); + let thin_attrs = ThinVec::from(attrs); + let catch_scope = self.catch_scopes.last().map(|x| *x); + let ret_expr = if let Some(catch_node) = catch_scope { + let target_id = Ok(self.lower_node_id(catch_node)); + P(self.expr( + try_span, + hir::ExprKind::Break( + hir::Destination { + label: None, + target_id, + }, + Some(from_err_expr), + ), + thin_attrs, + )) + } else { + P(self.expr(try_span, hir::ExprKind::Ret(Some(from_err_expr)), thin_attrs)) + }; + + let err_pat = self.pat_err(try_span, err_local); + self.arm(hir_vec![err_pat], ret_expr) + }; + + hir::ExprKind::Match( + scrutinee, + hir_vec![err_arm, ok_arm], + hir::MatchSource::TryDesugar, + ) + } } From 9d739ca12d88c3985cd78d4af027d48ce2254e18 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sat, 10 Aug 2019 14:16:57 +0200 Subject: [PATCH 03/25] lowering: extract lower_expr_for --- src/librustc/hir/lowering/expr.rs | 324 +++++++++++++++--------------- 1 file changed, 167 insertions(+), 157 deletions(-) diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs index 90bc6a3ad77..0c626d6b15f 100644 --- a/src/librustc/hir/lowering/expr.rs +++ b/src/librustc/hir/lowering/expr.rs @@ -533,164 +533,8 @@ impl LoweringContext<'_> { // Desugar `ExprForLoop` // from: `[opt_ident]: for in ` ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => { - // to: - // - // { - // let result = match ::std::iter::IntoIterator::into_iter() { - // mut iter => { - // [opt_ident]: loop { - // let mut __next; - // match ::std::iter::Iterator::next(&mut iter) { - // ::std::option::Option::Some(val) => __next = val, - // ::std::option::Option::None => break - // }; - // let = __next; - // StmtKind::Expr(); - // } - // } - // }; - // result - // } - - // expand - let mut head = self.lower_expr(head); - let head_sp = head.span; - let desugared_span = self.mark_span_with_reason( - DesugaringKind::ForLoop, - head_sp, - None, - ); - head.span = desugared_span; - - let iter = Ident::with_empty_ctxt(sym::iter); - - let next_ident = Ident::with_empty_ctxt(sym::__next); - let (next_pat, next_pat_hid) = self.pat_ident_binding_mode( - desugared_span, - next_ident, - hir::BindingAnnotation::Mutable, - ); - - // `::std::option::Option::Some(val) => __next = val` - let pat_arm = { - let val_ident = Ident::with_empty_ctxt(sym::val); - let (val_pat, val_pat_hid) = self.pat_ident(pat.span, val_ident); - let val_expr = P(self.expr_ident(pat.span, val_ident, val_pat_hid)); - let next_expr = P(self.expr_ident(pat.span, next_ident, next_pat_hid)); - let assign = P(self.expr( - pat.span, - hir::ExprKind::Assign(next_expr, val_expr), - ThinVec::new(), - )); - let some_pat = self.pat_some(pat.span, val_pat); - self.arm(hir_vec![some_pat], assign) - }; - - // `::std::option::Option::None => break` - let break_arm = { - let break_expr = - self.with_loop_scope(e.id, |this| this.expr_break(e.span, ThinVec::new())); - let pat = self.pat_none(e.span); - self.arm(hir_vec![pat], break_expr) - }; - - // `mut iter` - let (iter_pat, iter_pat_nid) = self.pat_ident_binding_mode( - desugared_span, - iter, - hir::BindingAnnotation::Mutable - ); - - // `match ::std::iter::Iterator::next(&mut iter) { ... }` - let match_expr = { - let iter = P(self.expr_ident(head_sp, iter, iter_pat_nid)); - let ref_mut_iter = self.expr_mut_addr_of(head_sp, iter); - let next_path = &[sym::iter, sym::Iterator, sym::next]; - let next_expr = P(self.expr_call_std_path( - head_sp, - next_path, - hir_vec![ref_mut_iter], - )); - let arms = hir_vec![pat_arm, break_arm]; - - self.expr_match(head_sp, next_expr, arms, hir::MatchSource::ForLoopDesugar) - }; - let match_stmt = self.stmt_expr(head_sp, match_expr); - - let next_expr = P(self.expr_ident(head_sp, next_ident, next_pat_hid)); - - // `let mut __next` - let next_let = self.stmt_let_pat( - ThinVec::new(), - desugared_span, - None, - next_pat, - hir::LocalSource::ForLoopDesugar, - ); - - // `let = __next` - let pat = self.lower_pat(pat); - let pat_let = self.stmt_let_pat( - ThinVec::new(), - head_sp, - Some(next_expr), - pat, - hir::LocalSource::ForLoopDesugar, - ); - - let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false)); - let body_expr = self.expr_block(body_block, ThinVec::new()); - let body_stmt = self.stmt_expr(body.span, body_expr); - - let loop_block = P(self.block_all( - e.span, - hir_vec![next_let, match_stmt, pat_let, body_stmt], - None, - )); - - // `[opt_ident]: loop { ... }` - let loop_expr = hir::ExprKind::Loop( - loop_block, - self.lower_label(opt_label), - hir::LoopSource::ForLoop, - ); - let loop_expr = P(hir::Expr { - hir_id: self.lower_node_id(e.id), - node: loop_expr, - span: e.span, - attrs: ThinVec::new(), - }); - - // `mut iter => { ... }` - let iter_arm = self.arm(hir_vec![iter_pat], loop_expr); - - // `match ::std::iter::IntoIterator::into_iter() { ... }` - let into_iter_expr = { - let into_iter_path = - &[sym::iter, sym::IntoIterator, sym::into_iter]; - P(self.expr_call_std_path( - head_sp, - into_iter_path, - hir_vec![head], - )) - }; - - let match_expr = P(self.expr_match( - head_sp, - into_iter_expr, - hir_vec![iter_arm], - hir::MatchSource::ForLoopDesugar, - )); - - // This is effectively `{ let _result = ...; _result }`. - // The construct was introduced in #21984 and is necessary to make sure that - // temporaries in the `head` expression are dropped and do not leak to the - // surrounding scope of the `match` since the `match` is not a terminating scope. - // - // Also, add the attributes to the outer returned expr node. - return self.expr_drop_temps(head_sp, match_expr, e.attrs.clone()) + 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::Mac(_) => panic!("Shouldn't exist here"), }; @@ -703,6 +547,172 @@ impl LoweringContext<'_> { } } + /// Desugar `ExprForLoop` from: `[opt_ident]: for in ` into: + /// ```rust + /// { + /// let result = match ::std::iter::IntoIterator::into_iter() { + /// mut iter => { + /// [opt_ident]: loop { + /// let mut __next; + /// match ::std::iter::Iterator::next(&mut iter) { + /// ::std::option::Option::Some(val) => __next = val, + /// ::std::option::Option::None => break + /// }; + /// let = __next; + /// StmtKind::Expr(); + /// } + /// } + /// }; + /// result + /// } + /// ``` + fn lower_expr_for( + &mut self, + e: &Expr, + pat: &Pat, + head: &Expr, + body: &Block, + opt_label: Option