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)
This commit is contained in:
parent
a0a4c7d1e4
commit
af2f0e6b7c
13 changed files with 160 additions and 92 deletions
|
@ -2,7 +2,6 @@ use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
|
||||||
use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
|
use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
use rustc_span::symbol::Ident;
|
|
||||||
use rustc_span::{sym, DesugaringKind};
|
use rustc_span::{sym, DesugaringKind};
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -39,8 +38,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
let hir_id = self.lower_node_id(s.id);
|
let hir_id = self.lower_node_id(s.id);
|
||||||
match &local.kind {
|
match &local.kind {
|
||||||
LocalKind::InitElse(init, els) => {
|
LocalKind::InitElse(init, els) => {
|
||||||
let (s, e) = self.lower_let_else(hir_id, local, init, els, tail);
|
let e = self.lower_let_else(hir_id, local, init, els, tail);
|
||||||
stmts.push(s);
|
|
||||||
expr = Some(e);
|
expr = Some(e);
|
||||||
// remaining statements are in let-else expression
|
// remaining statements are in let-else expression
|
||||||
break;
|
break;
|
||||||
|
@ -125,36 +123,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
init: &Expr,
|
init: &Expr,
|
||||||
els: &Block,
|
els: &Block,
|
||||||
tail: &[Stmt],
|
tail: &[Stmt],
|
||||||
) -> (hir::Stmt<'hir>, &'hir hir::Expr<'hir>) {
|
) -> &'hir hir::Expr<'hir> {
|
||||||
let ty = local
|
let ty = local
|
||||||
.ty
|
.ty
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
|
.map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
|
||||||
let span = self.lower_span(local.span);
|
let span = self.lower_span(local.span);
|
||||||
let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None);
|
let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None);
|
||||||
let init = Some(self.lower_expr(init));
|
let init = 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 local_hir_id = self.lower_node_id(local.id);
|
let local_hir_id = self.lower_node_id(local.id);
|
||||||
self.lower_attrs(local_hir_id, &local.attrs);
|
self.lower_attrs(local_hir_id, &local.attrs);
|
||||||
// first statement which basically exists for the type annotation
|
let let_expr = {
|
||||||
let stmt = {
|
let lex = self.arena.alloc(hir::Let {
|
||||||
let local = self.arena.alloc(hir::Local {
|
|
||||||
hir_id: local_hir_id,
|
hir_id: local_hir_id,
|
||||||
|
pat: self.lower_pat(&local.pat),
|
||||||
ty,
|
ty,
|
||||||
pat,
|
|
||||||
init,
|
init,
|
||||||
span,
|
span,
|
||||||
source: hir::LocalSource::Normal,
|
|
||||||
});
|
});
|
||||||
let kind = hir::StmtKind::Local(local);
|
self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), AttrVec::new()))
|
||||||
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()))
|
|
||||||
};
|
};
|
||||||
let then_expr = {
|
let then_expr = {
|
||||||
let (stmts, expr) = self.lower_stmts(tail);
|
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);
|
self.alias_attrs(else_expr.hir_id, local_hir_id);
|
||||||
let if_expr = self.arena.alloc(hir::Expr {
|
let if_expr = self.arena.alloc(hir::Expr {
|
||||||
hir_id: self.next_id(),
|
hir_id: stmt_hir_id,
|
||||||
span,
|
span,
|
||||||
kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
|
kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
|
||||||
});
|
});
|
||||||
|
@ -180,6 +167,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
)
|
)
|
||||||
.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
(stmt, if_expr)
|
if_expr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,11 +92,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
let ohs = self.lower_expr(ohs);
|
let ohs = self.lower_expr(ohs);
|
||||||
hir::ExprKind::AddrOf(k, m, ohs)
|
hir::ExprKind::AddrOf(k, m, ohs)
|
||||||
}
|
}
|
||||||
ExprKind::Let(ref pat, ref scrutinee, span) => hir::ExprKind::Let(
|
ExprKind::Let(ref pat, ref scrutinee, span) => {
|
||||||
self.lower_pat(pat),
|
hir::ExprKind::Let(self.arena.alloc(hir::Let {
|
||||||
self.lower_expr(scrutinee),
|
hir_id: self.next_id(),
|
||||||
self.lower_span(span),
|
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) => {
|
ExprKind::If(ref cond, ref then, ref else_opt) => {
|
||||||
self.lower_expr_if(cond, then, else_opt.as_deref())
|
self.lower_expr_if(cond, then, else_opt.as_deref())
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ macro_rules! arena_types {
|
||||||
[] generic_bound: rustc_hir::GenericBound<'tcx>,
|
[] generic_bound: rustc_hir::GenericBound<'tcx>,
|
||||||
[] generic_param: rustc_hir::GenericParam<'tcx>,
|
[] generic_param: rustc_hir::GenericParam<'tcx>,
|
||||||
[] expr: rustc_hir::Expr<'tcx>,
|
[] expr: rustc_hir::Expr<'tcx>,
|
||||||
|
[] let_expr: rustc_hir::Let<'tcx>,
|
||||||
[] expr_field: rustc_hir::ExprField<'tcx>,
|
[] expr_field: rustc_hir::ExprField<'tcx>,
|
||||||
[] pat_field: rustc_hir::PatField<'tcx>,
|
[] pat_field: rustc_hir::PatField<'tcx>,
|
||||||
[] fn_decl: rustc_hir::FnDecl<'tcx>,
|
[] fn_decl: rustc_hir::FnDecl<'tcx>,
|
||||||
|
|
|
@ -1176,10 +1176,24 @@ pub struct Arm<'hir> {
|
||||||
pub body: &'hir Expr<'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)]
|
#[derive(Debug, HashStable_Generic)]
|
||||||
pub enum Guard<'hir> {
|
pub enum Guard<'hir> {
|
||||||
If(&'hir Expr<'hir>),
|
If(&'hir Expr<'hir>),
|
||||||
// FIXME use ExprKind::Let for this.
|
// FIXME use hir::Let for this.
|
||||||
IfLet(&'hir Pat<'hir>, &'hir Expr<'hir>),
|
IfLet(&'hir Pat<'hir>, &'hir Expr<'hir>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1696,7 +1710,7 @@ pub enum ExprKind<'hir> {
|
||||||
///
|
///
|
||||||
/// These are not `Local` and only occur as expressions.
|
/// 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(..)`.
|
/// 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.
|
/// An `if` block, with an optional else block.
|
||||||
///
|
///
|
||||||
/// I.e., `if <expr> { <expr> } else { <expr> }`.
|
/// I.e., `if <expr> { <expr> } else { <expr> }`.
|
||||||
|
|
|
@ -389,6 +389,9 @@ pub trait Visitor<'v>: Sized {
|
||||||
fn visit_expr(&mut self, ex: &'v Expr<'v>) {
|
fn visit_expr(&mut self, ex: &'v Expr<'v>) {
|
||||||
walk_expr(self, ex)
|
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>) {
|
fn visit_ty(&mut self, t: &'v Ty<'v>) {
|
||||||
walk_ty(self, t)
|
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);
|
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>) {
|
pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) {
|
||||||
visitor.visit_id(expression.hir_id);
|
visitor.visit_id(expression.hir_id);
|
||||||
match expression.kind {
|
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) => {
|
ExprKind::DropTemps(ref subexpression) => {
|
||||||
visitor.visit_expr(subexpression);
|
visitor.visit_expr(subexpression);
|
||||||
}
|
}
|
||||||
ExprKind::Let(ref pat, ref expr, _) => {
|
ExprKind::Let(ref let_expr) => visitor.visit_let_expr(let_expr),
|
||||||
visitor.visit_expr(expr);
|
|
||||||
visitor.visit_pat(pat);
|
|
||||||
}
|
|
||||||
ExprKind::If(ref cond, ref then, ref else_opt) => {
|
ExprKind::If(ref cond, ref then, ref else_opt) => {
|
||||||
visitor.visit_expr(cond);
|
visitor.visit_expr(cond);
|
||||||
visitor.visit_expr(then);
|
visitor.visit_expr(then);
|
||||||
|
|
|
@ -1101,13 +1101,17 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print a `let pat = expr` expression.
|
/// Print a `let pat = expr` expression.
|
||||||
fn print_let(&mut self, pat: &hir::Pat<'_>, expr: &hir::Expr<'_>) {
|
fn print_let(&mut self, pat: &hir::Pat<'_>, ty: Option<&hir::Ty<'_>>, init: &hir::Expr<'_>) {
|
||||||
self.word("let ");
|
self.word_space("let");
|
||||||
self.print_pat(pat);
|
self.print_pat(pat);
|
||||||
|
if let Some(ty) = ty {
|
||||||
|
self.word_space(":");
|
||||||
|
self.print_type(ty);
|
||||||
|
}
|
||||||
self.space();
|
self.space();
|
||||||
self.word_space("=");
|
self.word_space("=");
|
||||||
let npals = || parser::needs_par_as_let_scrutinee(expr.precedence().order());
|
let npals = || parser::needs_par_as_let_scrutinee(init.precedence().order());
|
||||||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals())
|
self.print_expr_cond_paren(init, Self::cond_needs_par(init) || npals())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does `expr` need parentheses when printed in a condition position?
|
// Does `expr` need parentheses when printed in a condition position?
|
||||||
|
@ -1462,8 +1466,8 @@ impl<'a> State<'a> {
|
||||||
// Print `}`:
|
// Print `}`:
|
||||||
self.bclose_maybe_open(expr.span, true);
|
self.bclose_maybe_open(expr.span, true);
|
||||||
}
|
}
|
||||||
hir::ExprKind::Let(ref pat, ref scrutinee, _) => {
|
hir::ExprKind::Let(hir::Let { pat, ty, init, .. }) => {
|
||||||
self.print_let(pat, scrutinee);
|
self.print_let(pat, *ty, init);
|
||||||
}
|
}
|
||||||
hir::ExprKind::If(ref test, ref blk, ref elseopt) => {
|
hir::ExprKind::If(ref test, ref blk, ref elseopt) => {
|
||||||
self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e));
|
self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e));
|
||||||
|
|
|
@ -605,9 +605,10 @@ impl<'tcx> Cx<'tcx> {
|
||||||
},
|
},
|
||||||
Err(err) => bug!("invalid loop id for continue: {}", err),
|
Err(err) => bug!("invalid loop id for continue: {}", err),
|
||||||
},
|
},
|
||||||
hir::ExprKind::Let(ref pat, ref expr, _) => {
|
hir::ExprKind::Let(let_expr) => ExprKind::Let {
|
||||||
ExprKind::Let { expr: self.mirror_expr(expr), pat: self.pattern_from_hir(pat) }
|
expr: self.mirror_expr(let_expr.init),
|
||||||
}
|
pat: self.pattern_from_hir(let_expr.pat),
|
||||||
|
},
|
||||||
hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
|
hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
|
||||||
if_then_scope: region::Scope {
|
if_then_scope: region::Scope {
|
||||||
id: then.hir_id.local_id,
|
id: then.hir_id.local_id,
|
||||||
|
|
|
@ -64,7 +64,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
|
||||||
intravisit::walk_expr(self, ex);
|
intravisit::walk_expr(self, ex);
|
||||||
match &ex.kind {
|
match &ex.kind {
|
||||||
hir::ExprKind::Match(scrut, arms, source) => self.check_match(scrut, arms, *source),
|
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);
|
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);
|
let tpat = self.lower_pattern(&mut cx, pat, &mut false);
|
||||||
check_let_reachability(&mut cx, pat.hir_id, tpat, span);
|
check_let_reachability(&mut cx, pat.hir_id, tpat, span);
|
||||||
}
|
}
|
||||||
|
|
|
@ -429,8 +429,8 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
|
||||||
intravisit::walk_expr(self, expr);
|
intravisit::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::ExprKind::Let(ref pat, ..) => {
|
hir::ExprKind::Let(let_expr) => {
|
||||||
self.add_from_pat(pat);
|
self.add_from_pat(let_expr.pat);
|
||||||
intravisit::walk_expr(self, expr);
|
intravisit::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -856,9 +856,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::ExprKind::Let(ref pat, ref scrutinee, _) => {
|
hir::ExprKind::Let(let_expr) => {
|
||||||
let succ = self.propagate_through_expr(scrutinee, succ);
|
let succ = self.propagate_through_expr(let_expr.init, succ);
|
||||||
self.define_bindings_in_pat(pat, succ)
|
self.define_bindings_in_pat(let_expr.pat, succ)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that labels have been resolved, so we don't need to look
|
// 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, ..) => {
|
hir::ExprKind::Let(let_expr) => {
|
||||||
this.check_unused_vars_in_pat(pat, None, |_, _, _, _| {});
|
this.check_unused_vars_in_pat(let_expr.pat, None, |_, _, _, _| {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// no correctness conditions related to liveness
|
// no correctness conditions related to liveness
|
||||||
|
|
|
@ -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::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, _) => {
|
ExprKind::Loop(body, _, source, _) => {
|
||||||
self.check_expr_loop(body, source, expected, expr)
|
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> {
|
fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> {
|
||||||
self.warn_if_unreachable(expr.hir_id, expr.span, "block in `let` expression");
|
// for let statements, this is done in check_stmt
|
||||||
let expr_ty = self.demand_scrutinee_type(expr, pat.contains_explicit_ref_binding(), false);
|
let init = let_expr.init;
|
||||||
self.check_pat_top(pat, expr_ty, Some(expr.span), true);
|
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
|
self.tcx.types.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::astconv::AstConv;
|
use crate::astconv::AstConv;
|
||||||
use crate::check::coercion::CoerceMany;
|
use crate::check::coercion::CoerceMany;
|
||||||
|
use crate::check::gather_locals::Declaration;
|
||||||
use crate::check::method::MethodCallee;
|
use crate::check::method::MethodCallee;
|
||||||
use crate::check::Expectation::*;
|
use crate::check::Expectation::*;
|
||||||
use crate::check::TupleArgumentsFlag::*;
|
use crate::check::TupleArgumentsFlag::*;
|
||||||
|
@ -538,16 +539,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
pub fn check_decl_initializer(
|
pub fn check_decl_initializer(
|
||||||
&self,
|
&self,
|
||||||
local: &'tcx hir::Local<'tcx>,
|
hir_id: hir::HirId,
|
||||||
|
pat: &'tcx hir::Pat<'tcx>,
|
||||||
init: &'tcx hir::Expr<'tcx>,
|
init: &'tcx hir::Expr<'tcx>,
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
// FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
|
// FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
|
||||||
// for #42640 (default match binding modes).
|
// for #42640 (default match binding modes).
|
||||||
//
|
//
|
||||||
// See #44848.
|
// 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 {
|
if let Some(m) = ref_bindings {
|
||||||
// Somewhat subtle: if we have a `ref` binding in the pattern,
|
// Somewhat subtle: if we have a `ref` binding in the pattern,
|
||||||
// we want to avoid introducing coercions for the RHS. This is
|
// 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(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
|
||||||
pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
|
|
||||||
// Determine and write the type which we'll check the pattern against.
|
// Determine and write the type which we'll check the pattern against.
|
||||||
let ty = self.local_ty(local.span, local.hir_id).decl_ty;
|
let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty;
|
||||||
self.write_ty(local.hir_id, ty);
|
self.write_ty(decl.hir_id, decl_ty);
|
||||||
|
|
||||||
// Type check the initializer.
|
// Type check the initializer.
|
||||||
if let Some(ref init) = local.init {
|
if let Some(ref init) = decl.init {
|
||||||
let init_ty = self.check_decl_initializer(local, &init);
|
let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, &init);
|
||||||
self.overwrite_local_ty_if_err(local, ty, init_ty);
|
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?
|
// 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(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
|
||||||
(_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee.
|
(_, 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.
|
_ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Type check the pattern. Override if necessary to avoid knock-on errors.
|
// Type check the pattern. Override if necessary to avoid knock-on errors.
|
||||||
self.check_pat_top(&local.pat, ty, ty_span, origin_expr);
|
self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr);
|
||||||
let pat_ty = self.node_ty(local.pat.hir_id);
|
let pat_ty = self.node_ty(decl.pat.hir_id);
|
||||||
self.overwrite_local_ty_if_err(local, ty, pat_ty);
|
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) {
|
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(
|
fn overwrite_local_ty_if_err(
|
||||||
&self,
|
&self,
|
||||||
local: &'tcx hir::Local<'tcx>,
|
hir_id: hir::HirId,
|
||||||
|
pat: &'tcx hir::Pat<'tcx>,
|
||||||
decl_ty: Ty<'tcx>,
|
decl_ty: Ty<'tcx>,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
) {
|
) {
|
||||||
if ty.references_error() {
|
if ty.references_error() {
|
||||||
// Override the types everywhere with `err()` to avoid knock on errors.
|
// Override the types everywhere with `err()` to avoid knock on errors.
|
||||||
self.write_ty(local.hir_id, ty);
|
self.write_ty(hir_id, ty);
|
||||||
self.write_ty(local.pat.hir_id, ty);
|
self.write_ty(pat.hir_id, ty);
|
||||||
let local_ty = LocalTy { decl_ty, revealed_ty: 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(hir_id, local_ty);
|
||||||
self.locals.borrow_mut().insert(local.pat.hir_id, local_ty);
|
self.locals.borrow_mut().insert(pat.hir_id, local_ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,31 @@ use rustc_middle::ty::Ty;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_trait_selection::traits;
|
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> {
|
pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
|
||||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||||
// parameters are special cases of patterns, but we want to handle them as
|
// 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> {
|
/// Allocates a [LocalTy] for a declaration, which may have a type annotation. If it does have
|
||||||
type Map = intravisit::ErasedMap<'tcx>;
|
/// 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 nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
fn declare(&mut self, decl: Declaration<'tcx>) {
|
||||||
NestedVisitorMap::None
|
let local_ty = match decl.ty {
|
||||||
}
|
|
||||||
|
|
||||||
// Add explicitly-declared locals.
|
|
||||||
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
|
|
||||||
let local_ty = match local.ty {
|
|
||||||
Some(ref ty) => {
|
Some(ref ty) => {
|
||||||
let o_ty = self.fcx.to_ty(&ty);
|
let o_ty = self.fcx.to_ty(&ty);
|
||||||
|
|
||||||
|
@ -68,16 +87,34 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
self.assign(local.span, local.hir_id, local_ty);
|
self.assign(decl.span, decl.hir_id, local_ty);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"local variable {:?} is assigned type {}",
|
"local variable {:?} is assigned type {}",
|
||||||
local.pat,
|
decl.pat,
|
||||||
self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&local.hir_id).unwrap().decl_ty)
|
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);
|
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>) {
|
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);
|
let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span);
|
||||||
intravisit::walk_param(self, param);
|
intravisit::walk_param(self, param);
|
||||||
|
|
|
@ -229,8 +229,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::ExprKind::Let(pat, ref expr, _) => {
|
hir::ExprKind::Let(hir::Let { pat, init, .. }) => {
|
||||||
self.walk_local(expr, pat, |t| t.borrow_expr(expr, ty::ImmBorrow));
|
self.walk_local(init, pat, |t| t.borrow_expr(init, ty::ImmBorrow));
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::ExprKind::Match(ref discr, arms, _) => {
|
hir::ExprKind::Match(ref discr, arms, _) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue