Remove hir::ExprKind::If and replace it with lowering to hir::ExprKind::Match.
This commit is contained in:
parent
0ac53da03d
commit
5cccda1770
@ -166,47 +166,6 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
|
||||
self.add_ast_node(expr.hir_id.local_id, &[blk_exit])
|
||||
}
|
||||
|
||||
hir::ExprKind::If(ref cond, ref then, None) => {
|
||||
//
|
||||
// [pred]
|
||||
// |
|
||||
// v 1
|
||||
// [cond]
|
||||
// |
|
||||
// / \
|
||||
// / \
|
||||
// v 2 *
|
||||
// [then] |
|
||||
// | |
|
||||
// v 3 v 4
|
||||
// [..expr..]
|
||||
//
|
||||
let cond_exit = self.expr(&cond, pred); // 1
|
||||
let then_exit = self.expr(&then, cond_exit); // 2
|
||||
self.add_ast_node(expr.hir_id.local_id, &[cond_exit, then_exit]) // 3,4
|
||||
}
|
||||
|
||||
hir::ExprKind::If(ref cond, ref then, Some(ref otherwise)) => {
|
||||
//
|
||||
// [pred]
|
||||
// |
|
||||
// v 1
|
||||
// [cond]
|
||||
// |
|
||||
// / \
|
||||
// / \
|
||||
// v 2 v 3
|
||||
// [then][otherwise]
|
||||
// | |
|
||||
// v 4 v 5
|
||||
// [..expr..]
|
||||
//
|
||||
let cond_exit = self.expr(&cond, pred); // 1
|
||||
let then_exit = self.expr(&then, cond_exit); // 2
|
||||
let else_exit = self.expr(&otherwise, cond_exit); // 3
|
||||
self.add_ast_node(expr.hir_id.local_id, &[then_exit, else_exit]) // 4, 5
|
||||
}
|
||||
|
||||
hir::ExprKind::While(ref cond, ref body, _) => {
|
||||
//
|
||||
// [pred]
|
||||
|
@ -1032,11 +1032,6 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
|
||||
ExprKind::DropTemps(ref subexpression) => {
|
||||
visitor.visit_expr(subexpression);
|
||||
}
|
||||
ExprKind::If(ref head_expression, ref if_block, ref optional_else) => {
|
||||
visitor.visit_expr(head_expression);
|
||||
visitor.visit_expr(if_block);
|
||||
walk_list!(visitor, visit_expr, optional_else);
|
||||
}
|
||||
ExprKind::While(ref subexpression, ref block, ref opt_label) => {
|
||||
walk_list!(visitor, visit_label, opt_label);
|
||||
visitor.visit_expr(subexpression);
|
||||
|
@ -62,6 +62,7 @@ use syntax::ext::hygiene::Mark;
|
||||
use syntax::print::pprust;
|
||||
use syntax::ptr::P;
|
||||
use syntax::source_map::{respan, CompilerDesugaringKind, Spanned};
|
||||
use syntax::source_map::CompilerDesugaringKind::IfTemporary;
|
||||
use syntax::std_inject;
|
||||
use syntax::symbol::{keywords, Symbol};
|
||||
use syntax::tokenstream::{TokenStream, TokenTree};
|
||||
@ -4115,31 +4116,46 @@ impl<'a> LoweringContext<'a> {
|
||||
}
|
||||
// More complicated than you might expect because the else branch
|
||||
// might be `if let`.
|
||||
ExprKind::If(ref cond, ref blk, ref else_opt) => {
|
||||
let else_opt = else_opt.as_ref().map(|els| {
|
||||
match els.node {
|
||||
ExprKind::If(ref cond, ref then, ref else_opt) => {
|
||||
// `true => then`:
|
||||
let then_pat = self.pat_bool(e.span, true);
|
||||
let then_blk = self.lower_block(then, false);
|
||||
let then_expr = self.expr_block(then_blk, ThinVec::new());
|
||||
let then_arm = self.arm(hir_vec![then_pat], P(then_expr));
|
||||
|
||||
// `_ => else_block` where `else_block` is `{}` if there's `None`:
|
||||
let else_pat = self.pat_wild(e.span);
|
||||
let else_expr = match else_opt {
|
||||
None => self.expr_block_empty(e.span),
|
||||
Some(els) => match els.node {
|
||||
ExprKind::IfLet(..) => {
|
||||
// Wrap the `if let` expr in a block.
|
||||
let span = els.span;
|
||||
let els = P(self.lower_expr(els));
|
||||
let blk = P(hir::Block {
|
||||
stmts: hir_vec![],
|
||||
expr: Some(els),
|
||||
hir_id: self.next_id(),
|
||||
rules: hir::DefaultBlock,
|
||||
span,
|
||||
targeted_by_break: false,
|
||||
});
|
||||
P(self.expr_block(blk, ThinVec::new()))
|
||||
let els = self.lower_expr(els);
|
||||
let blk = self.block_all(els.span, hir_vec![], Some(P(els)));
|
||||
self.expr_block(P(blk), ThinVec::new())
|
||||
}
|
||||
_ => P(self.lower_expr(els)),
|
||||
_ => self.lower_expr(els),
|
||||
}
|
||||
});
|
||||
};
|
||||
let else_arm = self.arm(hir_vec![else_pat], P(else_expr));
|
||||
|
||||
let then_blk = self.lower_block(blk, false);
|
||||
let then_expr = self.expr_block(then_blk, ThinVec::new());
|
||||
// Lower condition:
|
||||
let span_block = self
|
||||
.sess
|
||||
.source_map()
|
||||
.mark_span_with_reason(IfTemporary, cond.span, None);
|
||||
let cond = self.lower_expr(cond);
|
||||
// Wrap in a construct equivalent to `{ let _t = $cond; _t }` to preserve drop
|
||||
// semantics since `if cond { ... }` don't let temporaries live outside of `cond`.
|
||||
let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
|
||||
|
||||
hir::ExprKind::If(P(self.lower_expr(cond)), P(then_expr), else_opt)
|
||||
hir::ExprKind::Match(
|
||||
P(cond),
|
||||
vec![then_arm, else_arm].into(),
|
||||
hir::MatchSource::IfDesugar {
|
||||
contains_else_clause: else_opt.is_some()
|
||||
},
|
||||
)
|
||||
}
|
||||
ExprKind::While(ref cond, ref body, opt_label) => self.with_loop_scope(e.id, |this| {
|
||||
hir::ExprKind::While(
|
||||
@ -4486,16 +4502,16 @@ impl<'a> LoweringContext<'a> {
|
||||
arms.push(self.arm(pats, body_expr));
|
||||
}
|
||||
|
||||
// _ => [<else_opt>|()]
|
||||
// _ => [<else_opt>|{}]
|
||||
{
|
||||
let wildcard_arm: Option<&Expr> = else_opt.as_ref().map(|p| &**p);
|
||||
let wildcard_pattern = self.pat_wild(e.span);
|
||||
let body = if let Some(else_expr) = wildcard_arm {
|
||||
P(self.lower_expr(else_expr))
|
||||
self.lower_expr(else_expr)
|
||||
} else {
|
||||
P(self.expr_tuple(e.span, hir_vec![]))
|
||||
self.expr_block_empty(e.span)
|
||||
};
|
||||
arms.push(self.arm(hir_vec![wildcard_pattern], body));
|
||||
arms.push(self.arm(hir_vec![wildcard_pattern], P(body)));
|
||||
}
|
||||
|
||||
let contains_else_clause = else_opt.is_some();
|
||||
@ -4658,11 +4674,7 @@ impl<'a> LoweringContext<'a> {
|
||||
ThinVec::new(),
|
||||
))
|
||||
};
|
||||
let match_stmt = hir::Stmt {
|
||||
hir_id: self.next_id(),
|
||||
node: hir::StmtKind::Expr(match_expr),
|
||||
span: head_sp,
|
||||
};
|
||||
let match_stmt = self.stmt(head_sp, hir::StmtKind::Expr(match_expr));
|
||||
|
||||
let next_expr = P(self.expr_ident(head_sp, next_ident, next_pat_hid));
|
||||
|
||||
@ -4685,11 +4697,7 @@ impl<'a> LoweringContext<'a> {
|
||||
|
||||
let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false));
|
||||
let body_expr = P(self.expr_block(body_block, ThinVec::new()));
|
||||
let body_stmt = hir::Stmt {
|
||||
hir_id: self.next_id(),
|
||||
node: hir::StmtKind::Expr(body_expr),
|
||||
span: body.span,
|
||||
};
|
||||
let body_stmt = self.stmt(body.span, hir::StmtKind::Expr(body_expr));
|
||||
|
||||
let loop_block = P(self.block_all(
|
||||
e.span,
|
||||
@ -4869,12 +4877,7 @@ impl<'a> LoweringContext<'a> {
|
||||
.into_iter()
|
||||
.map(|item_id| {
|
||||
let item_id = hir::ItemId { id: self.lower_node_id(item_id) };
|
||||
|
||||
hir::Stmt {
|
||||
hir_id: self.next_id(),
|
||||
node: hir::StmtKind::Item(item_id),
|
||||
span: s.span,
|
||||
}
|
||||
self.stmt(s.span, hir::StmtKind::Item(item_id))
|
||||
})
|
||||
.collect();
|
||||
ids.push({
|
||||
@ -5174,28 +5177,32 @@ impl<'a> LoweringContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn stmt(&mut self, span: Span, node: hir::StmtKind) -> hir::Stmt {
|
||||
hir::Stmt { span, node, hir_id: self.next_id() }
|
||||
}
|
||||
|
||||
fn stmt_let_pat(
|
||||
&mut self,
|
||||
sp: Span,
|
||||
ex: Option<P<hir::Expr>>,
|
||||
span: Span,
|
||||
init: Option<P<hir::Expr>>,
|
||||
pat: P<hir::Pat>,
|
||||
source: hir::LocalSource,
|
||||
) -> hir::Stmt {
|
||||
let local = hir::Local {
|
||||
pat,
|
||||
ty: None,
|
||||
init: ex,
|
||||
init,
|
||||
hir_id: self.next_id(),
|
||||
span: sp,
|
||||
attrs: ThinVec::new(),
|
||||
span,
|
||||
source,
|
||||
attrs: ThinVec::new()
|
||||
};
|
||||
self.stmt(span, hir::StmtKind::Local(P(local)))
|
||||
}
|
||||
|
||||
hir::Stmt {
|
||||
hir_id: self.next_id(),
|
||||
node: hir::StmtKind::Local(P(local)),
|
||||
span: sp
|
||||
}
|
||||
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())
|
||||
}
|
||||
|
||||
fn block_expr(&mut self, expr: P<hir::Expr>) -> hir::Block {
|
||||
@ -5235,6 +5242,13 @@ impl<'a> LoweringContext<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Constructs a `true` or `false` literal pattern.
|
||||
fn pat_bool(&mut self, span: Span, val: bool) -> P<hir::Pat> {
|
||||
let lit = Spanned { span, node: LitKind::Bool(val) };
|
||||
let expr = self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new());
|
||||
self.pat(span, hir::PatKind::Lit(P(expr)))
|
||||
}
|
||||
|
||||
fn pat_ok(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
|
||||
self.pat_std_enum(span, &["result", "Result", "Ok"], hir_vec![pat])
|
||||
}
|
||||
@ -5622,15 +5636,7 @@ impl<'a> LoweringContext<'a> {
|
||||
&["task", "Poll", "Pending"],
|
||||
hir_vec![],
|
||||
);
|
||||
let empty_block = P(hir::Block {
|
||||
stmts: hir_vec![],
|
||||
expr: None,
|
||||
hir_id: self.next_id(),
|
||||
rules: hir::DefaultBlock,
|
||||
span,
|
||||
targeted_by_break: false,
|
||||
});
|
||||
let empty_block = P(self.expr_block(empty_block, ThinVec::new()));
|
||||
let empty_block = P(self.expr_block_empty(span));
|
||||
self.arm(hir_vec![pending_pat], empty_block)
|
||||
};
|
||||
|
||||
|
@ -1368,7 +1368,6 @@ impl Expr {
|
||||
ExprKind::Lit(_) => ExprPrecedence::Lit,
|
||||
ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast,
|
||||
ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
|
||||
ExprKind::If(..) => ExprPrecedence::If,
|
||||
ExprKind::While(..) => ExprPrecedence::While,
|
||||
ExprKind::Loop(..) => ExprPrecedence::Loop,
|
||||
ExprKind::Match(..) => ExprPrecedence::Match,
|
||||
@ -1421,7 +1420,6 @@ impl Expr {
|
||||
ExprKind::MethodCall(..) |
|
||||
ExprKind::Struct(..) |
|
||||
ExprKind::Tup(..) |
|
||||
ExprKind::If(..) |
|
||||
ExprKind::Match(..) |
|
||||
ExprKind::Closure(..) |
|
||||
ExprKind::Block(..) |
|
||||
@ -1498,10 +1496,6 @@ pub enum ExprKind {
|
||||
/// This construct only exists to tweak the drop order in HIR lowering.
|
||||
/// An example of that is the desugaring of `for` loops.
|
||||
DropTemps(P<Expr>),
|
||||
/// An `if` block, with an optional else block.
|
||||
///
|
||||
/// I.e., `if <expr> { <expr> } else { <expr> }`.
|
||||
If(P<Expr>, P<Expr>, Option<P<Expr>>),
|
||||
/// A while loop, with an optional label
|
||||
///
|
||||
/// I.e., `'label: while expr { <block> }`.
|
||||
@ -1615,6 +1609,10 @@ pub enum LocalSource {
|
||||
pub enum MatchSource {
|
||||
/// A `match _ { .. }`.
|
||||
Normal,
|
||||
/// An `if _ { .. }` (optionally with `else { .. }`).
|
||||
IfDesugar {
|
||||
contains_else_clause: bool,
|
||||
},
|
||||
/// An `if let _ = _ { .. }` (optionally with `else { .. }`).
|
||||
IfLetDesugar {
|
||||
contains_else_clause: bool,
|
||||
|
@ -1093,65 +1093,6 @@ impl<'a> State<'a> {
|
||||
self.ann.post(self, AnnNode::Block(blk))
|
||||
}
|
||||
|
||||
fn print_else(&mut self, els: Option<&hir::Expr>) -> io::Result<()> {
|
||||
match els {
|
||||
Some(_else) => {
|
||||
match _else.node {
|
||||
// "another else-if"
|
||||
hir::ExprKind::If(ref i, ref then, ref e) => {
|
||||
self.cbox(indent_unit - 1)?;
|
||||
self.ibox(0)?;
|
||||
self.s.word(" else if ")?;
|
||||
self.print_expr_as_cond(&i)?;
|
||||
self.s.space()?;
|
||||
self.print_expr(&then)?;
|
||||
self.print_else(e.as_ref().map(|e| &**e))
|
||||
}
|
||||
// "final else"
|
||||
hir::ExprKind::Block(ref b, _) => {
|
||||
self.cbox(indent_unit - 1)?;
|
||||
self.ibox(0)?;
|
||||
self.s.word(" else ")?;
|
||||
self.print_block(&b)
|
||||
}
|
||||
// BLEAH, constraints would be great here
|
||||
_ => {
|
||||
panic!("print_if saw if with weird alternative");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_if(&mut self,
|
||||
test: &hir::Expr,
|
||||
blk: &hir::Expr,
|
||||
elseopt: Option<&hir::Expr>)
|
||||
-> io::Result<()> {
|
||||
self.head("if")?;
|
||||
self.print_expr_as_cond(test)?;
|
||||
self.s.space()?;
|
||||
self.print_expr(blk)?;
|
||||
self.print_else(elseopt)
|
||||
}
|
||||
|
||||
pub fn print_if_let(&mut self,
|
||||
pat: &hir::Pat,
|
||||
expr: &hir::Expr,
|
||||
blk: &hir::Block,
|
||||
elseopt: Option<&hir::Expr>)
|
||||
-> io::Result<()> {
|
||||
self.head("if let")?;
|
||||
self.print_pat(pat)?;
|
||||
self.s.space()?;
|
||||
self.word_space("=")?;
|
||||
self.print_expr_as_cond(expr)?;
|
||||
self.s.space()?;
|
||||
self.print_block(blk)?;
|
||||
self.print_else(elseopt)
|
||||
}
|
||||
|
||||
pub fn print_anon_const(&mut self, constant: &hir::AnonConst) -> io::Result<()> {
|
||||
self.ann.nested(self, Nested::Body(constant.body))
|
||||
}
|
||||
@ -1406,9 +1347,6 @@ impl<'a> State<'a> {
|
||||
// Print `}`:
|
||||
self.bclose_maybe_open(expr.span, indent_unit, true)?;
|
||||
}
|
||||
hir::ExprKind::If(ref test, ref blk, ref elseopt) => {
|
||||
self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e))?;
|
||||
}
|
||||
hir::ExprKind::While(ref test, ref blk, opt_label) => {
|
||||
if let Some(label) = opt_label {
|
||||
self.print_ident(label.ident)?;
|
||||
@ -2414,7 +2352,6 @@ impl<'a> State<'a> {
|
||||
/// isn't parsed as (if true {...} else {...} | x) | 5
|
||||
fn expr_requires_semi_to_be_stmt(e: &hir::Expr) -> bool {
|
||||
match e.node {
|
||||
hir::ExprKind::If(..) |
|
||||
hir::ExprKind::Match(..) |
|
||||
hir::ExprKind::Block(..) |
|
||||
hir::ExprKind::While(..) |
|
||||
|
@ -395,6 +395,7 @@ impl_stable_hash_for!(enum ::syntax_pos::hygiene::ExpnFormat {
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(enum ::syntax_pos::hygiene::CompilerDesugaringKind {
|
||||
IfTemporary,
|
||||
Async,
|
||||
Await,
|
||||
QuestionMark,
|
||||
|
@ -424,14 +424,6 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
|
||||
self.consume_exprs(exprs);
|
||||
}
|
||||
|
||||
hir::ExprKind::If(ref cond_expr, ref then_expr, ref opt_else_expr) => {
|
||||
self.consume_expr(&cond_expr);
|
||||
self.walk_expr(&then_expr);
|
||||
if let Some(ref else_expr) = *opt_else_expr {
|
||||
self.consume_expr(&else_expr);
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::Match(ref discr, ref arms, _) => {
|
||||
let discr_cmt = Rc::new(return_if_err!(self.mc.cat_expr(&discr)));
|
||||
let r = self.tcx().lifetimes.re_empty;
|
||||
|
@ -500,7 +500,6 @@ fn visit_expr<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
}
|
||||
|
||||
// live nodes required for interesting control flow:
|
||||
hir::ExprKind::If(..) |
|
||||
hir::ExprKind::Match(..) |
|
||||
hir::ExprKind::While(..) |
|
||||
hir::ExprKind::Loop(..) => {
|
||||
@ -1040,28 +1039,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
hir::ExprKind::If(ref cond, ref then, ref els) => {
|
||||
//
|
||||
// (cond)
|
||||
// |
|
||||
// v
|
||||
// (expr)
|
||||
// / \
|
||||
// | |
|
||||
// v v
|
||||
// (then)(els)
|
||||
// | |
|
||||
// v v
|
||||
// ( succ )
|
||||
//
|
||||
let else_ln = self.propagate_through_opt_expr(els.as_ref().map(|e| &**e), succ);
|
||||
let then_ln = self.propagate_through_expr(&then, succ);
|
||||
let ln = self.live_node(expr.hir_id, expr.span);
|
||||
self.init_from_succ(ln, else_ln);
|
||||
self.merge_from_succ(ln, then_ln, false);
|
||||
self.propagate_through_expr(&cond, ln)
|
||||
}
|
||||
|
||||
hir::ExprKind::While(ref cond, ref blk, _) => {
|
||||
self.propagate_through_loop(expr, WhileLoop(&cond), &blk, succ)
|
||||
}
|
||||
@ -1523,7 +1500,7 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
}
|
||||
|
||||
// no correctness conditions related to liveness
|
||||
hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) | hir::ExprKind::If(..) |
|
||||
hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) |
|
||||
hir::ExprKind::Match(..) | hir::ExprKind::While(..) | hir::ExprKind::Loop(..) |
|
||||
hir::ExprKind::Index(..) | hir::ExprKind::Field(..) |
|
||||
hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) | hir::ExprKind::Binary(..) |
|
||||
|
@ -678,7 +678,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
|
||||
hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) |
|
||||
hir::ExprKind::Unary(..) | hir::ExprKind::Yield(..) |
|
||||
hir::ExprKind::MethodCall(..) | hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) |
|
||||
hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) | hir::ExprKind::If(..) |
|
||||
hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) |
|
||||
hir::ExprKind::Binary(..) | hir::ExprKind::While(..) |
|
||||
hir::ExprKind::Block(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..) |
|
||||
hir::ExprKind::Lit(..) | hir::ExprKind::Break(..) |
|
||||
|
@ -884,17 +884,6 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
|
||||
terminating(r.hir_id.local_id);
|
||||
}
|
||||
|
||||
hir::ExprKind::If(ref expr, ref then, Some(ref otherwise)) => {
|
||||
terminating(expr.hir_id.local_id);
|
||||
terminating(then.hir_id.local_id);
|
||||
terminating(otherwise.hir_id.local_id);
|
||||
}
|
||||
|
||||
hir::ExprKind::If(ref expr, ref then, None) => {
|
||||
terminating(expr.hir_id.local_id);
|
||||
terminating(then.hir_id.local_id);
|
||||
}
|
||||
|
||||
hir::ExprKind::Loop(ref body, _, _) => {
|
||||
terminating(body.hir_id.local_id);
|
||||
}
|
||||
|
@ -100,6 +100,18 @@ impl<'a> DiagnosticBuilder<'a> {
|
||||
self.cancel();
|
||||
}
|
||||
|
||||
/// Emit the diagnostic unless `delay` is true,
|
||||
/// in which case the emission will be delayed as a bug.
|
||||
///
|
||||
/// See `emit` and `delay_as_bug` for details.
|
||||
pub fn emit_unless(&mut self, delay: bool) {
|
||||
if delay {
|
||||
self.delay_as_bug()
|
||||
} else {
|
||||
self.emit()
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffers the diagnostic for later emission, unless handler
|
||||
/// has disabled such buffering.
|
||||
pub fn buffer(mut self, buffered_diagnostics: &mut Vec<Diagnostic>) {
|
||||
|
@ -192,7 +192,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
| ExprKind::Pointer { .. }
|
||||
| ExprKind::Repeat { .. }
|
||||
| ExprKind::Borrow { .. }
|
||||
| ExprKind::If { .. }
|
||||
| ExprKind::Match { .. }
|
||||
| ExprKind::Loop { .. }
|
||||
| ExprKind::Block { .. }
|
||||
|
@ -345,7 +345,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
ExprKind::Literal { .. }
|
||||
| ExprKind::Block { .. }
|
||||
| ExprKind::Match { .. }
|
||||
| ExprKind::If { .. }
|
||||
| ExprKind::NeverToAny { .. }
|
||||
| ExprKind::Use { .. }
|
||||
| ExprKind::Loop { .. }
|
||||
|
@ -45,7 +45,6 @@ impl Category {
|
||||
| ExprKind::ValueTypeAscription { .. } => Some(Category::Place),
|
||||
|
||||
ExprKind::LogicalOp { .. }
|
||||
| ExprKind::If { .. }
|
||||
| ExprKind::Match { .. }
|
||||
| ExprKind::NeverToAny { .. }
|
||||
| ExprKind::Use { .. }
|
||||
|
@ -76,43 +76,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
end_block.unit()
|
||||
}
|
||||
}
|
||||
ExprKind::If {
|
||||
condition: cond_expr,
|
||||
then: then_expr,
|
||||
otherwise: else_expr,
|
||||
} => {
|
||||
let operand = unpack!(block = this.as_local_operand(block, cond_expr));
|
||||
|
||||
let mut then_block = this.cfg.start_new_block();
|
||||
let mut else_block = this.cfg.start_new_block();
|
||||
let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
|
||||
this.cfg.terminate(block, source_info, term);
|
||||
|
||||
unpack!(then_block = this.into(destination, then_block, then_expr));
|
||||
else_block = if let Some(else_expr) = else_expr {
|
||||
unpack!(this.into(destination, else_block, else_expr))
|
||||
} else {
|
||||
// Body of the `if` expression without an `else` clause must return `()`, thus
|
||||
// we implicitly generate a `else {}` if it is not specified.
|
||||
this.cfg
|
||||
.push_assign_unit(else_block, source_info, destination);
|
||||
else_block
|
||||
};
|
||||
|
||||
let join_block = this.cfg.start_new_block();
|
||||
this.cfg.terminate(
|
||||
then_block,
|
||||
source_info,
|
||||
TerminatorKind::Goto { target: join_block },
|
||||
);
|
||||
this.cfg.terminate(
|
||||
else_block,
|
||||
source_info,
|
||||
TerminatorKind::Goto { target: join_block },
|
||||
);
|
||||
|
||||
join_block.unit()
|
||||
}
|
||||
ExprKind::LogicalOp { op, lhs, rhs } => {
|
||||
// And:
|
||||
//
|
||||
|
@ -601,13 +601,6 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
||||
arms: arms.iter().map(|a| convert_arm(cx, a)).collect(),
|
||||
}
|
||||
}
|
||||
hir::ExprKind::If(ref cond, ref then, ref otherwise) => {
|
||||
ExprKind::If {
|
||||
condition: cond.to_ref(),
|
||||
then: then.to_ref(),
|
||||
otherwise: otherwise.to_ref(),
|
||||
}
|
||||
}
|
||||
hir::ExprKind::While(ref cond, ref body, _) => {
|
||||
ExprKind::Loop {
|
||||
condition: Some(cond.to_ref()),
|
||||
|
@ -185,11 +185,6 @@ pub enum ExprKind<'tcx> {
|
||||
cast: PointerCast,
|
||||
source: ExprRef<'tcx>,
|
||||
},
|
||||
If {
|
||||
condition: ExprRef<'tcx>,
|
||||
then: ExprRef<'tcx>,
|
||||
otherwise: Option<ExprRef<'tcx>>,
|
||||
},
|
||||
Loop {
|
||||
condition: Option<ExprRef<'tcx>>,
|
||||
body: ExprRef<'tcx>,
|
||||
|
@ -365,6 +365,7 @@ fn check_arms<'a, 'tcx>(
|
||||
match is_useful(cx, &seen, &v, LeaveOutWitness) {
|
||||
NotUseful => {
|
||||
match source {
|
||||
hir::MatchSource::IfDesugar { .. } => bug!(),
|
||||
hir::MatchSource::IfLetDesugar { .. } => {
|
||||
cx.tcx.lint_hir(
|
||||
lint::builtin::IRREFUTABLE_LET_PATTERNS,
|
||||
|
@ -518,15 +518,6 @@ fn check_expr_kind<'a, 'tcx>(
|
||||
NotPromotable
|
||||
}
|
||||
|
||||
hir::ExprKind::If(ref lhs, ref rhs, ref option_expr) => {
|
||||
let _ = v.check_expr(lhs);
|
||||
let _ = v.check_expr(rhs);
|
||||
if let Some(ref expr) = option_expr {
|
||||
let _ = v.check_expr(&expr);
|
||||
}
|
||||
NotPromotable
|
||||
}
|
||||
|
||||
// Loops (not very meaningful in constants).
|
||||
hir::ExprKind::While(ref expr, ref box_block, ref _option_label) => {
|
||||
let _ = v.check_expr(expr);
|
||||
|
@ -2,12 +2,12 @@ use crate::check::{FnCtxt, Expectation, Diverges, Needs};
|
||||
use crate::check::coercion::CoerceMany;
|
||||
use crate::util::nodemap::FxHashMap;
|
||||
use errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc::hir::{self, PatKind, Pat};
|
||||
use rustc::hir::{self, PatKind, Pat, ExprKind};
|
||||
use rustc::hir::def::{Res, DefKind, CtorKind};
|
||||
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
|
||||
use rustc::infer;
|
||||
use rustc::infer::type_variable::TypeVariableOrigin;
|
||||
use rustc::traits::ObligationCauseCode;
|
||||
use rustc::traits::{ObligationCause, ObligationCauseCode};
|
||||
use rustc::ty::{self, Ty, TypeFoldable};
|
||||
use rustc::ty::subst::Kind;
|
||||
use syntax::ast;
|
||||
@ -15,6 +15,7 @@ use syntax::source_map::Spanned;
|
||||
use syntax::ptr::P;
|
||||
use syntax::util::lev_distance::find_best_match_for_name;
|
||||
use syntax_pos::Span;
|
||||
use syntax_pos::hygiene::CompilerDesugaringKind;
|
||||
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
use std::cmp;
|
||||
@ -22,10 +23,9 @@ use std::cmp;
|
||||
use super::report_unexpected_variant_res;
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
/// `match_discrim_span` argument having a `Span` indicates that this pattern is part of
|
||||
/// a match expression arm guard, and it points to the match discriminant to add context
|
||||
/// in type errors. In the folloowing example, `match_discrim_span` corresponds to the
|
||||
/// `a + b` expression:
|
||||
/// `discrim_span` argument having a `Span` indicates that this pattern is part of a match
|
||||
/// expression arm guard, and it points to the match discriminant to add context in type errors.
|
||||
/// In the following example, `discrim_span` corresponds to the `a + b` expression:
|
||||
///
|
||||
/// ```text
|
||||
/// error[E0308]: mismatched types
|
||||
@ -44,7 +44,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pat: &'gcx hir::Pat,
|
||||
mut expected: Ty<'tcx>,
|
||||
mut def_bm: ty::BindingMode,
|
||||
match_discrim_span: Option<Span>,
|
||||
discrim_span: Option<Span>,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
|
||||
@ -176,7 +176,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// &'static str <: expected
|
||||
//
|
||||
// that's equivalent to there existing a LUB.
|
||||
self.demand_suptype(pat.span, expected, pat_ty);
|
||||
if let Some(mut err) = self.demand_suptype_diag(pat.span, expected, pat_ty) {
|
||||
err.emit_unless(discrim_span
|
||||
.filter(|&s| s.is_compiler_desugaring(CompilerDesugaringKind::IfTemporary))
|
||||
.is_some());
|
||||
}
|
||||
|
||||
pat_ty
|
||||
}
|
||||
PatKind::Range(ref begin, ref end, _) => {
|
||||
@ -224,8 +229,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
let common_type = self.resolve_type_vars_if_possible(&lhs_ty);
|
||||
|
||||
// subtyping doesn't matter here, as the value is some kind of scalar
|
||||
self.demand_eqtype_pat(pat.span, expected, lhs_ty, match_discrim_span);
|
||||
self.demand_eqtype_pat(pat.span, expected, rhs_ty, match_discrim_span);
|
||||
self.demand_eqtype_pat(pat.span, expected, lhs_ty, discrim_span);
|
||||
self.demand_eqtype_pat(pat.span, expected, rhs_ty, discrim_span);
|
||||
common_type
|
||||
}
|
||||
PatKind::Binding(ba, var_id, _, ref sub) => {
|
||||
@ -254,13 +259,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` is
|
||||
// required. However, we use equality, which is stronger. See (*) for
|
||||
// an explanation.
|
||||
self.demand_eqtype_pat(pat.span, region_ty, local_ty, match_discrim_span);
|
||||
self.demand_eqtype_pat(pat.span, region_ty, local_ty, discrim_span);
|
||||
}
|
||||
// otherwise the type of x is the expected type T
|
||||
ty::BindByValue(_) => {
|
||||
// As above, `T <: typeof(x)` is required but we
|
||||
// use equality, see (*) below.
|
||||
self.demand_eqtype_pat(pat.span, expected, local_ty, match_discrim_span);
|
||||
self.demand_eqtype_pat(pat.span, expected, local_ty, discrim_span);
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,11 +273,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// what the type of the binding `x` ought to be
|
||||
if var_id != pat.hir_id {
|
||||
let vt = self.local_ty(pat.span, var_id).decl_ty;
|
||||
self.demand_eqtype_pat(pat.span, vt, local_ty, match_discrim_span);
|
||||
self.demand_eqtype_pat(pat.span, vt, local_ty, discrim_span);
|
||||
}
|
||||
|
||||
if let Some(ref p) = *sub {
|
||||
self.check_pat_walk(&p, expected, def_bm, match_discrim_span);
|
||||
self.check_pat_walk(&p, expected, def_bm, discrim_span);
|
||||
}
|
||||
|
||||
local_ty
|
||||
@ -285,14 +290,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
ddpos,
|
||||
expected,
|
||||
def_bm,
|
||||
match_discrim_span,
|
||||
discrim_span,
|
||||
)
|
||||
}
|
||||
PatKind::Path(ref qpath) => {
|
||||
self.check_pat_path(pat, qpath, expected)
|
||||
}
|
||||
PatKind::Struct(ref qpath, ref fields, etc) => {
|
||||
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, match_discrim_span)
|
||||
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, discrim_span)
|
||||
}
|
||||
PatKind::Tuple(ref elements, ddpos) => {
|
||||
let mut expected_len = elements.len();
|
||||
@ -318,7 +323,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// further errors being emitted when using the bindings. #50333
|
||||
let element_tys_iter = (0..max_len).map(|_| tcx.types.err);
|
||||
for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
|
||||
self.check_pat_walk(elem, &tcx.types.err, def_bm, match_discrim_span);
|
||||
self.check_pat_walk(elem, &tcx.types.err, def_bm, discrim_span);
|
||||
}
|
||||
tcx.mk_tup(element_tys_iter)
|
||||
} else {
|
||||
@ -327,7 +332,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
elem,
|
||||
&element_tys[i].expect_ty(),
|
||||
def_bm,
|
||||
match_discrim_span,
|
||||
discrim_span,
|
||||
);
|
||||
}
|
||||
pat_ty
|
||||
@ -341,11 +346,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// Here, `demand::subtype` is good enough, but I don't
|
||||
// think any errors can be introduced by using
|
||||
// `demand::eqtype`.
|
||||
self.demand_eqtype_pat(pat.span, expected, uniq_ty, match_discrim_span);
|
||||
self.check_pat_walk(&inner, inner_ty, def_bm, match_discrim_span);
|
||||
self.demand_eqtype_pat(pat.span, expected, uniq_ty, discrim_span);
|
||||
self.check_pat_walk(&inner, inner_ty, def_bm, discrim_span);
|
||||
uniq_ty
|
||||
} else {
|
||||
self.check_pat_walk(&inner, tcx.types.err, def_bm, match_discrim_span);
|
||||
self.check_pat_walk(&inner, tcx.types.err, def_bm, discrim_span);
|
||||
tcx.types.err
|
||||
}
|
||||
}
|
||||
@ -384,10 +389,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
self.check_pat_walk(&inner, inner_ty, def_bm, match_discrim_span);
|
||||
self.check_pat_walk(&inner, inner_ty, def_bm, discrim_span);
|
||||
rptr_ty
|
||||
} else {
|
||||
self.check_pat_walk(&inner, tcx.types.err, def_bm, match_discrim_span);
|
||||
self.check_pat_walk(&inner, tcx.types.err, def_bm, discrim_span);
|
||||
tcx.types.err
|
||||
}
|
||||
}
|
||||
@ -445,13 +450,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
};
|
||||
|
||||
for elt in before {
|
||||
self.check_pat_walk(&elt, inner_ty, def_bm, match_discrim_span);
|
||||
self.check_pat_walk(&elt, inner_ty, def_bm, discrim_span);
|
||||
}
|
||||
if let Some(ref slice) = *slice {
|
||||
self.check_pat_walk(&slice, slice_ty, def_bm, match_discrim_span);
|
||||
self.check_pat_walk(&slice, slice_ty, def_bm, discrim_span);
|
||||
}
|
||||
for elt in after {
|
||||
self.check_pat_walk(&elt, inner_ty, def_bm, match_discrim_span);
|
||||
self.check_pat_walk(&elt, inner_ty, def_bm, discrim_span);
|
||||
}
|
||||
expected_ty
|
||||
}
|
||||
@ -595,6 +600,321 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
|
||||
) -> Ty<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
use hir::MatchSource::*;
|
||||
let (source_if, if_no_else, if_desugar) = match match_src {
|
||||
IfDesugar { contains_else_clause } => (true, !contains_else_clause, true),
|
||||
IfLetDesugar { contains_else_clause } => (true, !contains_else_clause, false),
|
||||
_ => (false, false, false),
|
||||
};
|
||||
|
||||
// Type check the descriminant and get its type.
|
||||
let discrim_ty = if if_desugar {
|
||||
// Here we want to ensure:
|
||||
//
|
||||
// 1. That default match bindings are *not* accepted in the condition of an
|
||||
// `if` expression. E.g. given `fn foo() -> &bool;` we reject `if foo() { .. }`.
|
||||
//
|
||||
// 2. By expecting `bool` for `expr` we get nice diagnostics for e.g. `if x = y { .. }`.
|
||||
self.check_expr_has_type_or_error(discrim, self.tcx.types.bool)
|
||||
} else {
|
||||
self.demand_discriminant_type(arms, discrim)
|
||||
};
|
||||
|
||||
// If there are no arms, that is a diverging match; a special case.
|
||||
if arms.is_empty() {
|
||||
self.diverges.set(self.diverges.get() | Diverges::Always);
|
||||
return tcx.types.never;
|
||||
}
|
||||
|
||||
self.warn_arms_when_scrutinee_diverges(arms, source_if);
|
||||
|
||||
// Otherwise, we have to union together the types that the
|
||||
// arms produce and so forth.
|
||||
let discrim_diverges = self.diverges.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
// rust-lang/rust#55810: Typecheck patterns first (via eager
|
||||
// collection into `Vec`), so we get types for all bindings.
|
||||
let all_arm_pats_diverge: Vec<_> = arms.iter().map(|arm| {
|
||||
let mut all_pats_diverge = Diverges::WarnedAlways;
|
||||
for p in &arm.pats {
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
let binding_mode = ty::BindingMode::BindByValue(hir::Mutability::MutImmutable);
|
||||
self.check_pat_walk(&p, discrim_ty, binding_mode, Some(discrim.span));
|
||||
all_pats_diverge &= self.diverges.get();
|
||||
}
|
||||
|
||||
// As discussed with @eddyb, this is for disabling unreachable_code
|
||||
// warnings on patterns (they're now subsumed by unreachable_patterns
|
||||
// warnings).
|
||||
match all_pats_diverge {
|
||||
Diverges::Maybe => Diverges::Maybe,
|
||||
Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
// Now typecheck the blocks.
|
||||
//
|
||||
// The result of the match is the common supertype of all the
|
||||
// arms. Start out the value as bottom, since it's the, well,
|
||||
// bottom the type lattice, and we'll be moving up the lattice as
|
||||
// we process each arm. (Note that any match with 0 arms is matching
|
||||
// on any empty type and is therefore unreachable; should the flow
|
||||
// of execution reach it, we will panic, so bottom is an appropriate
|
||||
// type in that case)
|
||||
let mut all_arms_diverge = Diverges::WarnedAlways;
|
||||
|
||||
let expected = expected.adjust_for_branches(self);
|
||||
|
||||
let mut coercion = {
|
||||
let coerce_first = match expected {
|
||||
// We don't coerce to `()` so that if the match expression is a
|
||||
// statement it's branches can have any consistent type. That allows
|
||||
// us to give better error messages (pointing to a usually better
|
||||
// arm for inconsistent arms or to the whole match when a `()` type
|
||||
// is required).
|
||||
Expectation::ExpectHasType(ety) if ety != self.tcx.mk_unit() => ety,
|
||||
_ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)),
|
||||
};
|
||||
CoerceMany::with_coercion_sites(coerce_first, arms)
|
||||
};
|
||||
|
||||
let mut other_arms = vec![]; // used only for diagnostics
|
||||
let mut prior_arm_ty = None;
|
||||
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
|
||||
if let Some(g) = &arm.guard {
|
||||
self.diverges.set(pats_diverge);
|
||||
match g {
|
||||
hir::Guard::If(e) => self.check_expr_has_type_or_error(e, tcx.types.bool),
|
||||
};
|
||||
}
|
||||
|
||||
self.diverges.set(pats_diverge);
|
||||
let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
|
||||
all_arms_diverge &= self.diverges.get();
|
||||
|
||||
let span = expr.span;
|
||||
|
||||
if source_if {
|
||||
let then_expr = &arms[0].body;
|
||||
match (i, if_no_else) {
|
||||
(0, _) => coercion.coerce(self, &self.misc(span), then_expr, arm_ty),
|
||||
(_, true) => self.if_fallback_coercion(span, then_expr, &mut coercion),
|
||||
(_, _) => {
|
||||
let then_ty = prior_arm_ty.unwrap();
|
||||
let cause = self.if_cause(span, then_expr, &arm.body, then_ty, arm_ty);
|
||||
coercion.coerce(self, &cause, &arm.body, arm_ty);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let arm_span = if let hir::ExprKind::Block(blk, _) = &arm.body.node {
|
||||
// Point at the block expr instead of the entire block
|
||||
blk.expr.as_ref().map(|e| e.span).unwrap_or(arm.body.span)
|
||||
} else {
|
||||
arm.body.span
|
||||
};
|
||||
let (span, code) = match i {
|
||||
// The reason for the first arm to fail is not that the match arms diverge,
|
||||
// but rather that there's a prior obligation that doesn't hold.
|
||||
0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
|
||||
_ => (span, ObligationCauseCode::MatchExpressionArm {
|
||||
arm_span,
|
||||
source: match_src,
|
||||
prior_arms: other_arms.clone(),
|
||||
last_ty: prior_arm_ty.unwrap(),
|
||||
discrim_hir_id: discrim.hir_id,
|
||||
}),
|
||||
};
|
||||
let cause = self.cause(span, code);
|
||||
coercion.coerce(self, &cause, &arm.body, arm_ty);
|
||||
other_arms.push(arm_span);
|
||||
if other_arms.len() > 5 {
|
||||
other_arms.remove(0);
|
||||
}
|
||||
}
|
||||
prior_arm_ty = Some(arm_ty);
|
||||
}
|
||||
|
||||
// We won't diverge unless the discriminant or all arms diverge.
|
||||
self.diverges.set(discrim_diverges | all_arms_diverge);
|
||||
|
||||
coercion.complete(self)
|
||||
}
|
||||
|
||||
/// When the previously checked expression (the scrutinee) diverges,
|
||||
/// warn the user about the match arms being unreachable.
|
||||
fn warn_arms_when_scrutinee_diverges(&self, arms: &'gcx [hir::Arm], source_if: bool) {
|
||||
if self.diverges.get().always() {
|
||||
let msg = if source_if { "block in `if` expression" } else { "arm" };
|
||||
for arm in arms {
|
||||
self.warn_if_unreachable(arm.body.hir_id, arm.body.span, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle the fallback arm of a desugared if(-let) like a missing else.
|
||||
fn if_fallback_coercion(
|
||||
&self,
|
||||
span: Span,
|
||||
then_expr: &'gcx hir::Expr,
|
||||
coercion: &mut CoerceMany<'gcx, 'tcx, '_, rustc::hir::Arm>,
|
||||
) {
|
||||
// If this `if` expr is the parent's function return expr,
|
||||
// the cause of the type coercion is the return type, point at it. (#25228)
|
||||
let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span);
|
||||
let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse);
|
||||
coercion.coerce_forced_unit(self, &cause, &mut |err| {
|
||||
if let Some((span, msg)) = &ret_reason {
|
||||
err.span_label(*span, msg.as_str());
|
||||
} else if let ExprKind::Block(block, _) = &then_expr.node {
|
||||
if let Some(expr) = &block.expr {
|
||||
err.span_label(expr.span, "found here".to_string());
|
||||
}
|
||||
}
|
||||
err.note("`if` expressions without `else` evaluate to `()`");
|
||||
err.help("consider adding an `else` block that evaluates to the expected type");
|
||||
}, ret_reason.is_none());
|
||||
}
|
||||
|
||||
fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, span: Span) -> Option<(Span, String)> {
|
||||
use hir::Node::{Block, Item, Local};
|
||||
|
||||
let node = self.tcx.hir().get_by_hir_id(self.tcx.hir().get_parent_node_by_hir_id(
|
||||
self.tcx.hir().get_parent_node_by_hir_id(hir_id),
|
||||
));
|
||||
if let Block(block) = node {
|
||||
// check that the body's parent is an fn
|
||||
let parent = self.tcx.hir().get_by_hir_id(
|
||||
self.tcx.hir().get_parent_node_by_hir_id(
|
||||
self.tcx.hir().get_parent_node_by_hir_id(block.hir_id),
|
||||
),
|
||||
);
|
||||
if let (Some(expr), Item(hir::Item {
|
||||
node: hir::ItemKind::Fn(..), ..
|
||||
})) = (&block.expr, parent) {
|
||||
// check that the `if` expr without `else` is the fn body's expr
|
||||
if expr.span == span {
|
||||
return self.get_fn_decl(hir_id).map(|(fn_decl, _)| (
|
||||
fn_decl.output.span(),
|
||||
format!("expected `{}` because of this return type", fn_decl.output),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Local(hir::Local { ty: Some(_), pat, .. }) = node {
|
||||
return Some((pat.span, "expected because of this assignment".to_string()));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn if_cause(
|
||||
&self,
|
||||
span: Span,
|
||||
then_expr: &'gcx hir::Expr,
|
||||
else_expr: &'gcx hir::Expr,
|
||||
then_ty: Ty<'tcx>,
|
||||
else_ty: Ty<'tcx>,
|
||||
) -> ObligationCause<'tcx> {
|
||||
let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) {
|
||||
// The `if`/`else` isn't in one line in the output, include some context to make it
|
||||
// clear it is an if/else expression:
|
||||
// ```
|
||||
// LL | let x = if true {
|
||||
// | _____________-
|
||||
// LL || 10i32
|
||||
// || ----- expected because of this
|
||||
// LL || } else {
|
||||
// LL || 10u32
|
||||
// || ^^^^^ expected i32, found u32
|
||||
// LL || };
|
||||
// ||_____- if and else have incompatible types
|
||||
// ```
|
||||
Some(span)
|
||||
} else {
|
||||
// The entire expression is in one line, only point at the arms
|
||||
// ```
|
||||
// LL | let x = if true { 10i32 } else { 10u32 };
|
||||
// | ----- ^^^^^ expected i32, found u32
|
||||
// | |
|
||||
// | expected because of this
|
||||
// ```
|
||||
None
|
||||
};
|
||||
|
||||
let mut remove_semicolon = None;
|
||||
let error_sp = if let ExprKind::Block(block, _) = &else_expr.node {
|
||||
if let Some(expr) = &block.expr {
|
||||
expr.span
|
||||
} else if let Some(stmt) = block.stmts.last() {
|
||||
// possibly incorrect trailing `;` in the else arm
|
||||
remove_semicolon = self.could_remove_semicolon(block, then_ty);
|
||||
stmt.span
|
||||
} else { // empty block, point at its entirety
|
||||
// Avoid overlapping spans that aren't as readable:
|
||||
// ```
|
||||
// 2 | let x = if true {
|
||||
// | _____________-
|
||||
// 3 | | 3
|
||||
// | | - expected because of this
|
||||
// 4 | | } else {
|
||||
// | |____________^
|
||||
// 5 | ||
|
||||
// 6 | || };
|
||||
// | || ^
|
||||
// | ||_____|
|
||||
// | |______if and else have incompatible types
|
||||
// | expected integer, found ()
|
||||
// ```
|
||||
// by not pointing at the entire expression:
|
||||
// ```
|
||||
// 2 | let x = if true {
|
||||
// | ------- if and else have incompatible types
|
||||
// 3 | 3
|
||||
// | - expected because of this
|
||||
// 4 | } else {
|
||||
// | ____________^
|
||||
// 5 | |
|
||||
// 6 | | };
|
||||
// | |_____^ expected integer, found ()
|
||||
// ```
|
||||
if outer_sp.is_some() {
|
||||
outer_sp = Some(self.tcx.sess.source_map().def_span(span));
|
||||
}
|
||||
else_expr.span
|
||||
}
|
||||
} else { // shouldn't happen unless the parser has done something weird
|
||||
else_expr.span
|
||||
};
|
||||
|
||||
// Compute `Span` of `then` part of `if`-expression:
|
||||
let then_sp = if let ExprKind::Block(block, _) = &then_expr.node {
|
||||
if let Some(expr) = &block.expr {
|
||||
expr.span
|
||||
} else if let Some(stmt) = block.stmts.last() {
|
||||
// possibly incorrect trailing `;` in the else arm
|
||||
remove_semicolon = remove_semicolon.or(self.could_remove_semicolon(block, else_ty));
|
||||
stmt.span
|
||||
} else { // empty block, point at its entirety
|
||||
outer_sp = None; // same as in `error_sp`, cleanup output
|
||||
then_expr.span
|
||||
}
|
||||
} else { // shouldn't happen unless the parser has done something weird
|
||||
then_expr.span
|
||||
};
|
||||
|
||||
// Finally construct the cause:
|
||||
self.cause(error_sp, ObligationCauseCode::IfExpression {
|
||||
then: then_sp,
|
||||
outer: outer_sp,
|
||||
semicolon: remove_semicolon,
|
||||
})
|
||||
}
|
||||
|
||||
fn demand_discriminant_type(
|
||||
&self,
|
||||
arms: &'gcx [hir::Arm],
|
||||
discrim: &'gcx hir::Expr,
|
||||
) -> Ty<'tcx> {
|
||||
// Not entirely obvious: if matches may create ref bindings, we want to
|
||||
// use the *precise* type of the discriminant, *not* some supertype, as
|
||||
// the "discriminant type" (issue #23116).
|
||||
@ -653,143 +973,17 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
|
||||
hir::MutMutable => 1,
|
||||
hir::MutImmutable => 0,
|
||||
});
|
||||
let discrim_ty;
|
||||
|
||||
if let Some(m) = contains_ref_bindings {
|
||||
discrim_ty = self.check_expr_with_needs(discrim, Needs::maybe_mut_place(m));
|
||||
self.check_expr_with_needs(discrim, Needs::maybe_mut_place(m))
|
||||
} else {
|
||||
// ...but otherwise we want to use any supertype of the
|
||||
// discriminant. This is sort of a workaround, see note (*) in
|
||||
// `check_pat` for some details.
|
||||
discrim_ty = self.next_ty_var(TypeVariableOrigin::TypeInference(discrim.span));
|
||||
let discrim_ty = self.next_ty_var(TypeVariableOrigin::TypeInference(discrim.span));
|
||||
self.check_expr_has_type_or_error(discrim, discrim_ty);
|
||||
};
|
||||
|
||||
// If there are no arms, that is a diverging match; a special case.
|
||||
if arms.is_empty() {
|
||||
self.diverges.set(self.diverges.get() | Diverges::Always);
|
||||
return tcx.types.never;
|
||||
discrim_ty
|
||||
}
|
||||
|
||||
if self.diverges.get().always() {
|
||||
for arm in arms {
|
||||
self.warn_if_unreachable(arm.body.hir_id, arm.body.span, "arm");
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, we have to union together the types that the
|
||||
// arms produce and so forth.
|
||||
let discrim_diverges = self.diverges.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
// rust-lang/rust#55810: Typecheck patterns first (via eager
|
||||
// collection into `Vec`), so we get types for all bindings.
|
||||
let all_arm_pats_diverge: Vec<_> = arms.iter().map(|arm| {
|
||||
let mut all_pats_diverge = Diverges::WarnedAlways;
|
||||
for p in &arm.pats {
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
self.check_pat_walk(
|
||||
&p,
|
||||
discrim_ty,
|
||||
ty::BindingMode::BindByValue(hir::Mutability::MutImmutable),
|
||||
Some(discrim.span),
|
||||
);
|
||||
all_pats_diverge &= self.diverges.get();
|
||||
}
|
||||
|
||||
// As discussed with @eddyb, this is for disabling unreachable_code
|
||||
// warnings on patterns (they're now subsumed by unreachable_patterns
|
||||
// warnings).
|
||||
match all_pats_diverge {
|
||||
Diverges::Maybe => Diverges::Maybe,
|
||||
Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
// Now typecheck the blocks.
|
||||
//
|
||||
// The result of the match is the common supertype of all the
|
||||
// arms. Start out the value as bottom, since it's the, well,
|
||||
// bottom the type lattice, and we'll be moving up the lattice as
|
||||
// we process each arm. (Note that any match with 0 arms is matching
|
||||
// on any empty type and is therefore unreachable; should the flow
|
||||
// of execution reach it, we will panic, so bottom is an appropriate
|
||||
// type in that case)
|
||||
let mut all_arms_diverge = Diverges::WarnedAlways;
|
||||
|
||||
let expected = expected.adjust_for_branches(self);
|
||||
|
||||
let mut coercion = {
|
||||
let coerce_first = match expected {
|
||||
// We don't coerce to `()` so that if the match expression is a
|
||||
// statement it's branches can have any consistent type. That allows
|
||||
// us to give better error messages (pointing to a usually better
|
||||
// arm for inconsistent arms or to the whole match when a `()` type
|
||||
// is required).
|
||||
Expectation::ExpectHasType(ety) if ety != self.tcx.mk_unit() => ety,
|
||||
_ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)),
|
||||
};
|
||||
CoerceMany::with_coercion_sites(coerce_first, arms)
|
||||
};
|
||||
|
||||
let mut other_arms = vec![]; // used only for diagnostics
|
||||
let mut prior_arm_ty = None;
|
||||
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
|
||||
if let Some(ref g) = arm.guard {
|
||||
self.diverges.set(pats_diverge);
|
||||
match g {
|
||||
hir::Guard::If(e) => self.check_expr_has_type_or_error(e, tcx.types.bool),
|
||||
};
|
||||
}
|
||||
|
||||
self.diverges.set(pats_diverge);
|
||||
let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
|
||||
all_arms_diverge &= self.diverges.get();
|
||||
|
||||
// Handle the fallback arm of a desugared if-let like a missing else.
|
||||
let is_if_let_fallback = match match_src {
|
||||
hir::MatchSource::IfLetDesugar { contains_else_clause: false } => {
|
||||
i == arms.len() - 1 && arm_ty.is_unit()
|
||||
}
|
||||
_ => false
|
||||
};
|
||||
|
||||
let arm_span = if let hir::ExprKind::Block(ref blk, _) = arm.body.node {
|
||||
// Point at the block expr instead of the entire block
|
||||
blk.expr.as_ref().map(|e| e.span).unwrap_or(arm.body.span)
|
||||
} else {
|
||||
arm.body.span
|
||||
};
|
||||
if is_if_let_fallback {
|
||||
let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse);
|
||||
assert!(arm_ty.is_unit());
|
||||
coercion.coerce_forced_unit(self, &cause, &mut |_| (), true);
|
||||
} else {
|
||||
let cause = if i == 0 {
|
||||
// The reason for the first arm to fail is not that the match arms diverge,
|
||||
// but rather that there's a prior obligation that doesn't hold.
|
||||
self.cause(arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id))
|
||||
} else {
|
||||
self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
|
||||
arm_span,
|
||||
source: match_src,
|
||||
prior_arms: other_arms.clone(),
|
||||
last_ty: prior_arm_ty.unwrap(),
|
||||
discrim_hir_id: discrim.hir_id,
|
||||
})
|
||||
};
|
||||
coercion.coerce(self, &cause, &arm.body, arm_ty);
|
||||
}
|
||||
other_arms.push(arm_span);
|
||||
if other_arms.len() > 5 {
|
||||
other_arms.remove(0);
|
||||
}
|
||||
prior_arm_ty = Some(arm_ty);
|
||||
}
|
||||
|
||||
// We won't diverge unless the discriminant or all arms diverge.
|
||||
self.diverges.set(discrim_diverges | all_arms_diverge);
|
||||
|
||||
coercion.complete(self)
|
||||
}
|
||||
|
||||
fn check_pat_struct(
|
||||
@ -800,7 +994,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
|
||||
etc: bool,
|
||||
expected: Ty<'tcx>,
|
||||
def_bm: ty::BindingMode,
|
||||
match_discrim_span: Option<Span>,
|
||||
discrim_span: Option<Span>,
|
||||
) -> Ty<'tcx>
|
||||
{
|
||||
// Resolve the path and check the definition for errors.
|
||||
@ -809,18 +1003,13 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
|
||||
variant_ty
|
||||
} else {
|
||||
for field in fields {
|
||||
self.check_pat_walk(
|
||||
&field.node.pat,
|
||||
self.tcx.types.err,
|
||||
def_bm,
|
||||
match_discrim_span,
|
||||
);
|
||||
self.check_pat_walk(&field.node.pat, self.tcx.types.err, def_bm, discrim_span);
|
||||
}
|
||||
return self.tcx.types.err;
|
||||
};
|
||||
|
||||
// Type-check the path.
|
||||
self.demand_eqtype_pat(pat.span, expected, pat_ty, match_discrim_span);
|
||||
self.demand_eqtype_pat(pat.span, expected, pat_ty, discrim_span);
|
||||
|
||||
// Type-check subpatterns.
|
||||
if self.check_struct_pat_fields(pat_ty, pat.hir_id, pat.span, variant, fields, etc, def_bm)
|
||||
|
@ -1248,12 +1248,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
|
||||
augment_error(&mut db);
|
||||
}
|
||||
|
||||
if expression.filter(|e| fcx.is_assign_to_bool(e, expected)).is_some() {
|
||||
// Error reported in `check_assign` so avoid emitting error again.
|
||||
db.delay_as_bug();
|
||||
} else {
|
||||
db.emit();
|
||||
}
|
||||
// Error possibly reported in `check_assign` so avoid emitting error again.
|
||||
db.emit_unless(expression.filter(|e| fcx.is_assign_to_bool(e, expected)).is_some());
|
||||
|
||||
self.final_ty = Some(fcx.tcx.types.err);
|
||||
}
|
||||
|
@ -117,6 +117,7 @@ use rustc::ty::subst::{UnpackedKind, Subst, InternalSubsts, SubstsRef, UserSelfT
|
||||
use rustc::ty::util::{Representability, IntTypeExt, Discr};
|
||||
use rustc::ty::layout::VariantIdx;
|
||||
use syntax_pos::{self, BytePos, Span, MultiSpan};
|
||||
use syntax_pos::hygiene::CompilerDesugaringKind;
|
||||
use syntax::ast;
|
||||
use syntax::attr;
|
||||
use syntax::feature_gate::{GateIssue, emit_feature_err};
|
||||
@ -1086,12 +1087,8 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
|
||||
// Add formal parameters.
|
||||
for (arg_ty, arg) in fn_sig.inputs().iter().zip(&body.arguments) {
|
||||
// Check the pattern.
|
||||
fcx.check_pat_walk(
|
||||
&arg.pat,
|
||||
arg_ty,
|
||||
ty::BindingMode::BindByValue(hir::Mutability::MutImmutable),
|
||||
None,
|
||||
);
|
||||
let binding_mode = ty::BindingMode::BindByValue(hir::Mutability::MutImmutable);
|
||||
fcx.check_pat_walk(&arg.pat, arg_ty, binding_mode, None);
|
||||
|
||||
// Check that argument is Sized.
|
||||
// The check for a non-trivial pattern is a hack to avoid duplicate warnings
|
||||
@ -2045,15 +2042,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
/// Produces warning on the given node, if the current point in the
|
||||
/// function is unreachable, and there hasn't been another warning.
|
||||
fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
|
||||
if self.diverges.get() == Diverges::Always {
|
||||
if self.diverges.get() == Diverges::Always &&
|
||||
// If span arose from a desugaring of `if` then it is the condition itself,
|
||||
// which diverges, that we are about to lint on. This gives suboptimal diagnostics
|
||||
// and so we stop here and allow the block of the `if`-expression to be linted instead.
|
||||
!span.is_compiler_desugaring(CompilerDesugaringKind::IfTemporary) {
|
||||
self.diverges.set(Diverges::WarnedAlways);
|
||||
|
||||
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
|
||||
|
||||
self.tcx().lint_hir(
|
||||
lint::builtin::UNREACHABLE_CODE,
|
||||
id, span,
|
||||
&format!("unreachable {}", kind));
|
||||
let msg = format!("unreachable {}", kind);
|
||||
self.tcx().lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3161,13 +3160,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
|
||||
if self.is_assign_to_bool(expr, expected_ty) {
|
||||
// Error reported in `check_assign` so avoid emitting error again.
|
||||
// FIXME(centril): Consider removing if/when `if` desugars to `match`.
|
||||
err.delay_as_bug();
|
||||
} else {
|
||||
err.emit();
|
||||
}
|
||||
let expr = match &expr.node {
|
||||
ExprKind::DropTemps(expr) => expr,
|
||||
_ => expr,
|
||||
};
|
||||
// Error possibly reported in `check_assign` so avoid emitting error again.
|
||||
err.emit_unless(self.is_assign_to_bool(expr, expected_ty));
|
||||
}
|
||||
ty
|
||||
}
|
||||
@ -3330,194 +3328,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
return_expr_ty);
|
||||
}
|
||||
|
||||
// A generic function for checking the 'then' and 'else' clauses in an 'if'
|
||||
// or 'if-else' expression.
|
||||
fn check_then_else(&self,
|
||||
cond_expr: &'gcx hir::Expr,
|
||||
then_expr: &'gcx hir::Expr,
|
||||
opt_else_expr: Option<&'gcx hir::Expr>,
|
||||
sp: Span,
|
||||
expected: Expectation<'tcx>) -> Ty<'tcx> {
|
||||
let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool);
|
||||
let cond_diverges = self.diverges.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
let expected = expected.adjust_for_branches(self);
|
||||
let then_ty = self.check_expr_with_expectation(then_expr, expected);
|
||||
let then_diverges = self.diverges.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
// We've already taken the expected type's preferences
|
||||
// into account when typing the `then` branch. To figure
|
||||
// out the initial shot at a LUB, we thus only consider
|
||||
// `expected` if it represents a *hard* constraint
|
||||
// (`only_has_type`); otherwise, we just go with a
|
||||
// fresh type variable.
|
||||
let coerce_to_ty = expected.coercion_target_type(self, sp);
|
||||
let mut coerce: DynamicCoerceMany<'_, '_> = CoerceMany::new(coerce_to_ty);
|
||||
|
||||
coerce.coerce(self, &self.misc(sp), then_expr, then_ty);
|
||||
|
||||
if let Some(else_expr) = opt_else_expr {
|
||||
let else_ty = self.check_expr_with_expectation(else_expr, expected);
|
||||
let else_diverges = self.diverges.get();
|
||||
|
||||
let mut outer_sp = if self.tcx.sess.source_map().is_multiline(sp) {
|
||||
// The `if`/`else` isn't in one line in the output, include some context to make it
|
||||
// clear it is an if/else expression:
|
||||
// ```
|
||||
// LL | let x = if true {
|
||||
// | _____________-
|
||||
// LL || 10i32
|
||||
// || ----- expected because of this
|
||||
// LL || } else {
|
||||
// LL || 10u32
|
||||
// || ^^^^^ expected i32, found u32
|
||||
// LL || };
|
||||
// ||_____- if and else have incompatible types
|
||||
// ```
|
||||
Some(sp)
|
||||
} else {
|
||||
// The entire expression is in one line, only point at the arms
|
||||
// ```
|
||||
// LL | let x = if true { 10i32 } else { 10u32 };
|
||||
// | ----- ^^^^^ expected i32, found u32
|
||||
// | |
|
||||
// | expected because of this
|
||||
// ```
|
||||
None
|
||||
};
|
||||
let mut remove_semicolon = None;
|
||||
let error_sp = if let ExprKind::Block(block, _) = &else_expr.node {
|
||||
if let Some(expr) = &block.expr {
|
||||
expr.span
|
||||
} else if let Some(stmt) = block.stmts.last() {
|
||||
// possibly incorrect trailing `;` in the else arm
|
||||
remove_semicolon = self.could_remove_semicolon(block, then_ty);
|
||||
stmt.span
|
||||
} else { // empty block, point at its entirety
|
||||
// Avoid overlapping spans that aren't as readable:
|
||||
// ```
|
||||
// 2 | let x = if true {
|
||||
// | _____________-
|
||||
// 3 | | 3
|
||||
// | | - expected because of this
|
||||
// 4 | | } else {
|
||||
// | |____________^
|
||||
// 5 | ||
|
||||
// 6 | || };
|
||||
// | || ^
|
||||
// | ||_____|
|
||||
// | |______if and else have incompatible types
|
||||
// | expected integer, found ()
|
||||
// ```
|
||||
// by not pointing at the entire expression:
|
||||
// ```
|
||||
// 2 | let x = if true {
|
||||
// | ------- if and else have incompatible types
|
||||
// 3 | 3
|
||||
// | - expected because of this
|
||||
// 4 | } else {
|
||||
// | ____________^
|
||||
// 5 | |
|
||||
// 6 | | };
|
||||
// | |_____^ expected integer, found ()
|
||||
// ```
|
||||
if outer_sp.is_some() {
|
||||
outer_sp = Some(self.tcx.sess.source_map().def_span(sp));
|
||||
}
|
||||
else_expr.span
|
||||
}
|
||||
} else { // shouldn't happen unless the parser has done something weird
|
||||
else_expr.span
|
||||
};
|
||||
let then_sp = if let ExprKind::Block(block, _) = &then_expr.node {
|
||||
if let Some(expr) = &block.expr {
|
||||
expr.span
|
||||
} else if let Some(stmt) = block.stmts.last() {
|
||||
// possibly incorrect trailing `;` in the else arm
|
||||
remove_semicolon = remove_semicolon.or(
|
||||
self.could_remove_semicolon(block, else_ty));
|
||||
stmt.span
|
||||
} else { // empty block, point at its entirety
|
||||
outer_sp = None; // same as in `error_sp`, cleanup output
|
||||
then_expr.span
|
||||
}
|
||||
} else { // shouldn't happen unless the parser has done something weird
|
||||
then_expr.span
|
||||
};
|
||||
|
||||
let if_cause = self.cause(error_sp, ObligationCauseCode::IfExpression {
|
||||
then: then_sp,
|
||||
outer: outer_sp,
|
||||
semicolon: remove_semicolon,
|
||||
});
|
||||
|
||||
coerce.coerce(self, &if_cause, else_expr, else_ty);
|
||||
|
||||
// We won't diverge unless both branches do (or the condition does).
|
||||
self.diverges.set(cond_diverges | then_diverges & else_diverges);
|
||||
} else {
|
||||
// If this `if` expr is the parent's function return expr, the cause of the type
|
||||
// coercion is the return type, point at it. (#25228)
|
||||
let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, sp);
|
||||
|
||||
let else_cause = self.cause(sp, ObligationCauseCode::IfExpressionWithNoElse);
|
||||
coerce.coerce_forced_unit(self, &else_cause, &mut |err| {
|
||||
if let Some((sp, msg)) = &ret_reason {
|
||||
err.span_label(*sp, msg.as_str());
|
||||
} else if let ExprKind::Block(block, _) = &then_expr.node {
|
||||
if let Some(expr) = &block.expr {
|
||||
err.span_label(expr.span, "found here".to_string());
|
||||
}
|
||||
}
|
||||
err.note("`if` expressions without `else` evaluate to `()`");
|
||||
err.help("consider adding an `else` block that evaluates to the expected type");
|
||||
}, ret_reason.is_none());
|
||||
|
||||
// If the condition is false we can't diverge.
|
||||
self.diverges.set(cond_diverges);
|
||||
}
|
||||
|
||||
let result_ty = coerce.complete(self);
|
||||
if cond_ty.references_error() {
|
||||
self.tcx.types.err
|
||||
} else {
|
||||
result_ty
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, sp: Span) -> Option<(Span, String)> {
|
||||
let node = self.tcx.hir().get_by_hir_id(self.tcx.hir().get_parent_node_by_hir_id(
|
||||
self.tcx.hir().get_parent_node_by_hir_id(hir_id),
|
||||
));
|
||||
if let Node::Block(block) = node {
|
||||
// check that the body's parent is an fn
|
||||
let parent = self.tcx.hir().get_by_hir_id(
|
||||
self.tcx.hir().get_parent_node_by_hir_id(
|
||||
self.tcx.hir().get_parent_node_by_hir_id(block.hir_id),
|
||||
),
|
||||
);
|
||||
if let (Some(expr), Node::Item(hir::Item {
|
||||
node: hir::ItemKind::Fn(..), ..
|
||||
})) = (&block.expr, parent) {
|
||||
// check that the `if` expr without `else` is the fn body's expr
|
||||
if expr.span == sp {
|
||||
return self.get_fn_decl(hir_id).map(|(fn_decl, _)| (
|
||||
fn_decl.output.span(),
|
||||
format!("expected `{}` because of this return type", fn_decl.output),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Node::Local(hir::Local {
|
||||
ty: Some(_), pat, ..
|
||||
}) = node {
|
||||
return Some((pat.span, "expected because of this assignment".to_string()));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// Check field access expressions
|
||||
fn check_field(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
@ -4062,7 +3872,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
match expr.node {
|
||||
ExprKind::Block(..) |
|
||||
ExprKind::Loop(..) | ExprKind::While(..) |
|
||||
ExprKind::If(..) | ExprKind::Match(..) => {}
|
||||
ExprKind::Match(..) => {}
|
||||
|
||||
_ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression")
|
||||
}
|
||||
@ -4444,10 +4254,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
ExprKind::Assign(ref lhs, ref rhs) => {
|
||||
self.check_assign(expr, expected, lhs, rhs)
|
||||
}
|
||||
ExprKind::If(ref cond, ref then_expr, ref opt_else_expr) => {
|
||||
self.check_then_else(&cond, then_expr, opt_else_expr.as_ref().map(|e| &**e),
|
||||
expr.span, expected)
|
||||
}
|
||||
ExprKind::While(ref cond, ref body, _) => {
|
||||
let ctxt = BreakableCtxt {
|
||||
// cannot use break with a value from a while loop
|
||||
@ -5261,7 +5067,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
match expression.node {
|
||||
ExprKind::Call(..) |
|
||||
ExprKind::MethodCall(..) |
|
||||
ExprKind::If(..) |
|
||||
ExprKind::While(..) |
|
||||
ExprKind::Loop(..) |
|
||||
ExprKind::Match(..) |
|
||||
|
@ -591,6 +591,10 @@ impl ExpnFormat {
|
||||
/// The kind of compiler desugaring.
|
||||
#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
|
||||
pub enum CompilerDesugaringKind {
|
||||
/// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`.
|
||||
/// However, we do not want to blame `c` for unreachability but rather say that `i`
|
||||
/// is unreachable. This desugaring kind allows us to avoid blaming `c`.
|
||||
IfTemporary,
|
||||
QuestionMark,
|
||||
TryBlock,
|
||||
/// Desugaring of an `impl Trait` in return type position
|
||||
@ -605,6 +609,7 @@ pub enum CompilerDesugaringKind {
|
||||
impl CompilerDesugaringKind {
|
||||
pub fn name(self) -> Symbol {
|
||||
Symbol::intern(match self {
|
||||
CompilerDesugaringKind::IfTemporary => "if",
|
||||
CompilerDesugaringKind::Async => "async",
|
||||
CompilerDesugaringKind::Await => "await",
|
||||
CompilerDesugaringKind::QuestionMark => "?",
|
||||
|
Loading…
Reference in New Issue
Block a user