rustc_typeck: correctly track "always-diverges" and "has-type-errors".
This commit is contained in:
parent
ff0830d749
commit
6b3cc0b8c8
@ -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() -> ! {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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<NodeMap<Ty<'tcx>>>,
|
||||
@ -371,6 +425,12 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
|
||||
ps: RefCell<UnsafetyState>,
|
||||
|
||||
/// Whether the last checked node can ever exit.
|
||||
diverges: Cell<Diverges>,
|
||||
|
||||
/// Whether any child nodes have any type errors.
|
||||
has_errors: Cell<bool>,
|
||||
|
||||
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];
|
||||
|
||||
// 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)
|
||||
});
|
||||
|
||||
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));
|
||||
if is_closure != check_closures {
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
debug!("checking the argument");
|
||||
let formal_ty = formal_tys[i];
|
||||
|
||||
// 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)
|
||||
});
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
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()
|
||||
} 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)
|
||||
}
|
||||
};
|
||||
|
||||
if any_err {
|
||||
self.tcx.types.err
|
||||
} else if any_diverges {
|
||||
self.next_diverging_ty_var()
|
||||
} else {
|
||||
ety
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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 {
|
||||
// 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:");
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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()
|
||||
}
|
||||
_ => {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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() { }
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ struct Bob;
|
||||
|
||||
impl<RHS: Scalar> Add <RHS> for Bob {
|
||||
type Output = Bob;
|
||||
fn add(self, rhs : RHS) -> Bob {}
|
||||
fn add(self, rhs : RHS) -> Bob { Bob }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -15,7 +15,7 @@ mod foo {
|
||||
}
|
||||
|
||||
pub trait Baz {
|
||||
fn bar(&self) -> bool {}
|
||||
fn bar(&self) -> bool { true }
|
||||
}
|
||||
impl Baz for Foo {}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ mod a {
|
||||
|
||||
impl Default for A {
|
||||
pub fn default() -> A { //~ ERROR unnecessary visibility qualifier
|
||||
A;
|
||||
A
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,6 @@ pub trait Tr<'a> {
|
||||
}
|
||||
|
||||
pub fn f<'a, T: Tr<'a>>() -> <T as Tr<'a>>::Out {}
|
||||
//~^ ERROR not all control paths return a value
|
||||
//~^ ERROR mismatched types
|
||||
|
||||
pub fn main() {}
|
||||
|
@ -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`
|
||||
}
|
||||
|
@ -8,11 +8,8 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn foo(b: bool) -> Result<bool,String> {
|
||||
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<bool,String> { //~ ERROR mismatched types
|
||||
Err("bar".to_string()); //~ HELP consider removing this semicolon
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -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); }
|
||||
|
@ -13,6 +13,6 @@ use std::vec::Vec;
|
||||
fn main() {
|
||||
let a: Vec<isize> = Vec::new();
|
||||
a.iter().all(|_| -> bool {
|
||||
//~^ ERROR not all control paths return a value
|
||||
//~^ ERROR mismatched types
|
||||
});
|
||||
}
|
||||
|
@ -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 => { } _ => {} };
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -10,4 +10,5 @@
|
||||
|
||||
fn main() -> char {
|
||||
//~^ ERROR: main function has wrong type
|
||||
' '
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ trait Foo<Bar, Baz, Quux>
|
||||
{}
|
||||
|
||||
fn foobar<U: Clone, T: Foo<u8, U, u32>>() -> T {
|
||||
|
||||
panic!()
|
||||
}
|
||||
|
||||
#[rustc_on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"]
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -8,8 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn equal<T>(_: &T, _: &T) -> bool where T : Eq {
|
||||
}
|
||||
fn equal<T>(a: &T, b: &T) -> bool where T : Eq { a == b }
|
||||
|
||||
struct Struct;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user