From af2f0e6b7ceb6a4344d83fc39ae80320e9b9e851 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 13 Oct 2021 16:39:06 +1100 Subject: [PATCH] let-else: add hir::Let and type check it like a hir::Local unify typeck of hir::Local and hir::Let remove extraneous pub(crate/super) --- compiler/rustc_ast_lowering/src/block.rs | 31 +++------ compiler/rustc_ast_lowering/src/expr.rs | 14 ++-- compiler/rustc_hir/src/arena.rs | 1 + compiler/rustc_hir/src/hir.rs | 18 ++++- compiler/rustc_hir/src/intravisit.rs | 16 +++-- compiler/rustc_hir_pretty/src/lib.rs | 16 +++-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 7 +- .../src/thir/pattern/check_match.rs | 8 ++- compiler/rustc_passes/src/liveness.rs | 14 ++-- compiler/rustc_typeck/src/check/expr.rs | 13 ++-- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 45 +++++++------ .../rustc_typeck/src/check/gather_locals.rs | 65 +++++++++++++++---- compiler/rustc_typeck/src/expr_use_visitor.rs | 4 +- 13 files changed, 160 insertions(+), 92 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 14a894d61f4f5..4eab936f85fa0 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -2,7 +2,6 @@ use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext}; 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 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { 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 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { 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 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; 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 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) .emit(); } - (stmt, if_expr) + if_expr } } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index c9578c2f50f90..d88292e0b9084 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -92,11 +92,15 @@ impl<'hir> LoweringContext<'_, '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()) } diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index f19ca497d8bf2..edad00ed6a2fe 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -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>, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 9a8bc2efbfa46..4cf7a5b1264c5 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1176,10 +1176,24 @@ pub struct Arm<'hir> { pub body: &'hir Expr<'hir>, } +/// Represents a `let [: ] = ` 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 = ) { ... }`; 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 { } else { }`. diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 21f89104c4b58..afe4f8e1417b4 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -389,6 +389,9 @@ pub trait Visitor<'v>: Sized { 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); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 65cdf10f9016a..44f2328b309ed 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1101,13 +1101,17 @@ impl<'a> State<'a> { } /// 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 @@ impl<'a> State<'a> { // 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)); diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b4005ccd1cc42..092fe13117470 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -605,9 +605,10 @@ impl<'tcx> Cx<'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, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index d74c53fae533b..7a4fd6ffc4ade 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -64,7 +64,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, '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 @@ impl<'p, 'tcx> MatchVisitor<'_, '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); } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 3d7a215754aca..4ae1e5cee925e 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -429,8 +429,8 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'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 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { }) } - 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 diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 311106474bea0..fd30723dcda42 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -300,7 +300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - 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 } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 4cb597cb6d6c7..f3f3f99e4eb7f 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - /// 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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); } } diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs index 4ebfd7fd21200..839bd56b396ef 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_typeck/src/check/gather_locals.rs @@ -7,6 +7,31 @@ use rustc_middle::ty::Ty; 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 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { } } } -} - -impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - 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 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, '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 { + 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); diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 7d0600b99e36e..bd09f4b4dcba4 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -229,8 +229,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } - 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, _) => {