]> git.lizzy.rs Git - rust.git/commitdiff
let-else: add hir::Let and type check it like a hir::Local
authorCormac Relf <web@cormacrelf.net>
Wed, 13 Oct 2021 05:39:06 +0000 (16:39 +1100)
committerCormac Relf <web@cormacrelf.net>
Mon, 13 Dec 2021 03:02:19 +0000 (14:02 +1100)
unify typeck of hir::Local and hir::Let
remove extraneous pub(crate/super)

13 files changed:
compiler/rustc_ast_lowering/src/block.rs
compiler/rustc_ast_lowering/src/expr.rs
compiler/rustc_hir/src/arena.rs
compiler/rustc_hir/src/hir.rs
compiler/rustc_hir/src/intravisit.rs
compiler/rustc_hir_pretty/src/lib.rs
compiler/rustc_mir_build/src/thir/cx/expr.rs
compiler/rustc_mir_build/src/thir/pattern/check_match.rs
compiler/rustc_passes/src/liveness.rs
compiler/rustc_typeck/src/check/expr.rs
compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
compiler/rustc_typeck/src/check/gather_locals.rs
compiler/rustc_typeck/src/expr_use_visitor.rs

index 14a894d61f4f5fdeeb5c8917b61a75b32025ba24..4eab936f85fa066bf13cfd80bbbdabac580fc890 100644 (file)
@@ -2,7 +2,6 @@
 use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
 use rustc_hir as hir;
 use rustc_session::parse::feature_err;
-use rustc_span::symbol::Ident;
 use rustc_span::{sym, DesugaringKind};
 
 use smallvec::SmallVec;
@@ -39,8 +38,7 @@ fn lower_stmts(
                     let hir_id = self.lower_node_id(s.id);
                     match &local.kind {
                         LocalKind::InitElse(init, els) => {
-                            let (s, e) = self.lower_let_else(hir_id, local, init, els, tail);
-                            stmts.push(s);
+                            let e = self.lower_let_else(hir_id, local, init, els, tail);
                             expr = Some(e);
                             // remaining statements are in let-else expression
                             break;
@@ -125,36 +123,25 @@ fn lower_let_else(
         init: &Expr,
         els: &Block,
         tail: &[Stmt],
-    ) -> (hir::Stmt<'hir>, &'hir hir::Expr<'hir>) {
+    ) -> &'hir hir::Expr<'hir> {
         let ty = local
             .ty
             .as_ref()
             .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
         let span = self.lower_span(local.span);
         let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None);
-        let init = Some(self.lower_expr(init));
-        let val = Ident::with_dummy_span(sym::val);
-        let (pat, val_id) =
-            self.pat_ident_binding_mode(span, val, hir::BindingAnnotation::Unannotated);
+        let init = self.lower_expr(init);
         let local_hir_id = self.lower_node_id(local.id);
         self.lower_attrs(local_hir_id, &local.attrs);
-        // first statement which basically exists for the type annotation
-        let stmt = {
-            let local = self.arena.alloc(hir::Local {
+        let let_expr = {
+            let lex = self.arena.alloc(hir::Let {
                 hir_id: local_hir_id,
+                pat: self.lower_pat(&local.pat),
                 ty,
-                pat,
                 init,
                 span,
-                source: hir::LocalSource::Normal,
             });
-            let kind = hir::StmtKind::Local(local);
-            hir::Stmt { hir_id: stmt_hir_id, kind, span }
-        };
-        let let_expr = {
-            let scrutinee = self.expr_ident(span, val, val_id);
-            let let_kind = hir::ExprKind::Let(self.lower_pat(&local.pat), scrutinee, span);
-            self.arena.alloc(self.expr(span, let_kind, AttrVec::new()))
+            self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), AttrVec::new()))
         };
         let then_expr = {
             let (stmts, expr) = self.lower_stmts(tail);
@@ -167,7 +154,7 @@ fn lower_let_else(
         };
         self.alias_attrs(else_expr.hir_id, local_hir_id);
         let if_expr = self.arena.alloc(hir::Expr {
-            hir_id: self.next_id(),
+            hir_id: stmt_hir_id,
             span,
             kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
         });
@@ -180,6 +167,6 @@ fn lower_let_else(
             )
             .emit();
         }
-        (stmt, if_expr)
+        if_expr
     }
 }
index c9578c2f50f9073260c5633ad726a9d54e7120b5..d88292e0b908454efe06a80ad26ffb815cfcc4e3 100644 (file)
@@ -92,11 +92,15 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
                     let ohs = self.lower_expr(ohs);
                     hir::ExprKind::AddrOf(k, m, ohs)
                 }
-                ExprKind::Let(ref pat, ref scrutinee, span) => hir::ExprKind::Let(
-                    self.lower_pat(pat),
-                    self.lower_expr(scrutinee),
-                    self.lower_span(span),
-                ),
+                ExprKind::Let(ref pat, ref scrutinee, span) => {
+                    hir::ExprKind::Let(self.arena.alloc(hir::Let {
+                        hir_id: self.next_id(),
+                        span: self.lower_span(span),
+                        pat: self.lower_pat(pat),
+                        ty: None,
+                        init: self.lower_expr(scrutinee),
+                    }))
+                }
                 ExprKind::If(ref cond, ref then, ref else_opt) => {
                     self.lower_expr_if(cond, then, else_opt.as_deref())
                 }
index f19ca497d8bf257f4a8853302366a54ad6c70dc7..edad00ed6a2fe0ce03de074dc48a0d3473d3c547 100644 (file)
@@ -20,6 +20,7 @@ macro_rules! arena_types {
             [] generic_bound: rustc_hir::GenericBound<'tcx>,
             [] generic_param: rustc_hir::GenericParam<'tcx>,
             [] expr: rustc_hir::Expr<'tcx>,
+            [] let_expr: rustc_hir::Let<'tcx>,
             [] expr_field: rustc_hir::ExprField<'tcx>,
             [] pat_field: rustc_hir::PatField<'tcx>,
             [] fn_decl: rustc_hir::FnDecl<'tcx>,
index 9a8bc2efbfa4628b3caaa8d1aa9284020fdff613..4cf7a5b1264c5f54083f9b9ebf8e1ba88cefed84 100644 (file)
@@ -1176,10 +1176,24 @@ pub struct Arm<'hir> {
     pub body: &'hir Expr<'hir>,
 }
 
+/// Represents a `let <pat>[: <ty>] = <expr>` expression (not a Local), occurring in an `if-let` or
+/// `let-else`, evaluating to a boolean. Typically the pattern is refutable.
+///
+/// In an if-let, imagine it as `if (let <pat> = <expr>) { ... }`; in a let-else, it is part of the
+/// desugaring to if-let. Only let-else supports the type annotation at present.
+#[derive(Debug, HashStable_Generic)]
+pub struct Let<'hir> {
+    pub hir_id: HirId,
+    pub span: Span,
+    pub pat: &'hir Pat<'hir>,
+    pub ty: Option<&'hir Ty<'hir>>,
+    pub init: &'hir Expr<'hir>,
+}
+
 #[derive(Debug, HashStable_Generic)]
 pub enum Guard<'hir> {
     If(&'hir Expr<'hir>),
-    // FIXME use ExprKind::Let for this.
+    // FIXME use hir::Let for this.
     IfLet(&'hir Pat<'hir>, &'hir Expr<'hir>),
 }
 
@@ -1696,7 +1710,7 @@ pub enum ExprKind<'hir> {
     ///
     /// These are not `Local` and only occur as expressions.
     /// The `let Some(x) = foo()` in `if let Some(x) = foo()` is an example of `Let(..)`.
-    Let(&'hir Pat<'hir>, &'hir Expr<'hir>, Span),
+    Let(&'hir Let<'hir>),
     /// An `if` block, with an optional else block.
     ///
     /// I.e., `if <expr> { <expr> } else { <expr> }`.
index 21f89104c4b5878f09c02b47a19507798e3a531c..afe4f8e1417b45338a6c2977ff240a35d5313e2b 100644 (file)
@@ -389,6 +389,9 @@ fn visit_anon_const(&mut self, c: &'v AnonConst) {
     fn visit_expr(&mut self, ex: &'v Expr<'v>) {
         walk_expr(self, ex)
     }
+    fn visit_let_expr(&mut self, lex: &'v Let<'v>) {
+        walk_let_expr(self, lex)
+    }
     fn visit_ty(&mut self, t: &'v Ty<'v>) {
         walk_ty(self, t)
     }
@@ -1126,6 +1129,14 @@ pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonCo
     visitor.visit_nested_body(constant.body);
 }
 
+pub fn walk_let_expr<'v, V: Visitor<'v>>(visitor: &mut V, let_expr: &'v Let<'v>) {
+    // match the visit order in walk_local
+    visitor.visit_expr(let_expr.init);
+    visitor.visit_id(let_expr.hir_id);
+    visitor.visit_pat(let_expr.pat);
+    walk_list!(visitor, visit_ty, let_expr.ty);
+}
+
 pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) {
     visitor.visit_id(expression.hir_id);
     match expression.kind {
@@ -1172,10 +1183,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
         ExprKind::DropTemps(ref subexpression) => {
             visitor.visit_expr(subexpression);
         }
-        ExprKind::Let(ref pat, ref expr, _) => {
-            visitor.visit_expr(expr);
-            visitor.visit_pat(pat);
-        }
+        ExprKind::Let(ref let_expr) => visitor.visit_let_expr(let_expr),
         ExprKind::If(ref cond, ref then, ref else_opt) => {
             visitor.visit_expr(cond);
             visitor.visit_expr(then);
index 65cdf10f9016a8a8884d401f7305a4195a60a3da..44f2328b309edf5f84e27484653b47b9ee7bb911 100644 (file)
@@ -1101,13 +1101,17 @@ fn print_expr_cond_paren(&mut self, expr: &hir::Expr<'_>, needs_par: bool) {
     }
 
     /// Print a `let pat = expr` expression.
-    fn print_let(&mut self, pat: &hir::Pat<'_>, expr: &hir::Expr<'_>) {
-        self.word("let ");
+    fn print_let(&mut self, pat: &hir::Pat<'_>, ty: Option<&hir::Ty<'_>>, init: &hir::Expr<'_>) {
+        self.word_space("let");
         self.print_pat(pat);
+        if let Some(ty) = ty {
+            self.word_space(":");
+            self.print_type(ty);
+        }
         self.space();
         self.word_space("=");
-        let npals = || parser::needs_par_as_let_scrutinee(expr.precedence().order());
-        self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals())
+        let npals = || parser::needs_par_as_let_scrutinee(init.precedence().order());
+        self.print_expr_cond_paren(init, Self::cond_needs_par(init) || npals())
     }
 
     // Does `expr` need parentheses when printed in a condition position?
@@ -1462,8 +1466,8 @@ pub fn print_expr(&mut self, expr: &hir::Expr<'_>) {
                 // Print `}`:
                 self.bclose_maybe_open(expr.span, true);
             }
-            hir::ExprKind::Let(ref pat, ref scrutinee, _) => {
-                self.print_let(pat, scrutinee);
+            hir::ExprKind::Let(hir::Let { pat, ty, init, .. }) => {
+                self.print_let(pat, *ty, init);
             }
             hir::ExprKind::If(ref test, ref blk, ref elseopt) => {
                 self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e));
index b4005ccd1cc42205db0909d37699f2b802fbf561..092fe131174703b32343eb75f608ed07900fd302 100644 (file)
@@ -605,9 +605,10 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
                 },
                 Err(err) => bug!("invalid loop id for continue: {}", err),
             },
-            hir::ExprKind::Let(ref pat, ref expr, _) => {
-                ExprKind::Let { expr: self.mirror_expr(expr), pat: self.pattern_from_hir(pat) }
-            }
+            hir::ExprKind::Let(let_expr) => ExprKind::Let {
+                expr: self.mirror_expr(let_expr.init),
+                pat: self.pattern_from_hir(let_expr.pat),
+            },
             hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
                 if_then_scope: region::Scope {
                     id: then.hir_id.local_id,
index d74c53fae533b425eac31501a70a6240edf2a825..7a4fd6ffc4adef9d1a6f186860b4eb44b4ab870a 100644 (file)
@@ -64,7 +64,9 @@ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
         intravisit::walk_expr(self, ex);
         match &ex.kind {
             hir::ExprKind::Match(scrut, arms, source) => self.check_match(scrut, arms, *source),
-            hir::ExprKind::Let(pat, scrut, span) => self.check_let(pat, scrut, *span),
+            hir::ExprKind::Let(hir::Let { pat, init, span, .. }) => {
+                self.check_let(pat, init, *span)
+            }
             _ => {}
         }
     }
@@ -148,9 +150,9 @@ fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'p, 'tcx> {
         }
     }
 
-    fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) {
+    fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, scrutinee: &hir::Expr<'_>, span: Span) {
         self.check_patterns(pat, Refutable);
-        let mut cx = self.new_cx(expr.hir_id);
+        let mut cx = self.new_cx(scrutinee.hir_id);
         let tpat = self.lower_pattern(&mut cx, pat, &mut false);
         check_let_reachability(&mut cx, pat.hir_id, tpat, span);
     }
index 3d7a215754acaf63e0aaed41c3deda44cc715744..4ae1e5cee925e7625517b7d07a2eb4b4eec02420 100644 (file)
@@ -429,8 +429,8 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
                 intravisit::walk_expr(self, expr);
             }
 
-            hir::ExprKind::Let(ref pat, ..) => {
-                self.add_from_pat(pat);
+            hir::ExprKind::Let(let_expr) => {
+                self.add_from_pat(let_expr.pat);
                 intravisit::walk_expr(self, expr);
             }
 
@@ -856,9 +856,9 @@ fn propagate_through_expr(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNod
                 })
             }
 
-            hir::ExprKind::Let(ref pat, ref scrutinee, _) => {
-                let succ = self.propagate_through_expr(scrutinee, succ);
-                self.define_bindings_in_pat(pat, succ)
+            hir::ExprKind::Let(let_expr) => {
+                let succ = self.propagate_through_expr(let_expr.init, succ);
+                self.define_bindings_in_pat(let_expr.pat, succ)
             }
 
             // Note that labels have been resolved, so we don't need to look
@@ -1401,8 +1401,8 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
             }
         }
 
-        hir::ExprKind::Let(ref pat, ..) => {
-            this.check_unused_vars_in_pat(pat, None, |_, _, _, _| {});
+        hir::ExprKind::Let(let_expr) => {
+            this.check_unused_vars_in_pat(let_expr.pat, None, |_, _, _, _| {});
         }
 
         // no correctness conditions related to liveness
index 311106474bea008dff0a8608316965513a02f095..fd30723dcda420016226d97ecd97e9efabfc73c5 100644 (file)
@@ -300,7 +300,7 @@ fn check_expr_kind(
                 }
             }
             ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
-            ExprKind::Let(pat, let_expr, _) => self.check_expr_let(let_expr, pat),
+            ExprKind::Let(let_expr) => self.check_expr_let(let_expr),
             ExprKind::Loop(body, _, source, _) => {
                 self.check_expr_loop(body, source, expected, expr)
             }
@@ -1048,10 +1048,13 @@ fn check_expr_assign(
         }
     }
 
-    fn check_expr_let(&self, expr: &'tcx hir::Expr<'tcx>, pat: &'tcx hir::Pat<'tcx>) -> Ty<'tcx> {
-        self.warn_if_unreachable(expr.hir_id, expr.span, "block in `let` expression");
-        let expr_ty = self.demand_scrutinee_type(expr, pat.contains_explicit_ref_binding(), false);
-        self.check_pat_top(pat, expr_ty, Some(expr.span), true);
+    fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> {
+        // for let statements, this is done in check_stmt
+        let init = let_expr.init;
+        self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression");
+        // otherwise check exactly as a let statement
+        self.check_decl(let_expr.into());
+        // but return a bool, for this is a boolean expression
         self.tcx.types.bool
     }
 
index 4cb597cb6d6c79e68644c2e33833a86a42bda392..f3f3f99e4eb7fe75ec59836612be5948db0dc14f 100644 (file)
@@ -1,5 +1,6 @@
 use crate::astconv::AstConv;
 use crate::check::coercion::CoerceMany;
+use crate::check::gather_locals::Declaration;
 use crate::check::method::MethodCallee;
 use crate::check::Expectation::*;
 use crate::check::TupleArgumentsFlag::*;
@@ -538,16 +539,17 @@ pub fn check_struct_path(
 
     pub fn check_decl_initializer(
         &self,
-        local: &'tcx hir::Local<'tcx>,
+        hir_id: hir::HirId,
+        pat: &'tcx hir::Pat<'tcx>,
         init: &'tcx hir::Expr<'tcx>,
     ) -> Ty<'tcx> {
         // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
         // for #42640 (default match binding modes).
         //
         // See #44848.
-        let ref_bindings = local.pat.contains_explicit_ref_binding();
+        let ref_bindings = pat.contains_explicit_ref_binding();
 
-        let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty;
+        let local_ty = self.local_ty(init.span, hir_id).revealed_ty;
         if let Some(m) = ref_bindings {
             // Somewhat subtle: if we have a `ref` binding in the pattern,
             // we want to avoid introducing coercions for the RHS. This is
@@ -565,29 +567,33 @@ pub fn check_decl_initializer(
         }
     }
 
-    /// Type check a `let` statement.
-    pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
+    pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
         // Determine and write the type which we'll check the pattern against.
-        let ty = self.local_ty(local.span, local.hir_id).decl_ty;
-        self.write_ty(local.hir_id, ty);
+        let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty;
+        self.write_ty(decl.hir_id, decl_ty);
 
         // Type check the initializer.
-        if let Some(ref init) = local.init {
-            let init_ty = self.check_decl_initializer(local, &init);
-            self.overwrite_local_ty_if_err(local, ty, init_ty);
+        if let Some(ref init) = decl.init {
+            let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, &init);
+            self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, init_ty);
         }
 
         // Does the expected pattern type originate from an expression and what is the span?
-        let (origin_expr, ty_span) = match (local.ty, local.init) {
+        let (origin_expr, ty_span) = match (decl.ty, decl.init) {
             (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
             (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee.
             _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
         };
 
         // Type check the pattern. Override if necessary to avoid knock-on errors.
-        self.check_pat_top(&local.pat, ty, ty_span, origin_expr);
-        let pat_ty = self.node_ty(local.pat.hir_id);
-        self.overwrite_local_ty_if_err(local, ty, pat_ty);
+        self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr);
+        let pat_ty = self.node_ty(decl.pat.hir_id);
+        self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty);
+    }
+
+    /// Type check a `let` statement.
+    pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
+        self.check_decl(local.into());
     }
 
     pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>, is_last: bool) {
@@ -891,17 +897,18 @@ fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
 
     fn overwrite_local_ty_if_err(
         &self,
-        local: &'tcx hir::Local<'tcx>,
+        hir_id: hir::HirId,
+        pat: &'tcx hir::Pat<'tcx>,
         decl_ty: Ty<'tcx>,
         ty: Ty<'tcx>,
     ) {
         if ty.references_error() {
             // Override the types everywhere with `err()` to avoid knock on errors.
-            self.write_ty(local.hir_id, ty);
-            self.write_ty(local.pat.hir_id, ty);
+            self.write_ty(hir_id, ty);
+            self.write_ty(pat.hir_id, ty);
             let local_ty = LocalTy { decl_ty, revealed_ty: ty };
-            self.locals.borrow_mut().insert(local.hir_id, local_ty);
-            self.locals.borrow_mut().insert(local.pat.hir_id, local_ty);
+            self.locals.borrow_mut().insert(hir_id, local_ty);
+            self.locals.borrow_mut().insert(pat.hir_id, local_ty);
         }
     }
 
index 4ebfd7fd21200aad744e75699ed87959f78439e7..839bd56b396ef1911b30a34635101bfa22c2130e 100644 (file)
@@ -7,6 +7,31 @@
 use rustc_span::Span;
 use rustc_trait_selection::traits;
 
+/// A declaration is an abstraction of [hir::Local] and [hir::Let].
+///
+/// It must have a hir_id, as this is how we connect gather_locals to the check functions.
+pub(super) struct Declaration<'a> {
+    pub hir_id: hir::HirId,
+    pub pat: &'a hir::Pat<'a>,
+    pub ty: Option<&'a hir::Ty<'a>>,
+    pub span: Span,
+    pub init: Option<&'a hir::Expr<'a>>,
+}
+
+impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> {
+    fn from(local: &'a hir::Local<'a>) -> Self {
+        let hir::Local { hir_id, pat, ty, span, init, .. } = *local;
+        Declaration { hir_id, pat, ty, span, init }
+    }
+}
+
+impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> {
+    fn from(let_expr: &'a hir::Let<'a>) -> Self {
+        let hir::Let { hir_id, pat, ty, span, init } = *let_expr;
+        Declaration { hir_id, pat, ty, span, init: Some(init) }
+    }
+}
+
 pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
     fcx: &'a FnCtxt<'a, 'tcx>,
     // parameters are special cases of patterns, but we want to handle them as
@@ -41,18 +66,12 @@ fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>)
             }
         }
     }
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
-    type Map = intravisit::ErasedMap<'tcx>;
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
 
-    // Add explicitly-declared locals.
-    fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
-        let local_ty = match local.ty {
+    /// Allocates a [LocalTy] for a declaration, which may have a type annotation. If it does have
+    /// a type annotation, then the LocalTy stored will be the resolved type. This may be found
+    /// again during type checking by querying [FnCtxt::local_ty] for the same hir_id.
+    fn declare(&mut self, decl: Declaration<'tcx>) {
+        let local_ty = match decl.ty {
             Some(ref ty) => {
                 let o_ty = self.fcx.to_ty(&ty);
 
@@ -68,16 +87,34 @@ fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
             }
             None => None,
         };
-        self.assign(local.span, local.hir_id, local_ty);
+        self.assign(decl.span, decl.hir_id, local_ty);
 
         debug!(
             "local variable {:?} is assigned type {}",
-            local.pat,
-            self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&local.hir_id).unwrap().decl_ty)
+            decl.pat,
+            self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&decl.hir_id).unwrap().decl_ty)
         );
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
+    type Map = intravisit::ErasedMap<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    // Add explicitly-declared locals.
+    fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
+        self.declare(local.into());
         intravisit::walk_local(self, local);
     }
 
+    fn visit_let_expr(&mut self, let_expr: &'tcx hir::Let<'tcx>) {
+        self.declare(let_expr.into());
+        intravisit::walk_let_expr(self, let_expr);
+    }
+
     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
         let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span);
         intravisit::walk_param(self, param);
index 7d0600b99e36e45d416ed6266695b0339946baba..bd09f4b4dcba42e1dc7cdc1d2c1e15735545ff0f 100644 (file)
@@ -229,8 +229,8 @@ pub fn walk_expr(&mut self, expr: &hir::Expr<'_>) {
                 }
             }
 
-            hir::ExprKind::Let(pat, ref expr, _) => {
-                self.walk_local(expr, pat, |t| t.borrow_expr(expr, ty::ImmBorrow));
+            hir::ExprKind::Let(hir::Let { pat, init, .. }) => {
+                self.walk_local(init, pat, |t| t.borrow_expr(init, ty::ImmBorrow));
             }
 
             hir::ExprKind::Match(ref discr, arms, _) => {