Remove hir::ExprKind::If and replace it with lowering to hir::ExprKind::Match.

This commit is contained in:
Mazdak Farrokhzad 2019-03-11 16:43:27 +01:00
parent 0ac53da03d
commit 5cccda1770
23 changed files with 461 additions and 660 deletions

View File

@ -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]

View File

@ -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);

View File

@ -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)
};

View File

@ -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,

View File

@ -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(..) |

View File

@ -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,

View File

@ -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;

View File

@ -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(..) |

View File

@ -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(..) |

View File

@ -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);
}

View File

@ -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>) {

View File

@ -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 { .. }

View File

@ -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 { .. }

View File

@ -45,7 +45,6 @@ impl Category {
| ExprKind::ValueTypeAscription { .. } => Some(Category::Place),
ExprKind::LogicalOp { .. }
| ExprKind::If { .. }
| ExprKind::Match { .. }
| ExprKind::NeverToAny { .. }
| ExprKind::Use { .. }

View File

@ -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:
//

View File

@ -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()),

View File

@ -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>,

View File

@ -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,

View File

@ -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);

View File

@ -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)

View File

@ -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);
}

View File

@ -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(..) |

View File

@ -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 => "?",