]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/hir/lowering/expr.rs
Auto merge of #63462 - matthewjasper:hygienic-builtin-derives, r=petrochenkov
[rust.git] / src / librustc / hir / lowering / expr.rs
index 378588d25b2e77f3e5337c8fef3d8ac9d0844c65..ff0c44a23874bfa96bd824625fc1e162583416de 100644 (file)
@@ -1,5 +1,6 @@
 use super::{LoweringContext, ParamMode, ParenthesizedGenericArgs, ImplTraitContext};
 use crate::hir::{self, HirVec};
+use crate::hir::def::Res;
 use crate::hir::ptr::P;
 
 use rustc_data_structures::thin_vec::ThinVec;
@@ -7,7 +8,7 @@
 use syntax::attr;
 use syntax::ptr::P as AstP;
 use syntax::ast::*;
-use syntax::source_map::{respan, DesugaringKind, Span};
+use syntax::source_map::{respan, DesugaringKind, Span, Spanned};
 use syntax::symbol::{sym, Symbol};
 
 impl LoweringContext<'_> {
@@ -67,156 +68,12 @@ pub(super) fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
                 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::Let(ref pats, ref scrutinee) => self.lower_expr_let(e.span, pats, scrutinee),
             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 {
-                    // `<pat> => <then>`:
-                    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 => <then>`:
-                    _ => {
-                        // 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)
+                self.lower_expr_if(e.span, cond, then, else_opt.as_deref())
             }
-            // 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 <sub_expr> {
-                        //       <pat> => <body>,
-                        //       _ => 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 => <then>`:
-                        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 <scrutinee> { ... }`
-                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
-                )
+                this.lower_expr_while_in_loop_scope(e.span, cond, body, opt_label)
             }),
             ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| {
                 hir::ExprKind::Loop(
@@ -281,28 +138,13 @@ pub(super) fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
                 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,
+                    self.lower_jump_destination(e.id, opt_label),
                     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)))
-                })
+                hir::ExprKind::Continue(self.lower_jump_destination(e.id, opt_label))
             }
             ExprKind::Ret(ref e) => hir::ExprKind::Ret(e.as_ref().map(|x| P(self.lower_expr(x)))),
             ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(asm),
@@ -351,6 +193,221 @@ pub(super) fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
         }
     }
 
+    fn lower_unop(&mut self, u: UnOp) -> hir::UnOp {
+        match u {
+            UnOp::Deref => hir::UnDeref,
+            UnOp::Not => hir::UnNot,
+            UnOp::Neg => hir::UnNeg,
+        }
+    }
+
+    fn lower_binop(&mut self, b: BinOp) -> hir::BinOp {
+        Spanned {
+            node: match b.node {
+                BinOpKind::Add => hir::BinOpKind::Add,
+                BinOpKind::Sub => hir::BinOpKind::Sub,
+                BinOpKind::Mul => hir::BinOpKind::Mul,
+                BinOpKind::Div => hir::BinOpKind::Div,
+                BinOpKind::Rem => hir::BinOpKind::Rem,
+                BinOpKind::And => hir::BinOpKind::And,
+                BinOpKind::Or => hir::BinOpKind::Or,
+                BinOpKind::BitXor => hir::BinOpKind::BitXor,
+                BinOpKind::BitAnd => hir::BinOpKind::BitAnd,
+                BinOpKind::BitOr => hir::BinOpKind::BitOr,
+                BinOpKind::Shl => hir::BinOpKind::Shl,
+                BinOpKind::Shr => hir::BinOpKind::Shr,
+                BinOpKind::Eq => hir::BinOpKind::Eq,
+                BinOpKind::Lt => hir::BinOpKind::Lt,
+                BinOpKind::Le => hir::BinOpKind::Le,
+                BinOpKind::Ne => hir::BinOpKind::Ne,
+                BinOpKind::Ge => hir::BinOpKind::Ge,
+                BinOpKind::Gt => hir::BinOpKind::Gt,
+            },
+            span: b.span,
+        }
+    }
+
+    /// Emit an error and lower `ast::ExprKind::Let(pats, scrutinee)` into:
+    /// ```rust
+    /// match scrutinee { pats => true, _ => false }
+    /// ```
+    fn lower_expr_let(
+        &mut self,
+        span: Span,
+        pats: &[AstP<Pat>],
+        scrutinee: &Expr
+    ) -> hir::ExprKind {
+        // If we got here, the `let` expression is not allowed.
+        self.sess
+            .struct_span_err(span, "`let` expressions are not supported here")
+            .note("only supported directly in conditions of `if`- and `while`-expressions")
+            .note("as well as when nested within `&&` and parenthesis in those conditions")
+            .emit();
+
+        // 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(span, true);
+            self.arm(pats, P(expr))
+        };
+        let else_arm = {
+            let pats = hir_vec![self.pat_wild(span)];
+            let expr = self.expr_bool(span, false);
+            self.arm(pats, P(expr))
+        };
+        hir::ExprKind::Match(
+            P(scrutinee),
+            vec![then_arm, else_arm].into(),
+            hir::MatchSource::Normal,
+        )
+    }
+
+    fn lower_expr_if(
+        &mut self,
+        span: Span,
+        cond: &Expr,
+        then: &Block,
+        else_opt: Option<&Expr>,
+    ) -> hir::ExprKind {
+        // FIXME(#53667): handle lowering of && and parens.
+
+        // `_ => else_block` where `else_block` is `{}` if there's `None`:
+        let else_pat = self.pat_wild(span);
+        let (else_expr, contains_else_clause) = match else_opt {
+            None => (self.expr_block_empty(span), false),
+            Some(els) => (self.lower_expr(els), true),
+        };
+        let else_arm = self.arm(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 {
+            // `<pat> => <then>`:
+            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 => <then>`:
+            _ => {
+                // 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(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)
+    }
+
+    fn lower_expr_while_in_loop_scope(
+        &mut self,
+        span: Span,
+        cond: &Expr,
+        body: &Block,
+        opt_label: Option<Label>
+    ) -> hir::ExprKind {
+        // FIXME(#53667): handle lowering of && and parens.
+
+        // 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 = self.pat_wild(span);
+            let else_expr = self.expr_break(span, ThinVec::new());
+            self.arm(hir_vec![else_pat], else_expr)
+        };
+
+        // Handle then + scrutinee:
+        let then_blk = self.lower_block(body, false);
+        let then_expr = self.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 <sub_expr> {
+                //       <pat> => <body>,
+                //       _ => break
+                //     }
+                //   }
+                let scrutinee = self.with_loop_condition_scope(|t| t.lower_expr(scrutinee));
+                let pats = pats.iter().map(|pat| self.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 = self.with_loop_condition_scope(|this| this.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 `while 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::WhileDesugar;
+                // `true => <then>`:
+                let pats = hir_vec![self.pat_bool(span, true)];
+                (pats, cond, desugar, hir::LoopSource::While)
+            }
+        };
+        let then_arm = self.arm(then_pats, P(then_expr));
+
+        // `match <scrutinee> { ... }`
+        let match_expr = self.expr_match(
+            scrutinee.span,
+            P(scrutinee),
+            hir_vec![then_arm, else_arm],
+            desugar,
+        );
+
+        // `[opt_ident]: loop { ... }`
+        hir::ExprKind::Loop(
+            P(self.block_expr(P(match_expr))),
+            self.lower_label(opt_label),
+            source
+        )
+    }
+
     fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind {
         self.with_catch_scope(body.id, |this| {
             let unstable_span = this.mark_span_with_reason(
@@ -379,6 +436,76 @@ fn wrap_in_try_constructor(
         P(self.expr_call(e.span, from_err, hir_vec![e]))
     }
 
+    fn lower_arm(&mut self, arm: &Arm) -> hir::Arm {
+        hir::Arm {
+            hir_id: self.next_id(),
+            attrs: self.lower_attrs(&arm.attrs),
+            pats: arm.pats.iter().map(|x| self.lower_pat(x)).collect(),
+            guard: match arm.guard {
+                Some(ref x) => Some(hir::Guard::If(P(self.lower_expr(x)))),
+                _ => None,
+            },
+            body: P(self.lower_expr(&arm.body)),
+            span: arm.span,
+        }
+    }
+
+    pub(super) fn make_async_expr(
+        &mut self,
+        capture_clause: CaptureBy,
+        closure_node_id: NodeId,
+        ret_ty: Option<AstP<Ty>>,
+        span: Span,
+        body: impl FnOnce(&mut LoweringContext<'_>) -> hir::Expr,
+    ) -> hir::ExprKind {
+        let capture_clause = self.lower_capture_clause(capture_clause);
+        let output = match ret_ty {
+            Some(ty) => FunctionRetTy::Ty(ty),
+            None => FunctionRetTy::Default(span),
+        };
+        let ast_decl = FnDecl {
+            inputs: vec![],
+            output,
+            c_variadic: false
+        };
+        let decl = self.lower_fn_decl(&ast_decl, None, /* impl trait allowed */ false, None);
+        let body_id = self.lower_fn_body(&ast_decl, |this| {
+            this.generator_kind = Some(hir::GeneratorKind::Async);
+            body(this)
+        });
+
+        // `static || -> <ret_ty> { body }`:
+        let generator_node = hir::ExprKind::Closure(
+            capture_clause,
+            decl,
+            body_id,
+            span,
+            Some(hir::GeneratorMovability::Static)
+        );
+        let generator = hir::Expr {
+            hir_id: self.lower_node_id(closure_node_id),
+            node: generator_node,
+            span,
+            attrs: ThinVec::new(),
+        };
+
+        // `future::from_generator`:
+        let unstable_span = self.mark_span_with_reason(
+            DesugaringKind::Async,
+            span,
+            self.allow_gen_future.clone(),
+        );
+        let gen_future = self.expr_std_path(
+            unstable_span,
+            &[sym::future, sym::from_generator],
+            None,
+            ThinVec::new()
+        );
+
+        // `future::from_generator(generator)`:
+        hir::ExprKind::Call(P(gen_future), hir_vec![generator])
+    }
+
     /// Desugar `<expr>.await` into:
     /// ```rust
     /// {
@@ -425,7 +552,7 @@ fn lower_expr_await(&mut self, await_span: Span, expr: &Expr) -> hir::ExprKind {
 
         // let mut pinned = <expr>;
         let expr = P(self.lower_expr(expr));
-        let pinned_ident = Ident::with_empty_ctxt(sym::pinned);
+        let pinned_ident = Ident::with_dummy_span(sym::pinned);
         let (pinned_pat, pinned_pat_hid) = self.pat_ident_binding_mode(
             span,
             pinned_ident,
@@ -466,7 +593,7 @@ fn lower_expr_await(&mut self, await_span: Span, expr: &Expr) -> hir::ExprKind {
         let loop_node_id = self.sess.next_node_id();
         let loop_hir_id = self.lower_node_id(loop_node_id);
         let ready_arm = {
-            let x_ident = Ident::with_empty_ctxt(sym::result);
+            let x_ident = Ident::with_dummy_span(sym::result);
             let (x_pat, x_pat_hid) = self.pat_ident(span, x_ident);
             let x_expr = P(self.expr_ident(span, x_ident, x_pat_hid));
             let ready_pat = self.pat_std_enum(
@@ -550,6 +677,7 @@ fn lower_expr_closure(
         let fn_decl = self.lower_fn_decl(decl, None, false, None);
 
         self.with_new_scopes(|this| {
+            let prev = this.current_item;
             this.current_item = Some(fn_decl_span);
             let mut generator_kind = None;
             let body_id = this.lower_fn_body(decl, |this| {
@@ -563,8 +691,10 @@ fn lower_expr_closure(
                 generator_kind,
                 movability,
             );
+            let capture_clause = this.lower_capture_clause(capture_clause);
+            this.current_item = prev;
             hir::ExprKind::Closure(
-                this.lower_capture_clause(capture_clause),
+                capture_clause,
                 fn_decl,
                 body_id,
                 fn_decl_span,
@@ -573,6 +703,53 @@ fn lower_expr_closure(
         })
     }
 
+    fn lower_capture_clause(&mut self, c: CaptureBy) -> hir::CaptureClause {
+        match c {
+            CaptureBy::Value => hir::CaptureByValue,
+            CaptureBy::Ref => hir::CaptureByRef,
+        }
+    }
+
+    fn generator_movability_for_fn(
+        &mut self,
+        decl: &FnDecl,
+        fn_decl_span: Span,
+        generator_kind: Option<hir::GeneratorKind>,
+        movability: Movability,
+    ) -> Option<hir::GeneratorMovability> {
+        match generator_kind {
+            Some(hir::GeneratorKind::Gen) =>  {
+                if !decl.inputs.is_empty() {
+                    span_err!(
+                        self.sess,
+                        fn_decl_span,
+                        E0628,
+                        "generators cannot have explicit arguments"
+                    );
+                    self.sess.abort_if_errors();
+                }
+                Some(match movability {
+                    Movability::Movable => hir::GeneratorMovability::Movable,
+                    Movability::Static => hir::GeneratorMovability::Static,
+                })
+            },
+            Some(hir::GeneratorKind::Async) => {
+                bug!("non-`async` closure body turned `async` during lowering");
+            },
+            None => {
+                if movability == Movability::Static {
+                    span_err!(
+                        self.sess,
+                        fn_decl_span,
+                        E0697,
+                        "closures cannot be static"
+                    );
+                }
+                None
+            },
+        }
+    }
+
     fn lower_expr_async_closure(
         &mut self,
         capture_clause: CaptureBy,
@@ -690,6 +867,105 @@ fn lower_expr_range(
         }
     }
 
+    fn lower_label(&mut self, label: Option<Label>) -> Option<hir::Label> {
+        label.map(|label| hir::Label {
+            ident: label.ident,
+        })
+    }
+
+    fn lower_loop_destination(&mut self, destination: Option<(NodeId, Label)>) -> hir::Destination {
+        let target_id = match destination {
+            Some((id, _)) => {
+                if let Some(loop_id) = self.resolver.get_label_res(id) {
+                    Ok(self.lower_node_id(loop_id))
+                } else {
+                    Err(hir::LoopIdError::UnresolvedLabel)
+                }
+            }
+            None => {
+                self.loop_scopes
+                    .last()
+                    .cloned()
+                    .map(|id| Ok(self.lower_node_id(id)))
+                    .unwrap_or(Err(hir::LoopIdError::OutsideLoopScope))
+                    .into()
+            }
+        };
+        hir::Destination {
+            label: self.lower_label(destination.map(|(_, label)| label)),
+            target_id,
+        }
+    }
+
+    fn lower_jump_destination(&mut self, id: NodeId, opt_label: Option<Label>) -> hir::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| (id, label)))
+        }
+    }
+
+    fn with_catch_scope<T, F>(&mut self, catch_id: NodeId, f: F) -> T
+    where
+        F: FnOnce(&mut LoweringContext<'_>) -> T,
+    {
+        let len = self.catch_scopes.len();
+        self.catch_scopes.push(catch_id);
+
+        let result = f(self);
+        assert_eq!(
+            len + 1,
+            self.catch_scopes.len(),
+            "catch scopes should be added and removed in stack order"
+        );
+
+        self.catch_scopes.pop().unwrap();
+
+        result
+    }
+
+    fn with_loop_scope<T, F>(&mut self, loop_id: NodeId, f: F) -> T
+    where
+        F: FnOnce(&mut LoweringContext<'_>) -> T,
+    {
+        // We're no longer in the base loop's condition; we're in another loop.
+        let was_in_loop_condition = self.is_in_loop_condition;
+        self.is_in_loop_condition = false;
+
+        let len = self.loop_scopes.len();
+        self.loop_scopes.push(loop_id);
+
+        let result = f(self);
+        assert_eq!(
+            len + 1,
+            self.loop_scopes.len(),
+            "loop scopes should be added and removed in stack order"
+        );
+
+        self.loop_scopes.pop().unwrap();
+
+        self.is_in_loop_condition = was_in_loop_condition;
+
+        result
+    }
+
+    fn with_loop_condition_scope<T, F>(&mut self, f: F) -> T
+    where
+        F: FnOnce(&mut LoweringContext<'_>) -> T,
+    {
+        let was_in_loop_condition = self.is_in_loop_condition;
+        self.is_in_loop_condition = true;
+
+        let result = f(self);
+
+        self.is_in_loop_condition = was_in_loop_condition;
+
+        result
+    }
+
     fn lower_expr_asm(&mut self, asm: &InlineAsm) -> hir::ExprKind {
         let hir_asm = hir::InlineAsm {
             inputs: asm.inputs.iter().map(|&(ref c, _)| c.clone()).collect(),
@@ -708,7 +984,6 @@ fn lower_expr_asm(&mut self, asm: &InlineAsm) -> hir::ExprKind {
             volatile: asm.volatile,
             alignstack: asm.alignstack,
             dialect: asm.dialect,
-            ctxt: asm.ctxt,
         };
 
         let outputs = asm.outputs
@@ -724,6 +999,16 @@ fn lower_expr_asm(&mut self, asm: &InlineAsm) -> hir::ExprKind {
         hir::ExprKind::InlineAsm(P(hir_asm), outputs, inputs)
     }
 
+    fn lower_field(&mut self, f: &Field) -> hir::Field {
+        hir::Field {
+            hir_id: self.next_id(),
+            ident: f.ident,
+            expr: P(self.lower_expr(&f.expr)),
+            span: f.span,
+            is_shorthand: f.is_shorthand,
+        }
+    }
+
     fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind {
         match self.generator_kind {
             Some(hir::GeneratorKind::Gen) => {},
@@ -784,9 +1069,9 @@ fn lower_expr_for(
         );
         head.span = desugared_span;
 
-        let iter = Ident::with_empty_ctxt(sym::iter);
+        let iter = Ident::with_dummy_span(sym::iter);
 
-        let next_ident = Ident::with_empty_ctxt(sym::__next);
+        let next_ident = Ident::with_dummy_span(sym::__next);
         let (next_pat, next_pat_hid) = self.pat_ident_binding_mode(
             desugared_span,
             next_ident,
@@ -795,7 +1080,7 @@ fn lower_expr_for(
 
         // `::std::option::Option::Some(val) => __next = val`
         let pat_arm = {
-            let val_ident = Ident::with_empty_ctxt(sym::val);
+            let val_ident = Ident::with_dummy_span(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));
@@ -961,7 +1246,7 @@ fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind {
 
         // `Ok(val) => #[allow(unreachable_code)] val,`
         let ok_arm = {
-            let val_ident = Ident::with_empty_ctxt(sym::val);
+            let val_ident = Ident::with_dummy_span(sym::val);
             let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
             let val_expr = P(self.expr_ident_with_attrs(
                 span,
@@ -977,7 +1262,7 @@ fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind {
         // `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_ident = Ident::with_dummy_span(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];
@@ -1015,4 +1300,200 @@ fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind {
             hir::MatchSource::TryDesugar,
         )
     }
+
+    // =========================================================================
+    // Helper methods for building HIR.
+    // =========================================================================
+
+    /// Constructs a `true` or `false` literal expression.
+    pub(super) fn expr_bool(&mut self, span: Span, val: bool) -> hir::Expr {
+        let lit = Spanned { span, node: LitKind::Bool(val) };
+        self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new())
+    }
+
+    /// Wrap the given `expr` in a terminating scope using `hir::ExprKind::DropTemps`.
+    ///
+    /// In terms of drop order, it has the same effect as wrapping `expr` in
+    /// `{ let _t = $expr; _t }` but should provide better compile-time performance.
+    ///
+    /// The drop order can be important in e.g. `if expr { .. }`.
+    fn expr_drop_temps(
+        &mut self,
+        span: Span,
+        expr: P<hir::Expr>,
+        attrs: ThinVec<Attribute>
+    ) -> hir::Expr {
+        self.expr(span, hir::ExprKind::DropTemps(expr), attrs)
+    }
+
+    fn expr_match(
+        &mut self,
+        span: Span,
+        arg: P<hir::Expr>,
+        arms: hir::HirVec<hir::Arm>,
+        source: hir::MatchSource,
+    ) -> hir::Expr {
+        self.expr(span, hir::ExprKind::Match(arg, arms, source), ThinVec::new())
+    }
+
+    fn expr_break(&mut self, span: Span, attrs: ThinVec<Attribute>) -> P<hir::Expr> {
+        let expr_break = hir::ExprKind::Break(self.lower_loop_destination(None), None);
+        P(self.expr(span, expr_break, attrs))
+    }
+
+    fn expr_mut_addr_of(&mut self, span: Span, e: P<hir::Expr>) -> hir::Expr {
+        self.expr(span, hir::ExprKind::AddrOf(hir::MutMutable, e), ThinVec::new())
+    }
+
+    fn expr_unit(&mut self, sp: Span) -> hir::Expr {
+        self.expr_tuple(sp, hir_vec![])
+    }
+
+    fn expr_tuple(&mut self, sp: Span, exprs: hir::HirVec<hir::Expr>) -> hir::Expr {
+        self.expr(sp, hir::ExprKind::Tup(exprs), ThinVec::new())
+    }
+
+    fn expr_call(
+        &mut self,
+        span: Span,
+        e: P<hir::Expr>,
+        args: hir::HirVec<hir::Expr>,
+    ) -> hir::Expr {
+        self.expr(span, hir::ExprKind::Call(e, args), ThinVec::new())
+    }
+
+    // Note: associated functions must use `expr_call_std_path`.
+    fn expr_call_std_path(
+        &mut self,
+        span: Span,
+        path_components: &[Symbol],
+        args: hir::HirVec<hir::Expr>,
+    ) -> hir::Expr {
+        let path = P(self.expr_std_path(span, path_components, None, ThinVec::new()));
+        self.expr_call(span, path, args)
+    }
+
+    // Create an expression calling an associated function of an std type.
+    //
+    // Associated functions cannot be resolved through the normal `std_path` function,
+    // as they are resolved differently and so cannot use `expr_call_std_path`.
+    //
+    // This function accepts the path component (`ty_path_components`) separately from
+    // the name of the associated function (`assoc_fn_name`) in order to facilitate
+    // separate resolution of the type and creation of a path referring to its associated
+    // function.
+    fn expr_call_std_assoc_fn(
+        &mut self,
+        ty_path_id: hir::HirId,
+        span: Span,
+        ty_path_components: &[Symbol],
+        assoc_fn_name: &str,
+        args: hir::HirVec<hir::Expr>,
+    ) -> hir::ExprKind {
+        let ty_path = P(self.std_path(span, ty_path_components, None, false));
+        let ty = P(self.ty_path(ty_path_id, span, hir::QPath::Resolved(None, ty_path)));
+        let fn_seg = P(hir::PathSegment::from_ident(Ident::from_str(assoc_fn_name)));
+        let fn_path = hir::QPath::TypeRelative(ty, fn_seg);
+        let fn_expr = P(self.expr(span, hir::ExprKind::Path(fn_path), ThinVec::new()));
+        hir::ExprKind::Call(fn_expr, args)
+    }
+
+    fn expr_std_path(
+        &mut self,
+        span: Span,
+        components: &[Symbol],
+        params: Option<P<hir::GenericArgs>>,
+        attrs: ThinVec<Attribute>,
+    ) -> hir::Expr {
+        let path = self.std_path(span, components, params, true);
+        self.expr(
+            span,
+            hir::ExprKind::Path(hir::QPath::Resolved(None, P(path))),
+            attrs,
+        )
+    }
+
+    pub(super) fn expr_ident(&mut self, sp: Span, ident: Ident, binding: hir::HirId) -> hir::Expr {
+        self.expr_ident_with_attrs(sp, ident, binding, ThinVec::new())
+    }
+
+    fn expr_ident_with_attrs(
+        &mut self,
+        span: Span,
+        ident: Ident,
+        binding: hir::HirId,
+        attrs: ThinVec<Attribute>,
+    ) -> hir::Expr {
+        let expr_path = hir::ExprKind::Path(hir::QPath::Resolved(
+            None,
+            P(hir::Path {
+                span,
+                res: Res::Local(binding),
+                segments: hir_vec![hir::PathSegment::from_ident(ident)],
+            }),
+        ));
+
+        self.expr(span, expr_path, attrs)
+    }
+
+    fn expr_unsafe(&mut self, expr: P<hir::Expr>) -> hir::Expr {
+        let hir_id = self.next_id();
+        let span = expr.span;
+        self.expr(
+            span,
+            hir::ExprKind::Block(P(hir::Block {
+                stmts: hir_vec![],
+                expr: Some(expr),
+                hir_id,
+                rules: hir::UnsafeBlock(hir::CompilerGenerated),
+                span,
+                targeted_by_break: false,
+            }), None),
+            ThinVec::new(),
+        )
+    }
+
+    fn expr_block_empty(&mut self, span: Span) -> hir::Expr {
+        let blk = self.block_all(span, hir_vec![], None);
+        self.expr_block(P(blk), ThinVec::new())
+    }
+
+    pub(super) fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<Attribute>) -> hir::Expr {
+        self.expr(b.span, hir::ExprKind::Block(b, None), attrs)
+    }
+
+    pub(super) fn expr(
+        &mut self,
+        span: Span,
+        node: hir::ExprKind,
+        attrs: ThinVec<Attribute>
+    ) -> hir::Expr {
+        hir::Expr {
+            hir_id: self.next_id(),
+            node,
+            span,
+            attrs,
+        }
+    }
+
+    fn field(&mut self, ident: Ident, expr: P<hir::Expr>, span: Span) -> hir::Field {
+        hir::Field {
+            hir_id: self.next_id(),
+            ident,
+            span,
+            expr,
+            is_shorthand: false,
+        }
+    }
+
+    fn arm(&mut self, pats: hir::HirVec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
+        hir::Arm {
+            hir_id: self.next_id(),
+            attrs: hir_vec![],
+            pats,
+            guard: None,
+            span: expr.span,
+            body: expr,
+        }
+    }
 }