diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 4c1216aa862..c6a329bd64f 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -35,7 +35,7 @@ use ty::fold::{TypeFolder, TypeFoldable}; use ty::relate::{Relate, RelateResult, TypeRelation}; use traits::{self, PredicateObligations, ProjectionMode}; use rustc_data_structures::unify::{self, UnificationTable}; -use std::cell::{RefCell, Ref}; +use std::cell::{Cell, RefCell, Ref}; use std::fmt; use syntax::ast; use syntax::codemap; @@ -110,6 +110,25 @@ pub struct InferCtxt<'a, 'tcx: 'a> { // documentation for `ProjectionMode`. projection_mode: ProjectionMode, + // When an error occurs, we want to avoid reporting "derived" + // errors that are due to this original failure. Normally, we + // handle this with the `err_count_on_creation` count, which + // basically just tracks how many errors were reported when we + // started type-checking a fn and checks to see if any new errors + // have been reported since then. Not great, but it works. + // + // However, when errors originated in other passes -- notably + // resolve -- this heuristic breaks down. Therefore, we have this + // auxiliary flag that one can set whenever one creates a + // type-error that is due to an error in a prior pass. + // + // Don't read this flag directly, call `is_tainted_by_errors()` + // and `set_tainted_by_errors()`. + tainted_by_errors_flag: Cell, + + // Track how many errors were reported when this infcx is created. + // If the number of errors increases, that's also a sign (line + // `tained_by_errors`) to avoid reporting certain kinds of errors. err_count_on_creation: usize, } @@ -379,6 +398,7 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>, reported_trait_errors: RefCell::new(FnvHashSet()), normalize: false, projection_mode: projection_mode, + tainted_by_errors_flag: Cell::new(false), err_count_on_creation: tcx.sess.err_count() } } @@ -1128,15 +1148,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .map(|method| resolve_ty(method.ty))) } - pub fn errors_since_creation(&self) -> bool { - self.tcx.sess.err_count() - self.err_count_on_creation != 0 + /// True if errors have been reported since this infcx was + /// created. This is sometimes used as a heuristic to skip + /// reporting errors that often occur as a result of earlier + /// errors, but where it's hard to be 100% sure (e.g., unresolved + /// inference variables, regionck errors). + pub fn is_tainted_by_errors(&self) -> bool { + debug!("is_tainted_by_errors(err_count={}, err_count_on_creation={}, \ + tainted_by_errors_flag={})", + self.tcx.sess.err_count(), + self.err_count_on_creation, + self.tainted_by_errors_flag.get()); + + if self.tcx.sess.err_count() > self.err_count_on_creation { + return true; // errors reported since this infcx was made + } + self.tainted_by_errors_flag.get() + } + + /// Set the "tainted by errors" flag to true. We call this when we + /// observe an error from a prior pass. + pub fn set_tainted_by_errors(&self) { + debug!("set_tainted_by_errors()"); + self.tainted_by_errors_flag.set(true) } pub fn node_type(&self, id: ast::NodeId) -> Ty<'tcx> { match self.tables.borrow().node_types.get(&id) { Some(&t) => t, // FIXME - None if self.errors_since_creation() => + None if self.is_tainted_by_errors() => self.tcx.types.err, None => { bug!("no type for node {}: {} in fcx", @@ -1158,7 +1199,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { free_regions: &FreeRegionMap, subject_node_id: ast::NodeId) { let errors = self.region_vars.resolve_regions(free_regions, subject_node_id); - if !self.errors_since_creation() { + if !self.is_tainted_by_errors() { // As a heuristic, just skip reporting region errors // altogether if other errors have been reported while // this infcx was in use. This is totally hokey but diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index ece8c0c696a..71e1efe220f 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -91,6 +91,7 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Sub<'a, 'tcx> { } (&ty::TyError, _) | (_, &ty::TyError) => { + infcx.set_tainted_by_errors(); Ok(self.tcx().types.err) } diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 286733c7c26..44cd05907df 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -609,6 +609,12 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, predicate, obligation); + // Ambiguity errors are often caused as fallout from earlier + // errors. So just ignore them if this infcx is tainted. + if infcx.is_tainted_by_errors() { + return; + } + match predicate { ty::Predicate::Trait(ref data) => { let trait_ref = data.to_poly_trait_ref(); diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 21122e7095d..3f9f8a45272 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -154,6 +154,12 @@ pub trait AstConv<'tcx> { _trait_ref: ty::TraitRef<'tcx>, _item_name: ast::Name) -> Ty<'tcx>; + + /// Invoked when we encounter an error from some prior pass + /// (e.g. resolve) that is translated into a ty-error. This is + /// used to help suppress derived errors typeck might otherwise + /// report. + fn set_tainted_by_errors(&self); } pub fn ast_region_to_region(tcx: &TyCtxt, lifetime: &hir::Lifetime) @@ -1532,6 +1538,7 @@ fn base_def_to_ty<'tcx>(this: &AstConv<'tcx>, prim_ty_to_ty(tcx, base_segments, prim_ty) } Def::Err => { + this.set_tainted_by_errors(); return this.tcx().types.err; } _ => { diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 8dbd6496b6f..a7a04f4a85f 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -209,6 +209,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, let self_ty = fcx.to_ty(&qself.ty); let path_res = if let Some(&d) = tcx.def_map.borrow().get(&pat.id) { if d.base_def == Def::Err { + fcx.infcx().set_tainted_by_errors(); fcx.write_error(pat.id); return; } @@ -628,6 +629,7 @@ fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, let path_res = match tcx.def_map.borrow().get(&pat.id) { Some(&path_res) if path_res.base_def != Def::Err => path_res, _ => { + fcx.infcx().set_tainted_by_errors(); fcx.write_error(pat.id); if let Some(subpats) = subpats { diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 91cdb8d966d..ea872a92dcf 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -121,8 +121,27 @@ impl<'tcx> CastCheck<'tcx> { } } - fn report_cast_error<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, + fn report_cast_error<'a>(&self, + fcx: &FnCtxt<'a, 'tcx>, e: CastError) { + // As a heuristic, don't report errors if there are unresolved + // inference variables floating around AND we've already + // reported some errors in this fn. It happens often that those + // inference variables are unresolved precisely *because* of + // the errors we've already reported. See #31997. + // + // Note: it's kind of annoying that we need this. Fallback is + // modified to push all unresolved inference variables to + // ty-err, but it's STILL possible to see fallback for + // integral/float variables, because those cannot be unified + // with ty-error. + if + fcx.infcx().is_tainted_by_errors() && + (self.cast_ty.has_infer_types() || self.expr_ty.has_infer_types()) + { + return; + } + match e { CastError::NeedViaPtr | CastError::NeedViaThinPtr | diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 67b91f7838c..5bc6184263c 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1240,6 +1240,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { { self.normalize_associated_type(span, trait_ref, item_name) } + + fn set_tainted_by_errors(&self) { + self.infcx().set_tainted_by_errors() + } } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -1771,16 +1775,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn default_type_parameters(&self) { use rustc::ty::error::UnconstrainedNumeric::Neither; use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat}; + + // Defaulting inference variables becomes very dubious if we have + // encountered type-checking errors. Therefore, if we think we saw + // some errors in this function, just resolve all uninstanted type + // varibles to TyError. + if self.infcx().is_tainted_by_errors() { + for ty in &self.infcx().unsolved_variables() { + if let ty::TyInfer(_) = self.infcx().shallow_resolve(ty).sty { + debug!("default_type_parameters: defaulting `{:?}` to error", ty); + demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.err); + } + } + return; + } + for ty in &self.infcx().unsolved_variables() { let resolved = self.infcx().resolve_type_vars_if_possible(ty); if self.infcx().type_var_diverges(resolved) { + debug!("default_type_parameters: defaulting `{:?}` to `()` because it diverges", + resolved); demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil()); } else { match self.infcx().type_is_unconstrained_numeric(resolved) { UnconstrainedInt => { + debug!("default_type_parameters: defaulting `{:?}` to `i32`", + resolved); demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32) }, UnconstrainedFloat => { + debug!("default_type_parameters: defaulting `{:?}` to `f32`", + resolved); demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64) } Neither => { } @@ -3232,6 +3257,7 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // Find the relevant variant let def = lookup_full_def(tcx, path.span, expr.id); if def == Def::Err { + fcx.infcx().set_tainted_by_errors(); check_struct_fields_on_error(fcx, expr.id, fields, base_expr); return; } @@ -3435,6 +3461,7 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr.span, id); } else { + fcx.infcx().set_tainted_by_errors(); fcx.write_ty(id, fcx.tcx().types.err); } } @@ -4408,8 +4435,12 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, Def::ForeignMod(..) | Def::Local(..) | Def::Label(..) | - Def::Upvar(..) | + Def::Upvar(..) => { + segment_spaces = vec![None; segments.len()]; + } + Def::Err => { + fcx.infcx().set_tainted_by_errors(); segment_spaces = vec![None; segments.len()]; } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 2e1a6846843..39465d6ffe3 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -383,6 +383,10 @@ impl<'a, 'tcx> AstConv<'tcx> for ItemCtxt<'a, 'tcx> { { self.tcx().mk_projection(trait_ref, item_name) } + + fn set_tainted_by_errors(&self) { + // no obvious place to track this, just let it go + } } /// Interface used to find the bounds on a type parameter from within diff --git a/src/test/compile-fail/cast-rfc0401-2.rs b/src/test/compile-fail/cast-rfc0401-2.rs new file mode 100644 index 00000000000..fdc250f9946 --- /dev/null +++ b/src/test/compile-fail/cast-rfc0401-2.rs @@ -0,0 +1,20 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// RFC 401 test extracted into distinct file. This is because some the +// change to suppress "derived" errors wound up suppressing this error +// message, since the fallback for `3` doesn't occur. + +fn main() { + let _ = 3 as bool; + //~^ ERROR cannot cast as `bool` + //~| HELP see a detailed explanation + //~| HELP compare with zero +} diff --git a/src/test/compile-fail/cast-rfc0401.rs b/src/test/compile-fail/cast-rfc0401.rs index dcd49e34bb2..fcfb5706e5d 100644 --- a/src/test/compile-fail/cast-rfc0401.rs +++ b/src/test/compile-fail/cast-rfc0401.rs @@ -58,7 +58,7 @@ fn main() let _ = f as *const u8; //~^ ERROR casting //~^^ HELP through a usize first - let _ = 3 as bool; + let _ = 3_i32 as bool; //~^ ERROR cannot cast as `bool` //~^^ HELP compare with zero //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation diff --git a/src/test/compile-fail/issue-30580.rs b/src/test/compile-fail/derived-errors/issue-30580.rs similarity index 100% rename from src/test/compile-fail/issue-30580.rs rename to src/test/compile-fail/derived-errors/issue-30580.rs diff --git a/src/test/compile-fail/derived-errors/issue-31997.rs b/src/test/compile-fail/derived-errors/issue-31997.rs new file mode 100644 index 00000000000..cf283f6d3e4 --- /dev/null +++ b/src/test/compile-fail/derived-errors/issue-31997.rs @@ -0,0 +1,27 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that the resolve failure does not lead to downstream type errors. +// See issue #31997. + +trait TheTrait { } + +fn closure(x: F) -> Result + where F: FnMut() -> T, T: TheTrait, +{ + unimplemented!() +} + +fn foo() -> Result<(), ()> { + try!(closure(|| bar(0 as *mut _))); //~ ERROR unresolved name `bar` + Ok(()) +} + +fn main() { } diff --git a/src/test/compile-fail/issue-26480.rs b/src/test/compile-fail/issue-26480.rs index 903df42291c..44aff623860 100644 --- a/src/test/compile-fail/issue-26480.rs +++ b/src/test/compile-fail/issue-26480.rs @@ -31,7 +31,6 @@ macro_rules! write { macro_rules! cast { ($x:expr) => ($x as ()) - //~^ ERROR non-scalar cast: `i32` as `()` } fn main() { @@ -40,5 +39,4 @@ fn main() { //~^ NOTE in this expansion of write! cast!(2); - //~^ NOTE in this expansion of cast! }