rustc_typeck: correctly track "always-diverges" and "has-type-errors".

This commit is contained in:
Eduard Burtescu 2016-10-26 02:28:20 +03:00
parent ff0830d749
commit 6b3cc0b8c8
26 changed files with 349 additions and 216 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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()
}
_ => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@ mod foo {
}
pub trait Baz {
fn bar(&self) -> bool {}
fn bar(&self) -> bool { true }
}
impl Baz for Foo {}
}

View File

@ -13,7 +13,7 @@ mod a {
impl Default for A {
pub fn default() -> A { //~ ERROR unnecessary visibility qualifier
A;
A
}
}
}

View File

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

View File

@ -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`
}

View File

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

View File

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

View File

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

View File

@ -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 => { } _ => {} };

View File

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

View File

@ -10,4 +10,5 @@
fn main() -> char {
//~^ ERROR: main function has wrong type
' '
}

View File

@ -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}`"]

View File

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

View File

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

View File

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

View File

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