]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/lowering/expr.rs
lowering: extract lower_expr_async_closure
[rust.git] / src / librustc / hir / lowering / expr.rs
1 use super::{LoweringContext, ParamMode, ParenthesizedGenericArgs, ImplTraitContext};
2 use crate::hir::{self, HirVec};
3 use crate::hir::ptr::P;
4
5 use rustc_data_structures::thin_vec::ThinVec;
6
7 use syntax::attr;
8 use syntax::ptr::P as AstP;
9 use syntax::ast::*;
10 use syntax::source_map::{respan, DesugaringKind, Span};
11 use syntax::symbol::{sym, Symbol};
12
13 impl LoweringContext<'_> {
14     fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> HirVec<hir::Expr> {
15         exprs.iter().map(|x| self.lower_expr(x)).collect()
16     }
17
18     pub(super) fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
19         let kind = match e.node {
20             ExprKind::Box(ref inner) => hir::ExprKind::Box(P(self.lower_expr(inner))),
21             ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
22             ExprKind::Repeat(ref expr, ref count) => {
23                 let expr = P(self.lower_expr(expr));
24                 let count = self.lower_anon_const(count);
25                 hir::ExprKind::Repeat(expr, count)
26             }
27             ExprKind::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)),
28             ExprKind::Call(ref f, ref args) => {
29                 let f = P(self.lower_expr(f));
30                 hir::ExprKind::Call(f, self.lower_exprs(args))
31             }
32             ExprKind::MethodCall(ref seg, ref args) => {
33                 let hir_seg = P(self.lower_path_segment(
34                     e.span,
35                     seg,
36                     ParamMode::Optional,
37                     0,
38                     ParenthesizedGenericArgs::Err,
39                     ImplTraitContext::disallowed(),
40                     None,
41                 ));
42                 let args = self.lower_exprs(args);
43                 hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args)
44             }
45             ExprKind::Binary(binop, ref lhs, ref rhs) => {
46                 let binop = self.lower_binop(binop);
47                 let lhs = P(self.lower_expr(lhs));
48                 let rhs = P(self.lower_expr(rhs));
49                 hir::ExprKind::Binary(binop, lhs, rhs)
50             }
51             ExprKind::Unary(op, ref ohs) => {
52                 let op = self.lower_unop(op);
53                 let ohs = P(self.lower_expr(ohs));
54                 hir::ExprKind::Unary(op, ohs)
55             }
56             ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.node.clone())),
57             ExprKind::Cast(ref expr, ref ty) => {
58                 let expr = P(self.lower_expr(expr));
59                 hir::ExprKind::Cast(expr, self.lower_ty(ty, ImplTraitContext::disallowed()))
60             }
61             ExprKind::Type(ref expr, ref ty) => {
62                 let expr = P(self.lower_expr(expr));
63                 hir::ExprKind::Type(expr, self.lower_ty(ty, ImplTraitContext::disallowed()))
64             }
65             ExprKind::AddrOf(m, ref ohs) => {
66                 let m = self.lower_mutability(m);
67                 let ohs = P(self.lower_expr(ohs));
68                 hir::ExprKind::AddrOf(m, ohs)
69             }
70             ExprKind::Let(ref pats, ref scrutinee) => {
71                 // If we got here, the `let` expression is not allowed.
72                 self.sess
73                     .struct_span_err(e.span, "`let` expressions are not supported here")
74                     .note("only supported directly in conditions of `if`- and `while`-expressions")
75                     .note("as well as when nested within `&&` and parenthesis in those conditions")
76                     .emit();
77
78                 // For better recovery, we emit:
79                 // ```
80                 // match scrutinee { pats => true, _ => false }
81                 // ```
82                 // While this doesn't fully match the user's intent, it has key advantages:
83                 // 1. We can avoid using `abort_if_errors`.
84                 // 2. We can typeck both `pats` and `scrutinee`.
85                 // 3. `pats` is allowed to be refutable.
86                 // 4. The return type of the block is `bool` which seems like what the user wanted.
87                 let scrutinee = self.lower_expr(scrutinee);
88                 let then_arm = {
89                     let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
90                     let expr = self.expr_bool(e.span, true);
91                     self.arm(pats, P(expr))
92                 };
93                 let else_arm = {
94                     let pats = hir_vec![self.pat_wild(e.span)];
95                     let expr = self.expr_bool(e.span, false);
96                     self.arm(pats, P(expr))
97                 };
98                 hir::ExprKind::Match(
99                     P(scrutinee),
100                     vec![then_arm, else_arm].into(),
101                     hir::MatchSource::Normal,
102                 )
103             }
104             // FIXME(#53667): handle lowering of && and parens.
105             ExprKind::If(ref cond, ref then, ref else_opt) => {
106                 // `_ => else_block` where `else_block` is `{}` if there's `None`:
107                 let else_pat = self.pat_wild(e.span);
108                 let (else_expr, contains_else_clause) = match else_opt {
109                     None => (self.expr_block_empty(e.span), false),
110                     Some(els) => (self.lower_expr(els), true),
111                 };
112                 let else_arm = self.arm(hir_vec![else_pat], P(else_expr));
113
114                 // Handle then + scrutinee:
115                 let then_blk = self.lower_block(then, false);
116                 let then_expr = self.expr_block(then_blk, ThinVec::new());
117                 let (then_pats, scrutinee, desugar) = match cond.node {
118                     // `<pat> => <then>`:
119                     ExprKind::Let(ref pats, ref scrutinee) => {
120                         let scrutinee = self.lower_expr(scrutinee);
121                         let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
122                         let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause };
123                         (pats, scrutinee, desugar)
124                     }
125                     // `true => <then>`:
126                     _ => {
127                         // Lower condition:
128                         let cond = self.lower_expr(cond);
129                         let span_block = self.mark_span_with_reason(
130                             DesugaringKind::CondTemporary, cond.span, None
131                         );
132                         // Wrap in a construct equivalent to `{ let _t = $cond; _t }`
133                         // to preserve drop semantics since `if cond { ... }` does not
134                         // let temporaries live outside of `cond`.
135                         let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
136
137                         let desugar = hir::MatchSource::IfDesugar { contains_else_clause };
138                         let pats = hir_vec![self.pat_bool(e.span, true)];
139                         (pats, cond, desugar)
140                     }
141                 };
142                 let then_arm = self.arm(then_pats, P(then_expr));
143
144                 hir::ExprKind::Match(P(scrutinee), vec![then_arm, else_arm].into(), desugar)
145             }
146             // FIXME(#53667): handle lowering of && and parens.
147             ExprKind::While(ref cond, ref body, opt_label) => self.with_loop_scope(e.id, |this| {
148                 // Note that the block AND the condition are evaluated in the loop scope.
149                 // This is done to allow `break` from inside the condition of the loop.
150
151                 // `_ => break`:
152                 let else_arm = {
153                     let else_pat = this.pat_wild(e.span);
154                     let else_expr = this.expr_break(e.span, ThinVec::new());
155                     this.arm(hir_vec![else_pat], else_expr)
156                 };
157
158                 // Handle then + scrutinee:
159                 let then_blk = this.lower_block(body, false);
160                 let then_expr = this.expr_block(then_blk, ThinVec::new());
161                 let (then_pats, scrutinee, desugar, source) = match cond.node {
162                     ExprKind::Let(ref pats, ref scrutinee) => {
163                         // to:
164                         //
165                         //   [opt_ident]: loop {
166                         //     match <sub_expr> {
167                         //       <pat> => <body>,
168                         //       _ => break
169                         //     }
170                         //   }
171                         let scrutinee = this.with_loop_condition_scope(|t| t.lower_expr(scrutinee));
172                         let pats = pats.iter().map(|pat| this.lower_pat(pat)).collect();
173                         let desugar = hir::MatchSource::WhileLetDesugar;
174                         (pats, scrutinee, desugar, hir::LoopSource::WhileLet)
175                     }
176                     _ => {
177                         // We desugar: `'label: while $cond $body` into:
178                         //
179                         // ```
180                         // 'label: loop {
181                         //     match DropTemps($cond) {
182                         //         true => $body,
183                         //         _ => break,
184                         //     }
185                         // }
186                         // ```
187
188                         // Lower condition:
189                         let cond = this.with_loop_condition_scope(|this| this.lower_expr(cond));
190                         let span_block = this.mark_span_with_reason(
191                             DesugaringKind::CondTemporary, cond.span, None
192                         );
193                         // Wrap in a construct equivalent to `{ let _t = $cond; _t }`
194                         // to preserve drop semantics since `while cond { ... }` does not
195                         // let temporaries live outside of `cond`.
196                         let cond = this.expr_drop_temps(span_block, P(cond), ThinVec::new());
197
198                         let desugar = hir::MatchSource::WhileDesugar;
199                         // `true => <then>`:
200                         let pats = hir_vec![this.pat_bool(e.span, true)];
201                         (pats, cond, desugar, hir::LoopSource::While)
202                     }
203                 };
204                 let then_arm = this.arm(then_pats, P(then_expr));
205
206                 // `match <scrutinee> { ... }`
207                 let match_expr = this.expr_match(
208                     scrutinee.span,
209                     P(scrutinee),
210                     hir_vec![then_arm, else_arm],
211                     desugar,
212                 );
213
214                 // `[opt_ident]: loop { ... }`
215                 hir::ExprKind::Loop(
216                     P(this.block_expr(P(match_expr))),
217                     this.lower_label(opt_label),
218                     source
219                 )
220             }),
221             ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| {
222                 hir::ExprKind::Loop(
223                     this.lower_block(body, false),
224                     this.lower_label(opt_label),
225                     hir::LoopSource::Loop,
226                 )
227             }),
228             ExprKind::TryBlock(ref body) => {
229                 self.with_catch_scope(body.id, |this| {
230                     let unstable_span = this.mark_span_with_reason(
231                         DesugaringKind::TryBlock,
232                         body.span,
233                         this.allow_try_trait.clone(),
234                     );
235                     let mut block = this.lower_block(body, true).into_inner();
236                     let tail = block.expr.take().map_or_else(
237                         || {
238                             let span = this.sess.source_map().end_point(unstable_span);
239                             hir::Expr {
240                                 span,
241                                 node: hir::ExprKind::Tup(hir_vec![]),
242                                 attrs: ThinVec::new(),
243                                 hir_id: this.next_id(),
244                             }
245                         },
246                         |x: P<hir::Expr>| x.into_inner(),
247                     );
248                     block.expr = Some(this.wrap_in_try_constructor(
249                         sym::from_ok, tail, unstable_span));
250                     hir::ExprKind::Block(P(block), None)
251                 })
252             }
253             ExprKind::Match(ref expr, ref arms) => hir::ExprKind::Match(
254                 P(self.lower_expr(expr)),
255                 arms.iter().map(|x| self.lower_arm(x)).collect(),
256                 hir::MatchSource::Normal,
257             ),
258             ExprKind::Async(capture_clause, closure_node_id, ref block) => {
259                 self.make_async_expr(capture_clause, closure_node_id, None, block.span, |this| {
260                     this.with_new_scopes(|this| {
261                         let block = this.lower_block(block, false);
262                         this.expr_block(block, ThinVec::new())
263                     })
264                 })
265             }
266             ExprKind::Await(ref expr) => self.lower_await(e.span, expr),
267             ExprKind::Closure(
268                 capture_clause, asyncness, movability, ref decl, ref body, fn_decl_span
269             ) => {
270                 if let IsAsync::Async { closure_id, .. } = asyncness {
271                     self.lower_expr_async_closure(
272                         capture_clause,
273                         closure_id,
274                         decl,
275                         body,
276                         fn_decl_span,
277                     )
278                 } else {
279                     // Lower outside new scope to preserve `is_in_loop_condition`.
280                     let fn_decl = self.lower_fn_decl(decl, None, false, None);
281
282                     self.with_new_scopes(|this| {
283                         this.current_item = Some(fn_decl_span);
284                         let mut generator_kind = None;
285                         let body_id = this.lower_fn_body(decl, |this| {
286                             let e = this.lower_expr(body);
287                             generator_kind = this.generator_kind;
288                             e
289                         });
290                         let generator_option = this.generator_movability_for_fn(
291                             &decl,
292                             fn_decl_span,
293                             generator_kind,
294                             movability,
295                         );
296                         hir::ExprKind::Closure(
297                             this.lower_capture_clause(capture_clause),
298                             fn_decl,
299                             body_id,
300                             fn_decl_span,
301                             generator_option,
302                         )
303                     })
304                 }
305             }
306             ExprKind::Block(ref blk, opt_label) => {
307                 hir::ExprKind::Block(self.lower_block(blk,
308                                                       opt_label.is_some()),
309                                                       self.lower_label(opt_label))
310             }
311             ExprKind::Assign(ref el, ref er) => {
312                 hir::ExprKind::Assign(P(self.lower_expr(el)), P(self.lower_expr(er)))
313             }
314             ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp(
315                 self.lower_binop(op),
316                 P(self.lower_expr(el)),
317                 P(self.lower_expr(er)),
318             ),
319             ExprKind::Field(ref el, ident) => hir::ExprKind::Field(P(self.lower_expr(el)), ident),
320             ExprKind::Index(ref el, ref er) => {
321                 hir::ExprKind::Index(P(self.lower_expr(el)), P(self.lower_expr(er)))
322             }
323             ExprKind::Range(Some(ref e1), Some(ref e2), RangeLimits::Closed) => {
324                 self.lower_expr_range_closed(e.span, e1, e2)
325             }
326             ExprKind::Range(ref e1, ref e2, lims) => {
327                 self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims)
328             }
329             ExprKind::Path(ref qself, ref path) => {
330                 let qpath = self.lower_qpath(
331                     e.id,
332                     qself,
333                     path,
334                     ParamMode::Optional,
335                     ImplTraitContext::disallowed(),
336                 );
337                 hir::ExprKind::Path(qpath)
338             }
339             ExprKind::Break(opt_label, ref opt_expr) => {
340                 let destination = if self.is_in_loop_condition && opt_label.is_none() {
341                     hir::Destination {
342                         label: None,
343                         target_id: Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into(),
344                     }
345                 } else {
346                     self.lower_loop_destination(opt_label.map(|label| (e.id, label)))
347                 };
348                 hir::ExprKind::Break(
349                     destination,
350                     opt_expr.as_ref().map(|x| P(self.lower_expr(x))),
351                 )
352             }
353             ExprKind::Continue(opt_label) => {
354                 hir::ExprKind::Continue(if self.is_in_loop_condition && opt_label.is_none() {
355                     hir::Destination {
356                         label: None,
357                         target_id: Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into(),
358                     }
359                 } else {
360                     self.lower_loop_destination(opt_label.map(|label| (e.id, label)))
361                 })
362             }
363             ExprKind::Ret(ref e) => hir::ExprKind::Ret(e.as_ref().map(|x| P(self.lower_expr(x)))),
364             ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(asm),
365             ExprKind::Struct(ref path, ref fields, ref maybe_expr) => hir::ExprKind::Struct(
366                 P(self.lower_qpath(
367                     e.id,
368                     &None,
369                     path,
370                     ParamMode::Optional,
371                     ImplTraitContext::disallowed(),
372                 )),
373                 fields.iter().map(|x| self.lower_field(x)).collect(),
374                 maybe_expr.as_ref().map(|x| P(self.lower_expr(x))),
375             ),
376             ExprKind::Paren(ref ex) => {
377                 let mut ex = self.lower_expr(ex);
378                 // Include parens in span, but only if it is a super-span.
379                 if e.span.contains(ex.span) {
380                     ex.span = e.span;
381                 }
382                 // Merge attributes into the inner expression.
383                 let mut attrs = e.attrs.clone();
384                 attrs.extend::<Vec<_>>(ex.attrs.into());
385                 ex.attrs = attrs;
386                 return ex;
387             }
388
389             ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
390
391             ExprKind::Err => hir::ExprKind::Err,
392
393             // Desugar `ExprForLoop`
394             // from: `[opt_ident]: for <pat> in <head> <body>`
395             ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => {
396                 return self.lower_expr_for(e, pat, head, body, opt_label);
397             }
398             ExprKind::Try(ref sub_expr) => self.lower_expr_try(e.span, sub_expr),
399             ExprKind::Mac(_) => panic!("Shouldn't exist here"),
400         };
401
402         hir::Expr {
403             hir_id: self.lower_node_id(e.id),
404             node: kind,
405             span: e.span,
406             attrs: e.attrs.clone(),
407         }
408     }
409
410     fn lower_expr_async_closure(
411         &mut self,
412         capture_clause: CaptureBy,
413         closure_id: NodeId,
414         decl: &FnDecl,
415         body: &Expr,
416         fn_decl_span: Span,
417     ) -> hir::ExprKind {
418         let outer_decl = FnDecl {
419             inputs: decl.inputs.clone(),
420             output: FunctionRetTy::Default(fn_decl_span),
421             c_variadic: false,
422         };
423         // We need to lower the declaration outside the new scope, because we
424         // have to conserve the state of being inside a loop condition for the
425         // closure argument types.
426         let fn_decl = self.lower_fn_decl(&outer_decl, None, false, None);
427
428         self.with_new_scopes(|this| {
429             // FIXME(cramertj): allow `async` non-`move` closures with arguments.
430             if capture_clause == CaptureBy::Ref && !decl.inputs.is_empty() {
431                 struct_span_err!(
432                     this.sess,
433                     fn_decl_span,
434                     E0708,
435                     "`async` non-`move` closures with arguments are not currently supported",
436                 )
437                 .help(
438                     "consider using `let` statements to manually capture \
439                     variables by reference before entering an `async move` closure"
440                 )
441                 .emit();
442             }
443
444             // Transform `async |x: u8| -> X { ... }` into
445             // `|x: u8| future_from_generator(|| -> X { ... })`.
446             let body_id = this.lower_fn_body(&outer_decl, |this| {
447                 let async_ret_ty = if let FunctionRetTy::Ty(ty) = &decl.output {
448                     Some(ty.clone())
449                 } else {
450                     None
451                 };
452                 let async_body = this.make_async_expr(
453                     capture_clause, closure_id, async_ret_ty, body.span,
454                     |this| {
455                         this.with_new_scopes(|this| this.lower_expr(body))
456                     }
457                 );
458                 this.expr(fn_decl_span, async_body, ThinVec::new())
459             });
460             hir::ExprKind::Closure(
461                 this.lower_capture_clause(capture_clause),
462                 fn_decl,
463                 body_id,
464                 fn_decl_span,
465                 None,
466             )
467         })
468     }
469
470     /// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
471     fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind {
472         let id = self.next_id();
473         let e1 = self.lower_expr(e1);
474         let e2 = self.lower_expr(e2);
475         self.expr_call_std_assoc_fn(
476             id,
477             span,
478             &[sym::ops, sym::RangeInclusive],
479             "new",
480             hir_vec![e1, e2],
481         )
482     }
483
484     fn lower_expr_range(
485         &mut self,
486         span: Span,
487         e1: Option<&Expr>,
488         e2: Option<&Expr>,
489         lims: RangeLimits,
490     ) -> hir::ExprKind {
491         use syntax::ast::RangeLimits::*;
492
493         let path = match (e1, e2, lims) {
494             (None, None, HalfOpen) => sym::RangeFull,
495             (Some(..), None, HalfOpen) => sym::RangeFrom,
496             (None, Some(..), HalfOpen) => sym::RangeTo,
497             (Some(..), Some(..), HalfOpen) => sym::Range,
498             (None, Some(..), Closed) => sym::RangeToInclusive,
499             (Some(..), Some(..), Closed) => unreachable!(),
500             (_, None, Closed) => self.diagnostic()
501                 .span_fatal(span, "inclusive range with no end")
502                 .raise(),
503         };
504
505         let fields = e1.iter()
506             .map(|e| ("start", e))
507             .chain(e2.iter().map(|e| ("end", e)))
508             .map(|(s, e)| {
509                 let expr = P(self.lower_expr(&e));
510                 let ident = Ident::new(Symbol::intern(s), e.span);
511                 self.field(ident, expr, e.span)
512             })
513             .collect::<P<[hir::Field]>>();
514
515         let is_unit = fields.is_empty();
516         let struct_path = [sym::ops, path];
517         let struct_path = self.std_path(span, &struct_path, None, is_unit);
518         let struct_path = hir::QPath::Resolved(None, P(struct_path));
519
520         if is_unit {
521             hir::ExprKind::Path(struct_path)
522         } else {
523             hir::ExprKind::Struct(P(struct_path), fields, None)
524         }
525     }
526
527     fn lower_expr_asm(&mut self, asm: &InlineAsm) -> hir::ExprKind {
528         let hir_asm = hir::InlineAsm {
529             inputs: asm.inputs.iter().map(|&(ref c, _)| c.clone()).collect(),
530             outputs: asm.outputs
531                 .iter()
532                 .map(|out| hir::InlineAsmOutput {
533                     constraint: out.constraint.clone(),
534                     is_rw: out.is_rw,
535                     is_indirect: out.is_indirect,
536                     span: out.expr.span,
537                 })
538                 .collect(),
539             asm: asm.asm.clone(),
540             asm_str_style: asm.asm_str_style,
541             clobbers: asm.clobbers.clone().into(),
542             volatile: asm.volatile,
543             alignstack: asm.alignstack,
544             dialect: asm.dialect,
545             ctxt: asm.ctxt,
546         };
547
548         let outputs = asm.outputs
549             .iter()
550             .map(|out| self.lower_expr(&out.expr))
551             .collect();
552
553         let inputs = asm.inputs
554             .iter()
555             .map(|&(_, ref input)| self.lower_expr(input))
556             .collect();
557
558         hir::ExprKind::InlineAsm(P(hir_asm), outputs, inputs)
559     }
560
561     fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind {
562         match self.generator_kind {
563             Some(hir::GeneratorKind::Gen) => {},
564             Some(hir::GeneratorKind::Async) => {
565                 span_err!(
566                     self.sess,
567                     span,
568                     E0727,
569                     "`async` generators are not yet supported",
570                 );
571                 self.sess.abort_if_errors();
572             },
573             None => self.generator_kind = Some(hir::GeneratorKind::Gen),
574         }
575
576         let expr = opt_expr
577             .as_ref()
578             .map(|x| self.lower_expr(x))
579             .unwrap_or_else(|| self.expr_unit(span));
580
581         hir::ExprKind::Yield(P(expr), hir::YieldSource::Yield)
582     }
583
584     /// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into:
585     /// ```rust
586     /// {
587     ///     let result = match ::std::iter::IntoIterator::into_iter(<head>) {
588     ///         mut iter => {
589     ///             [opt_ident]: loop {
590     ///                 let mut __next;
591     ///                 match ::std::iter::Iterator::next(&mut iter) {
592     ///                     ::std::option::Option::Some(val) => __next = val,
593     ///                     ::std::option::Option::None => break
594     ///                 };
595     ///                 let <pat> = __next;
596     ///                 StmtKind::Expr(<body>);
597     ///             }
598     ///         }
599     ///     };
600     ///     result
601     /// }
602     /// ```
603     fn lower_expr_for(
604         &mut self,
605         e: &Expr,
606         pat: &Pat,
607         head: &Expr,
608         body: &Block,
609         opt_label: Option<Label>,
610     ) -> hir::Expr {
611         // expand <head>
612         let mut head = self.lower_expr(head);
613         let head_sp = head.span;
614         let desugared_span = self.mark_span_with_reason(
615             DesugaringKind::ForLoop,
616             head_sp,
617             None,
618         );
619         head.span = desugared_span;
620
621         let iter = Ident::with_empty_ctxt(sym::iter);
622
623         let next_ident = Ident::with_empty_ctxt(sym::__next);
624         let (next_pat, next_pat_hid) = self.pat_ident_binding_mode(
625             desugared_span,
626             next_ident,
627             hir::BindingAnnotation::Mutable,
628         );
629
630         // `::std::option::Option::Some(val) => __next = val`
631         let pat_arm = {
632             let val_ident = Ident::with_empty_ctxt(sym::val);
633             let (val_pat, val_pat_hid) = self.pat_ident(pat.span, val_ident);
634             let val_expr = P(self.expr_ident(pat.span, val_ident, val_pat_hid));
635             let next_expr = P(self.expr_ident(pat.span, next_ident, next_pat_hid));
636             let assign = P(self.expr(
637                 pat.span,
638                 hir::ExprKind::Assign(next_expr, val_expr),
639                 ThinVec::new(),
640             ));
641             let some_pat = self.pat_some(pat.span, val_pat);
642             self.arm(hir_vec![some_pat], assign)
643         };
644
645         // `::std::option::Option::None => break`
646         let break_arm = {
647             let break_expr =
648                 self.with_loop_scope(e.id, |this| this.expr_break(e.span, ThinVec::new()));
649             let pat = self.pat_none(e.span);
650             self.arm(hir_vec![pat], break_expr)
651         };
652
653         // `mut iter`
654         let (iter_pat, iter_pat_nid) = self.pat_ident_binding_mode(
655             desugared_span,
656             iter,
657             hir::BindingAnnotation::Mutable
658         );
659
660         // `match ::std::iter::Iterator::next(&mut iter) { ... }`
661         let match_expr = {
662             let iter = P(self.expr_ident(head_sp, iter, iter_pat_nid));
663             let ref_mut_iter = self.expr_mut_addr_of(head_sp, iter);
664             let next_path = &[sym::iter, sym::Iterator, sym::next];
665             let next_expr = P(self.expr_call_std_path(
666                 head_sp,
667                 next_path,
668                 hir_vec![ref_mut_iter],
669             ));
670             let arms = hir_vec![pat_arm, break_arm];
671
672             self.expr_match(head_sp, next_expr, arms, hir::MatchSource::ForLoopDesugar)
673         };
674         let match_stmt = self.stmt_expr(head_sp, match_expr);
675
676         let next_expr = P(self.expr_ident(head_sp, next_ident, next_pat_hid));
677
678         // `let mut __next`
679         let next_let = self.stmt_let_pat(
680             ThinVec::new(),
681             desugared_span,
682             None,
683             next_pat,
684             hir::LocalSource::ForLoopDesugar,
685         );
686
687         // `let <pat> = __next`
688         let pat = self.lower_pat(pat);
689         let pat_let = self.stmt_let_pat(
690             ThinVec::new(),
691             head_sp,
692             Some(next_expr),
693             pat,
694             hir::LocalSource::ForLoopDesugar,
695         );
696
697         let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false));
698         let body_expr = self.expr_block(body_block, ThinVec::new());
699         let body_stmt = self.stmt_expr(body.span, body_expr);
700
701         let loop_block = P(self.block_all(
702             e.span,
703             hir_vec![next_let, match_stmt, pat_let, body_stmt],
704             None,
705         ));
706
707         // `[opt_ident]: loop { ... }`
708         let loop_expr = hir::ExprKind::Loop(
709             loop_block,
710             self.lower_label(opt_label),
711             hir::LoopSource::ForLoop,
712         );
713         let loop_expr = P(hir::Expr {
714             hir_id: self.lower_node_id(e.id),
715             node: loop_expr,
716             span: e.span,
717             attrs: ThinVec::new(),
718         });
719
720         // `mut iter => { ... }`
721         let iter_arm = self.arm(hir_vec![iter_pat], loop_expr);
722
723         // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
724         let into_iter_expr = {
725             let into_iter_path =
726                 &[sym::iter, sym::IntoIterator, sym::into_iter];
727             P(self.expr_call_std_path(
728                 head_sp,
729                 into_iter_path,
730                 hir_vec![head],
731             ))
732         };
733
734         let match_expr = P(self.expr_match(
735             head_sp,
736             into_iter_expr,
737             hir_vec![iter_arm],
738             hir::MatchSource::ForLoopDesugar,
739         ));
740
741         // This is effectively `{ let _result = ...; _result }`.
742         // The construct was introduced in #21984 and is necessary to make sure that
743         // temporaries in the `head` expression are dropped and do not leak to the
744         // surrounding scope of the `match` since the `match` is not a terminating scope.
745         //
746         // Also, add the attributes to the outer returned expr node.
747         self.expr_drop_temps(head_sp, match_expr, e.attrs.clone())
748     }
749
750     /// Desugar `ExprKind::Try` from: `<expr>?` into:
751     /// ```rust
752     /// match Try::into_result(<expr>) {
753     ///     Ok(val) => #[allow(unreachable_code)] val,
754     ///     Err(err) => #[allow(unreachable_code)]
755     ///                 // If there is an enclosing `try {...}`:
756     ///                 break 'catch_target Try::from_error(From::from(err)),
757     ///                 // Otherwise:
758     ///                 return Try::from_error(From::from(err)),
759     /// }
760     /// ```
761     fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind {
762         let unstable_span = self.mark_span_with_reason(
763             DesugaringKind::QuestionMark,
764             span,
765             self.allow_try_trait.clone(),
766         );
767         let try_span = self.sess.source_map().end_point(span);
768         let try_span = self.mark_span_with_reason(
769             DesugaringKind::QuestionMark,
770             try_span,
771             self.allow_try_trait.clone(),
772         );
773
774         // `Try::into_result(<expr>)`
775         let scrutinee = {
776             // expand <expr>
777             let sub_expr = self.lower_expr(sub_expr);
778
779             let path = &[sym::ops, sym::Try, sym::into_result];
780             P(self.expr_call_std_path(unstable_span, path, hir_vec![sub_expr]))
781         };
782
783         // `#[allow(unreachable_code)]`
784         let attr = {
785             // `allow(unreachable_code)`
786             let allow = {
787                 let allow_ident = Ident::new(sym::allow, span);
788                 let uc_ident = Ident::new(sym::unreachable_code, span);
789                 let uc_nested = attr::mk_nested_word_item(uc_ident);
790                 attr::mk_list_item(allow_ident, vec![uc_nested])
791             };
792             attr::mk_attr_outer(allow)
793         };
794         let attrs = vec![attr];
795
796         // `Ok(val) => #[allow(unreachable_code)] val,`
797         let ok_arm = {
798             let val_ident = Ident::with_empty_ctxt(sym::val);
799             let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
800             let val_expr = P(self.expr_ident_with_attrs(
801                 span,
802                 val_ident,
803                 val_pat_nid,
804                 ThinVec::from(attrs.clone()),
805             ));
806             let ok_pat = self.pat_ok(span, val_pat);
807
808             self.arm(hir_vec![ok_pat], val_expr)
809         };
810
811         // `Err(err) => #[allow(unreachable_code)]
812         //              return Try::from_error(From::from(err)),`
813         let err_arm = {
814             let err_ident = Ident::with_empty_ctxt(sym::err);
815             let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident);
816             let from_expr = {
817                 let from_path = &[sym::convert, sym::From, sym::from];
818                 let err_expr = self.expr_ident(try_span, err_ident, err_local_nid);
819                 self.expr_call_std_path(try_span, from_path, hir_vec![err_expr])
820             };
821             let from_err_expr =
822                 self.wrap_in_try_constructor(sym::from_error, from_expr, unstable_span);
823             let thin_attrs = ThinVec::from(attrs);
824             let catch_scope = self.catch_scopes.last().map(|x| *x);
825             let ret_expr = if let Some(catch_node) = catch_scope {
826                 let target_id = Ok(self.lower_node_id(catch_node));
827                 P(self.expr(
828                     try_span,
829                     hir::ExprKind::Break(
830                         hir::Destination {
831                             label: None,
832                             target_id,
833                         },
834                         Some(from_err_expr),
835                     ),
836                     thin_attrs,
837                 ))
838             } else {
839                 P(self.expr(try_span, hir::ExprKind::Ret(Some(from_err_expr)), thin_attrs))
840             };
841
842             let err_pat = self.pat_err(try_span, err_local);
843             self.arm(hir_vec![err_pat], ret_expr)
844         };
845
846         hir::ExprKind::Match(
847             scrutinee,
848             hir_vec![err_arm, ok_arm],
849             hir::MatchSource::TryDesugar,
850         )
851     }
852 }