From 6b3cc0b8c8094407a3b5ea75f946c682d6d0142a Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Wed, 26 Oct 2016 02:28:20 +0300 Subject: [PATCH] rustc_typeck: correctly track "always-diverges" and "has-type-errors". --- src/libpanic_abort/lib.rs | 2 +- src/librustc_driver/lib.rs | 2 - src/librustc_typeck/check/_match.rs | 26 +- src/librustc_typeck/check/mod.rs | 457 +++++++++++------- src/librustc_typeck/check/op.rs | 5 + src/test/compile-fail/E0138.rs | 4 +- .../consider-removing-last-semi.rs | 4 +- .../compile-fail/diverging-fn-tail-35849.rs | 4 +- src/test/compile-fail/issue-11714.rs | 2 +- src/test/compile-fail/issue-13428.rs | 4 +- src/test/compile-fail/issue-22645.rs | 2 +- src/test/compile-fail/issue-22684.rs | 2 +- src/test/compile-fail/issue-29161.rs | 2 +- src/test/compile-fail/issue-32323.rs | 2 +- src/test/compile-fail/issue-5239-1.rs | 2 +- src/test/compile-fail/issue-6458-4.rs | 7 +- src/test/compile-fail/liveness-forgot-ret.rs | 3 +- src/test/compile-fail/liveness-issue-2163.rs | 2 +- .../compile-fail/liveness-missing-ret2.rs | 4 +- .../liveness-return-last-stmt-semi.rs | 8 +- src/test/compile-fail/main-wrong-type-2.rs | 1 + .../compile-fail/on-unimplemented/on-trait.rs | 2 +- .../compile-fail/private-in-public-lint.rs | 4 +- src/test/compile-fail/required-lang-item.rs | 1 + src/test/compile-fail/unreachable-in-call.rs | 2 +- .../compile-fail/where-clauses-unsatisfied.rs | 3 +- 26 files changed, 345 insertions(+), 212 deletions(-) diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index b87160dd75d04..853f81ceaa9b8 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -53,7 +53,7 @@ pub unsafe extern fn __rust_maybe_catch_panic(f: fn(*mut u8), // now hopefully. #[no_mangle] pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 { - return abort(); + abort(); #[cfg(unix)] unsafe fn abort() -> ! { diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 6551bad3bc92e..7e60c40220f84 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -455,8 +455,6 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { 1 => panic!("make_input should have provided valid inputs"), _ => early_error(sopts.error_format, "multiple input filenames provided"), } - - None } fn late_callback(&mut self, diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 15b29573ac4e8..ca630624cdb38 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -13,7 +13,7 @@ use rustc::hir::def::{Def, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; use rustc::infer::{self, InferOk, TypeOrigin}; use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference}; -use check::{FnCtxt, Expectation}; +use check::{FnCtxt, Expectation, Diverges}; use util::nodemap::FxHashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -360,9 +360,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } true } -} -impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn check_match(&self, expr: &'gcx hir::Expr, discrim: &'gcx hir::Expr, @@ -390,14 +388,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { discrim_ty = self.next_ty_var(); self.check_expr_has_type(discrim, discrim_ty); }; + let discrim_diverges = self.diverges.get(); + self.diverges.set(Diverges::Maybe); // Typecheck the patterns first, so that we get types for all the // bindings. - for arm in arms { + 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(&p, discrim_ty); + all_pats_diverge &= self.diverges.get(); } - } + all_pats_diverge + }).collect(); // Now typecheck the blocks. // @@ -410,6 +414,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // type in that case) let expected = expected.adjust_for_branches(self); let mut result_ty = self.next_diverging_ty_var(); + let mut all_arms_diverge = Diverges::WarnedAlways; 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 @@ -422,11 +427,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => result_ty }; - for (i, arm) in arms.iter().enumerate() { + for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() { if let Some(ref e) = arm.guard { + self.diverges.set(pats_diverge); self.check_expr_has_type(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(); if result_ty.references_error() || arm_ty.references_error() { result_ty = tcx.types.err; @@ -476,11 +485,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; } + // We won't diverge unless the discriminant or all arms diverge. + self.diverges.set(discrim_diverges | all_arms_diverge); + result_ty } -} -impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn check_pat_struct(&self, pat: &'gcx hir::Pat, path: &hir::Path, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 725862022c59a..f6f585eb05f5f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -106,17 +106,18 @@ use util::common::{block_query, ErrorReported, indenter, loop_query}; use util::nodemap::{DefIdMap, FxHashMap, FxHashSet, NodeMap}; use std::cell::{Cell, Ref, RefCell}; +use std::cmp; use std::mem::replace; -use std::ops::Deref; +use std::ops::{self, Deref}; use syntax::abi::Abi; use syntax::ast; use syntax::attr; -use syntax::codemap::{self, Spanned}; +use syntax::codemap::{self, original_sp, Spanned}; use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::parse::token::{self, InternedString, keywords}; use syntax::ptr::P; use syntax::util::lev_distance::find_best_match_for_name; -use syntax_pos::{self, Span}; +use syntax_pos::{self, BytePos, Span}; use rustc::hir::intravisit::{self, Visitor}; use rustc::hir::{self, PatKind}; @@ -351,6 +352,59 @@ impl UnsafetyState { } } +/// Whether a node ever exits normally or not. +/// Tracked semi-automatically (through type variables +/// marked as diverging), with some manual adjustments +/// for control-flow primitives (approximating a CFG). +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +enum Diverges { + /// Potentially unknown, some cases converge, + /// others require a CFG to determine them. + Maybe, + + /// Definitely known to diverge and therefore + /// not reach the next sibling or its parent. + Always, + + /// Same as `Always` but with a reachability + /// warning already emitted + WarnedAlways +} + +// Convenience impls for combinig `Diverges`. + +impl ops::BitAnd for Diverges { + type Output = Self; + fn bitand(self, other: Self) -> Self { + cmp::min(self, other) + } +} + +impl ops::BitOr for Diverges { + type Output = Self; + fn bitor(self, other: Self) -> Self { + cmp::max(self, other) + } +} + +impl ops::BitAndAssign for Diverges { + fn bitand_assign(&mut self, other: Self) { + *self = *self & other; + } +} + +impl ops::BitOrAssign for Diverges { + fn bitor_assign(&mut self, other: Self) { + *self = *self | other; + } +} + +impl Diverges { + fn always(self) -> bool { + self >= Diverges::Always + } +} + #[derive(Clone)] pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { ast_ty_to_ty_cache: RefCell>>, @@ -371,6 +425,12 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { ps: RefCell, + /// Whether the last checked node can ever exit. + diverges: Cell, + + /// Whether any child nodes have any type errors. + has_errors: Cell, + inh: &'a Inherited<'a, 'gcx, 'tcx>, } @@ -1491,6 +1551,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ret_ty: rty, ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, ast::CRATE_NODE_ID)), + diverges: Cell::new(Diverges::Maybe), + has_errors: Cell::new(false), inh: inh, } } @@ -1507,6 +1569,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.tcx.sess.err_count() - self.err_count_on_creation } + /// Produce 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: ast::NodeId, span: Span, kind: &str) { + if self.diverges.get() == Diverges::Always { + self.diverges.set(Diverges::WarnedAlways); + + self.tcx.sess.add_lint(lint::builtin::UNREACHABLE_CODE, + id, span, + format!("unreachable {}", kind)); + } + } + /// Resolves type variables in `ty` if possible. Unlike the infcx /// version (resolve_type_vars_if_possible), this version will /// also select obligations if it seems useful, in an effort @@ -1577,6 +1651,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { debug!("write_ty({}, {:?}) in fcx {}", node_id, ty, self.tag()); self.tables.borrow_mut().node_types.insert(node_id, ty); + + if ty.references_error() { + self.has_errors.set(true); + } + + // FIXME(canndrew): This is_never should probably be an is_uninhabited + if ty.is_never() || self.type_var_diverges(ty) { + self.diverges.set(self.diverges.get() | Diverges::Always); + } } pub fn write_substs(&self, node_id: ast::NodeId, substs: ty::ItemSubsts<'tcx>) { @@ -2512,21 +2595,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Check the arguments. // We do this in a pretty awful way: first we typecheck any arguments - // that are not anonymous functions, then we typecheck the anonymous - // functions. This is so that we have more information about the types - // of arguments when we typecheck the functions. This isn't really the - // right way to do this. - let xs = [false, true]; - let mut any_diverges = false; // has any of the arguments diverged? - let mut warned = false; // have we already warned about unreachable code? - for check_blocks in &xs { - let check_blocks = *check_blocks; - debug!("check_blocks={}", check_blocks); + // that are not closures, then we typecheck the closures. This is so + // that we have more information about the types of arguments when we + // typecheck the functions. This isn't really the right way to do this. + for &check_closures in &[false, true] { + debug!("check_closures={}", check_closures); // More awful hacks: before we check argument types, try to do // an "opportunistic" vtable resolution of any trait bounds on // the call. This helps coercions. - if check_blocks { + if check_closures { self.select_obligations_where_possible(); } @@ -2541,61 +2619,43 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { supplied_arg_count }; for (i, arg) in args.iter().take(t).enumerate() { - if any_diverges && !warned { - self.tcx - .sess - .add_lint(lint::builtin::UNREACHABLE_CODE, - arg.id, - arg.span, - "unreachable expression".to_string()); - warned = true; + // Warn only for the first loop (the "no closures" one). + // Closure arguments themselves can't be diverging, but + // a previous argument can, e.g. `foo(panic!(), || {})`. + if !check_closures { + self.warn_if_unreachable(arg.id, arg.span, "expression"); } - let is_block = match arg.node { + + let is_closure = match arg.node { hir::ExprClosure(..) => true, _ => false }; - if is_block == check_blocks { - debug!("checking the argument"); - let formal_ty = formal_tys[i]; + if is_closure != check_closures { + continue; + } - // The special-cased logic below has three functions: - // 1. Provide as good of an expected type as possible. - let expected = expected_arg_tys.get(i).map(|&ty| { - Expectation::rvalue_hint(self, ty) - }); + debug!("checking the argument"); + let formal_ty = formal_tys[i]; - let checked_ty = self.check_expr_with_expectation(&arg, - expected.unwrap_or(ExpectHasType(formal_ty))); - // 2. Coerce to the most detailed type that could be coerced - // to, which is `expected_ty` if `rvalue_hint` returns an - // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. - let coerce_ty = expected.and_then(|e| e.only_has_type(self)); - self.demand_coerce(&arg, checked_ty, coerce_ty.unwrap_or(formal_ty)); - - // 3. Relate the expected type and the formal one, - // if the expected type was used for the coercion. - coerce_ty.map(|ty| self.demand_suptype(arg.span, formal_ty, ty)); - } + // The special-cased logic below has three functions: + // 1. Provide as good of an expected type as possible. + let expected = expected_arg_tys.get(i).map(|&ty| { + Expectation::rvalue_hint(self, ty) + }); - if let Some(&arg_ty) = self.tables.borrow().node_types.get(&arg.id) { - // FIXME(canndrew): This is_never should probably be an is_uninhabited - any_diverges = any_diverges || - self.type_var_diverges(arg_ty) || - arg_ty.is_never(); - } - } - if any_diverges && !warned { - let parent = self.tcx.map.get_parent_node(args[0].id); - self.tcx - .sess - .add_lint(lint::builtin::UNREACHABLE_CODE, - parent, - sp, - "unreachable call".to_string()); - warned = true; + let checked_ty = self.check_expr_with_expectation(&arg, + expected.unwrap_or(ExpectHasType(formal_ty))); + // 2. Coerce to the most detailed type that could be coerced + // to, which is `expected_ty` if `rvalue_hint` returns an + // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. + let coerce_ty = expected.and_then(|e| e.only_has_type(self)); + self.demand_coerce(&arg, checked_ty, coerce_ty.unwrap_or(formal_ty)); + + // 3. Relate the expected type and the formal one, + // if the expected type was used for the coercion. + coerce_ty.map(|ty| self.demand_suptype(arg.span, formal_ty, ty)); } - } // We also need to make sure we at least write the ty of the other @@ -2846,18 +2906,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { sp: Span, expected: Expectation<'tcx>) -> Ty<'tcx> { let cond_ty = self.check_expr_has_type(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_block_with_expected(then_blk, expected); + let then_diverges = self.diverges.get(); + self.diverges.set(Diverges::Maybe); let unit = self.tcx.mk_nil(); let (origin, expected, found, result) = if let Some(else_expr) = opt_else_expr { let else_ty = self.check_expr_with_expectation(else_expr, expected); - let origin = TypeOrigin::IfExpression(sp); + let else_diverges = self.diverges.get(); // Only try to coerce-unify if we have a then expression // to assign coercions to, otherwise it's () or diverging. + let origin = TypeOrigin::IfExpression(sp); let result = if let Some(ref then) = then_blk.expr { let res = self.try_find_coercion_lub(origin, || Some(&**then), then_ty, else_expr, else_ty); @@ -2883,8 +2948,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }) }) }; + + // We won't diverge unless both branches do (or the condition does). + self.diverges.set(cond_diverges | then_diverges & else_diverges); + (origin, then_ty, else_ty, result) } else { + // If the condition is false we can't diverge. + self.diverges.set(cond_diverges); + let origin = TypeOrigin::IfExpressionWithNoElse(sp); (origin, unit, then_ty, self.eq_types(true, origin, unit, then_ty) @@ -3346,10 +3418,36 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { lvalue_pref: LvaluePreference) -> Ty<'tcx> { debug!(">> typechecking: expr={:?} expected={:?}", expr, expected); + + // Warn for expressions after diverging siblings. + self.warn_if_unreachable(expr.id, expr.span, "expression"); + + // Hide the outer diverging and has_errors flags. + let old_diverges = self.diverges.get(); + let old_has_errors = self.has_errors.get(); + self.diverges.set(Diverges::Maybe); + self.has_errors.set(false); + let ty = self.check_expr_kind(expr, expected, lvalue_pref); + // Warn for non-block expressions with diverging children. + match expr.node { + hir::ExprBlock(_) | + hir::ExprLoop(..) | hir::ExprWhile(..) | + hir::ExprIf(..) | hir::ExprMatch(..) => {} + + _ => self.warn_if_unreachable(expr.id, expr.span, "expression") + } + + // Record the type, which applies it effects. + // We need to do this after the warning above, so that + // we don't warn for the diverging expression itself. self.write_ty(expr.id, ty); + // Combine the diverging and has_error flags. + self.diverges.set(self.diverges.get() | old_diverges); + self.has_errors.set(self.has_errors.get() | old_has_errors); + debug!("type of expr({}) {} is...", expr.id, pprust::expr_to_string(expr)); debug!("... {:?}, expected is {:?}", @@ -3574,22 +3672,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr.span, expected) } hir::ExprWhile(ref cond, ref body, _) => { - let cond_ty = self.check_expr_has_type(&cond, tcx.types.bool); + self.check_expr_has_type(&cond, tcx.types.bool); + let cond_diverging = self.diverges.get(); self.check_block_no_value(&body); - let body_ty = self.node_ty(body.id); - if cond_ty.references_error() || body_ty.references_error() { + + // We may never reach the body so it diverging means nothing. + self.diverges.set(cond_diverging); + + if self.has_errors.get() { tcx.types.err - } - else { + } else { tcx.mk_nil() } } hir::ExprLoop(ref body, _) => { self.check_block_no_value(&body); - if !may_break(tcx, expr.id, &body) { - tcx.types.never - } else { + if may_break(tcx, expr.id, &body) { + // No way to know whether it's diverging because + // of a `break` or an outer `break` or `return. + self.diverges.set(Diverges::Maybe); + tcx.mk_nil() + } else { + tcx.types.never } } hir::ExprMatch(ref discrim, ref arms, match_src) => { @@ -3922,55 +4027,66 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } pub fn check_stmt(&self, stmt: &'gcx hir::Stmt) { - let node_id; - let mut saw_bot = false; - let mut saw_err = false; + // Don't do all the complex logic below for DeclItem. match stmt.node { - hir::StmtDecl(ref decl, id) => { - node_id = id; - match decl.node { - hir::DeclLocal(ref l) => { - self.check_decl_local(&l); - let l_t = self.node_ty(l.id); - saw_bot = saw_bot || self.type_var_diverges(l_t); - saw_err = saw_err || l_t.references_error(); - } - hir::DeclItem(_) => {/* ignore for now */ } + hir::StmtDecl(ref decl, id) => { + match decl.node { + hir::DeclLocal(_) => {} + hir::DeclItem(_) => { + self.write_nil(id); + return; + } + } } - } - hir::StmtExpr(ref expr, id) => { - node_id = id; - // Check with expected type of () - let ty = self.check_expr_has_type(&expr, self.tcx.mk_nil()); - saw_bot = saw_bot || self.type_var_diverges(ty); - saw_err = saw_err || ty.references_error(); - } - hir::StmtSemi(ref expr, id) => { - node_id = id; - let ty = self.check_expr(&expr); - saw_bot |= self.type_var_diverges(ty); - saw_err |= ty.references_error(); - } + hir::StmtExpr(..) | hir::StmtSemi(..) => {} } - if saw_bot { - self.write_ty(node_id, self.next_diverging_ty_var()); - } - else if saw_err { + + self.warn_if_unreachable(stmt.node.id(), stmt.span, "statement"); + + // Hide the outer diverging and has_errors flags. + let old_diverges = self.diverges.get(); + let old_has_errors = self.has_errors.get(); + self.diverges.set(Diverges::Maybe); + self.has_errors.set(false); + + let node_id = match stmt.node { + hir::StmtDecl(ref decl, id) => { + match decl.node { + hir::DeclLocal(ref l) => { + self.check_decl_local(&l); + } + hir::DeclItem(_) => {/* ignore for now */ } + } + id + } + hir::StmtExpr(ref expr, id) => { + // Check with expected type of () + self.check_expr_has_type(&expr, self.tcx.mk_nil()); + id + } + hir::StmtSemi(ref expr, id) => { + self.check_expr(&expr); + id + } + }; + + if self.has_errors.get() { self.write_error(node_id); - } - else { + } else if self.diverges.get().always() { + self.write_ty(node_id, self.next_diverging_ty_var()); + } else { self.write_nil(node_id); } + + // Combine the diverging and has_error flags. + self.diverges.set(self.diverges.get() | old_diverges); + self.has_errors.set(self.has_errors.get() | old_has_errors); } pub fn check_block_no_value(&self, blk: &'gcx hir::Block) { - let blkty = self.check_block_with_expected(blk, ExpectHasType(self.tcx.mk_nil())); - if blkty.references_error() { - self.write_error(blk.id); - } else { - let nilty = self.tcx.mk_nil(); - self.demand_suptype(blk.span, nilty, blkty); - } + let unit = self.tcx.mk_nil(); + let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); + self.demand_suptype(blk.span, unit, ty); } fn check_block_with_expected(&self, @@ -3982,72 +4098,81 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { replace(&mut *fcx_ps, unsafety_state) }; - let mut warned = false; - let mut any_diverges = false; - let mut any_err = false; for s in &blk.stmts { self.check_stmt(s); - let s_id = s.node.id(); - let s_ty = self.node_ty(s_id); - if any_diverges && !warned && match s.node { - hir::StmtDecl(ref decl, _) => { - match decl.node { - hir::DeclLocal(_) => true, - _ => false, + } + + let mut ty = match blk.expr { + Some(ref e) => self.check_expr_with_expectation(e, expected), + None => self.tcx.mk_nil() + }; + + if self.diverges.get().always() { + if let ExpectHasType(ety) = expected { + // Avoid forcing a type (only `!` for now) in unreachable code. + // FIXME(aburka) do we need this special case? and should it be is_uninhabited? + if !ety.is_never() { + if let Some(ref e) = blk.expr { + // Coerce the tail expression to the right type. + self.demand_coerce(e, ty, ety); } } - hir::StmtExpr(..) | hir::StmtSemi(..) => true, - } { - self.tcx - .sess - .add_lint(lint::builtin::UNREACHABLE_CODE, - s_id, - s.span, - "unreachable statement".to_string()); - warned = true; } - // FIXME(canndrew): This is_never should probably be an is_uninhabited - any_diverges = any_diverges || - self.type_var_diverges(s_ty) || - s_ty.is_never(); - any_err = any_err || s_ty.references_error(); - } - let ty = match blk.expr { - None => if any_err { - self.tcx.types.err - } else if any_diverges { - self.next_diverging_ty_var() + + ty = self.next_diverging_ty_var(); + } else if let ExpectHasType(ety) = expected { + if let Some(ref e) = blk.expr { + // Coerce the tail expression to the right type. + self.demand_coerce(e, ty, ety); } else { - self.tcx.mk_nil() - }, - Some(ref e) => { - if any_diverges && !warned { - self.tcx - .sess - .add_lint(lint::builtin::UNREACHABLE_CODE, - e.id, - e.span, - "unreachable expression".to_string()); - } - let ety = match expected { - ExpectHasType(ety) => { - self.check_expr_coercable_to_type(&e, ety); - ety - } - _ => { - self.check_expr_with_expectation(&e, expected) - } - }; + // We're not diverging and there's an expected type, which, + // in case it's not `()`, could result in an error higher-up. + // We have a chance to error here early and be more helpful. + let origin = TypeOrigin::Misc(blk.span); + let trace = TypeTrace::types(origin, false, ty, ety); + match self.sub_types(false, origin, ty, ety) { + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + }, + Err(err) => { + let mut err = self.report_and_explain_type_error(trace, &err); + + // Be helpful when the user wrote `{... expr;}` and + // taking the `;` off is enough to fix the error. + let mut extra_semi = None; + if let Some(stmt) = blk.stmts.last() { + if let hir::StmtSemi(ref e, _) = stmt.node { + if self.can_sub_types(self.node_ty(e.id), ety).is_ok() { + extra_semi = Some(stmt); + } + } + } + if let Some(last_stmt) = extra_semi { + let original_span = original_sp(self.tcx.sess.codemap(), + last_stmt.span, blk.span); + let span_semi = Span { + lo: original_span.hi - BytePos(1), + hi: original_span.hi, + expn_id: original_span.expn_id + }; + err.span_help(span_semi, "consider removing this semicolon:"); + } - if any_err { - self.tcx.types.err - } else if any_diverges { - self.next_diverging_ty_var() - } else { - ety + err.emit(); + } } } - }; + + // We already applied the type (and potentially errored), + // use the expected type to avoid further errors out. + ty = ety; + } + + if self.has_errors.get() || ty.references_error() { + ty = self.tcx.types.err + } + self.write_ty(blk.id, ty); *self.ps.borrow_mut() = prev; diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 411bd7e7b5ca1..8b4975b7e3a2f 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -75,8 +75,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { match BinOpCategory::from(op) { BinOpCategory::Shortcircuit => { // && and || are a simple case. + let lhs_diverges = self.diverges.get(); self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty); self.check_expr_coercable_to_type(rhs_expr, tcx.mk_bool()); + + // Depending on the LHS' value, the RHS can never execute. + self.diverges.set(lhs_diverges); + tcx.mk_bool() } _ => { diff --git a/src/test/compile-fail/E0138.rs b/src/test/compile-fail/E0138.rs index d4630d7c2effb..11d90658ab26a 100644 --- a/src/test/compile-fail/E0138.rs +++ b/src/test/compile-fail/E0138.rs @@ -11,10 +11,10 @@ #![feature(start)] #[start] -fn foo(argc: isize, argv: *const *const u8) -> isize {} +fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } //~^ NOTE previous `start` function here #[start] -fn f(argc: isize, argv: *const *const u8) -> isize {} +fn f(argc: isize, argv: *const *const u8) -> isize { 0 } //~^ ERROR E0138 //~| NOTE multiple `start` functions diff --git a/src/test/compile-fail/consider-removing-last-semi.rs b/src/test/compile-fail/consider-removing-last-semi.rs index 2e110cb3d0bc8..530a0e4156228 100644 --- a/src/test/compile-fail/consider-removing-last-semi.rs +++ b/src/test/compile-fail/consider-removing-last-semi.rs @@ -8,12 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn f() -> String { //~ ERROR E0269 +fn f() -> String { //~ ERROR mismatched types 0u8; "bla".to_string(); //~ HELP consider removing this semicolon } -fn g() -> String { //~ ERROR E0269 +fn g() -> String { //~ ERROR mismatched types "this won't work".to_string(); "removeme".to_string(); //~ HELP consider removing this semicolon } diff --git a/src/test/compile-fail/diverging-fn-tail-35849.rs b/src/test/compile-fail/diverging-fn-tail-35849.rs index 6dc447b4dc887..3a27c08413328 100644 --- a/src/test/compile-fail/diverging-fn-tail-35849.rs +++ b/src/test/compile-fail/diverging-fn-tail-35849.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn _converge() -> ! { //~ ERROR computation may converge - 42 +fn _converge() -> ! { + 42 //~ ERROR mismatched types } fn main() { } diff --git a/src/test/compile-fail/issue-11714.rs b/src/test/compile-fail/issue-11714.rs index 998576097a0a0..192f78e41cb43 100644 --- a/src/test/compile-fail/issue-11714.rs +++ b/src/test/compile-fail/issue-11714.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn blah() -> i32 { //~ ERROR not all control paths return a value +fn blah() -> i32 { //~ ERROR mismatched types 1 ; //~ HELP consider removing this semicolon: diff --git a/src/test/compile-fail/issue-13428.rs b/src/test/compile-fail/issue-13428.rs index c771970650d31..9406199afc231 100644 --- a/src/test/compile-fail/issue-13428.rs +++ b/src/test/compile-fail/issue-13428.rs @@ -10,7 +10,7 @@ // Regression test for #13428 -fn foo() -> String { //~ ERROR not all control paths return a value +fn foo() -> String { //~ ERROR mismatched types format!("Hello {}", "world") // Put the trailing semicolon on its own line to test that the @@ -18,7 +18,7 @@ fn foo() -> String { //~ ERROR not all control paths return a value ; //~ HELP consider removing this semicolon } -fn bar() -> String { //~ ERROR not all control paths return a value +fn bar() -> String { //~ ERROR mismatched types "foobar".to_string() ; //~ HELP consider removing this semicolon } diff --git a/src/test/compile-fail/issue-22645.rs b/src/test/compile-fail/issue-22645.rs index 402b9a04496e9..81f66e3e2cfee 100644 --- a/src/test/compile-fail/issue-22645.rs +++ b/src/test/compile-fail/issue-22645.rs @@ -17,7 +17,7 @@ struct Bob; impl Add for Bob { type Output = Bob; - fn add(self, rhs : RHS) -> Bob {} + fn add(self, rhs : RHS) -> Bob { Bob } } fn main() { diff --git a/src/test/compile-fail/issue-22684.rs b/src/test/compile-fail/issue-22684.rs index b7ffbefba6a08..a791758ad1763 100644 --- a/src/test/compile-fail/issue-22684.rs +++ b/src/test/compile-fail/issue-22684.rs @@ -15,7 +15,7 @@ mod foo { } pub trait Baz { - fn bar(&self) -> bool {} + fn bar(&self) -> bool { true } } impl Baz for Foo {} } diff --git a/src/test/compile-fail/issue-29161.rs b/src/test/compile-fail/issue-29161.rs index bc09f61a754c2..97ba222fe45f0 100644 --- a/src/test/compile-fail/issue-29161.rs +++ b/src/test/compile-fail/issue-29161.rs @@ -13,7 +13,7 @@ mod a { impl Default for A { pub fn default() -> A { //~ ERROR unnecessary visibility qualifier - A; + A } } } diff --git a/src/test/compile-fail/issue-32323.rs b/src/test/compile-fail/issue-32323.rs index e3461e52e1c71..e5cb813032771 100644 --- a/src/test/compile-fail/issue-32323.rs +++ b/src/test/compile-fail/issue-32323.rs @@ -13,6 +13,6 @@ pub trait Tr<'a> { } pub fn f<'a, T: Tr<'a>>() -> >::Out {} -//~^ ERROR not all control paths return a value +//~^ ERROR mismatched types pub fn main() {} diff --git a/src/test/compile-fail/issue-5239-1.rs b/src/test/compile-fail/issue-5239-1.rs index 06e3c9a207b78..a77b27150d797 100644 --- a/src/test/compile-fail/issue-5239-1.rs +++ b/src/test/compile-fail/issue-5239-1.rs @@ -11,7 +11,7 @@ // Regression test for issue #5239 fn main() { - let x = |ref x: isize| -> isize { x += 1; }; + let x = |ref x: isize| { x += 1; }; //~^ ERROR E0368 //~| NOTE cannot use `+=` on type `&isize` } diff --git a/src/test/compile-fail/issue-6458-4.rs b/src/test/compile-fail/issue-6458-4.rs index c3f3a718ad0e2..a078cdea4ac4d 100644 --- a/src/test/compile-fail/issue-6458-4.rs +++ b/src/test/compile-fail/issue-6458-4.rs @@ -8,11 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn foo(b: bool) -> Result { - Err("bar".to_string()); - //~^ ERROR unable to infer enough type information about `_` [E0282] - //~| NOTE cannot infer type for `_` - //~| NOTE type annotations or generic parameter binding +fn foo(b: bool) -> Result { //~ ERROR mismatched types + Err("bar".to_string()); //~ HELP consider removing this semicolon } fn main() { diff --git a/src/test/compile-fail/liveness-forgot-ret.rs b/src/test/compile-fail/liveness-forgot-ret.rs index e08515e40af78..1ee4be08a1c50 100644 --- a/src/test/compile-fail/liveness-forgot-ret.rs +++ b/src/test/compile-fail/liveness-forgot-ret.rs @@ -8,10 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: not all control paths return a value - fn god_exists(a: isize) -> bool { return god_exists(a); } fn f(a: isize) -> isize { if god_exists(a) { return 5; }; } +//~^ ERROR mismatched types fn main() { f(12); } diff --git a/src/test/compile-fail/liveness-issue-2163.rs b/src/test/compile-fail/liveness-issue-2163.rs index 7c94e33b47b38..69bceec8c3225 100644 --- a/src/test/compile-fail/liveness-issue-2163.rs +++ b/src/test/compile-fail/liveness-issue-2163.rs @@ -13,6 +13,6 @@ use std::vec::Vec; fn main() { let a: Vec = Vec::new(); a.iter().all(|_| -> bool { - //~^ ERROR not all control paths return a value + //~^ ERROR mismatched types }); } diff --git a/src/test/compile-fail/liveness-missing-ret2.rs b/src/test/compile-fail/liveness-missing-ret2.rs index b53bb6159e8dd..a35eb1af4f336 100644 --- a/src/test/compile-fail/liveness-missing-ret2.rs +++ b/src/test/compile-fail/liveness-missing-ret2.rs @@ -8,9 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: not all control paths return a value - -fn f() -> isize { +fn f() -> isize { //~ ERROR mismatched types // Make sure typestate doesn't interpret this match expression as // the function result match true { true => { } _ => {} }; diff --git a/src/test/compile-fail/liveness-return-last-stmt-semi.rs b/src/test/compile-fail/liveness-return-last-stmt-semi.rs index 03733cc2eb596..ada91c38d48c3 100644 --- a/src/test/compile-fail/liveness-return-last-stmt-semi.rs +++ b/src/test/compile-fail/liveness-return-last-stmt-semi.rs @@ -11,16 +11,16 @@ // regression test for #8005 macro_rules! test { () => { fn foo() -> i32 { 1; } } } - //~^ ERROR not all control paths return a value + //~^ ERROR mismatched types //~| HELP consider removing this semicolon -fn no_return() -> i32 {} //~ ERROR not all control paths return a value +fn no_return() -> i32 {} //~ ERROR mismatched types -fn bar(x: u32) -> u32 { //~ ERROR not all control paths return a value +fn bar(x: u32) -> u32 { //~ ERROR mismatched types x * 2; //~ HELP consider removing this semicolon } -fn baz(x: u64) -> u32 { //~ ERROR not all control paths return a value +fn baz(x: u64) -> u32 { //~ ERROR mismatched types x * 2; } diff --git a/src/test/compile-fail/main-wrong-type-2.rs b/src/test/compile-fail/main-wrong-type-2.rs index 7434a6c960b2d..2878cbc7fc154 100644 --- a/src/test/compile-fail/main-wrong-type-2.rs +++ b/src/test/compile-fail/main-wrong-type-2.rs @@ -10,4 +10,5 @@ fn main() -> char { //~^ ERROR: main function has wrong type + ' ' } diff --git a/src/test/compile-fail/on-unimplemented/on-trait.rs b/src/test/compile-fail/on-unimplemented/on-trait.rs index 3a789f3faeb2a..0f4b0919b6500 100644 --- a/src/test/compile-fail/on-unimplemented/on-trait.rs +++ b/src/test/compile-fail/on-unimplemented/on-trait.rs @@ -16,7 +16,7 @@ trait Foo {} fn foobar>() -> T { - + panic!() } #[rustc_on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"] diff --git a/src/test/compile-fail/private-in-public-lint.rs b/src/test/compile-fail/private-in-public-lint.rs index 8e23bfcfb1051..4796548112d9e 100644 --- a/src/test/compile-fail/private-in-public-lint.rs +++ b/src/test/compile-fail/private-in-public-lint.rs @@ -13,7 +13,7 @@ mod m1 { struct Priv; impl Pub { - pub fn f() -> Priv {} //~ ERROR private type in public interface + pub fn f() -> Priv {Priv} //~ ERROR private type in public interface } } @@ -24,7 +24,7 @@ mod m2 { struct Priv; impl Pub { - pub fn f() -> Priv {} //~ ERROR private type in public interface + pub fn f() -> Priv {Priv} //~ ERROR private type in public interface } } diff --git a/src/test/compile-fail/required-lang-item.rs b/src/test/compile-fail/required-lang-item.rs index 1aa22a1676ef2..ce40702b3dc2a 100644 --- a/src/test/compile-fail/required-lang-item.rs +++ b/src/test/compile-fail/required-lang-item.rs @@ -11,6 +11,7 @@ #![feature(lang_items, no_core)] #![no_core] +#[lang="copy"] pub trait Copy { } #[lang="sized"] pub trait Sized { } // error-pattern:requires `start` lang_item diff --git a/src/test/compile-fail/unreachable-in-call.rs b/src/test/compile-fail/unreachable-in-call.rs index 5a3257d54db21..72462468432d9 100644 --- a/src/test/compile-fail/unreachable-in-call.rs +++ b/src/test/compile-fail/unreachable-in-call.rs @@ -24,7 +24,7 @@ fn diverge_first() { get_u8()); //~ ERROR unreachable expression } fn diverge_second() { - call( //~ ERROR unreachable call + call( //~ ERROR unreachable expression get_u8(), diverge()); } diff --git a/src/test/compile-fail/where-clauses-unsatisfied.rs b/src/test/compile-fail/where-clauses-unsatisfied.rs index 278a8db4e1ad4..ffc39008c4e5a 100644 --- a/src/test/compile-fail/where-clauses-unsatisfied.rs +++ b/src/test/compile-fail/where-clauses-unsatisfied.rs @@ -8,8 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn equal(_: &T, _: &T) -> bool where T : Eq { -} +fn equal(a: &T, b: &T) -> bool where T : Eq { a == b } struct Struct;