about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/infer/mod.rs51
-rw-r--r--src/librustc/infer/sub.rs1
-rw-r--r--src/librustc/traits/error_reporting.rs6
-rw-r--r--src/librustc_typeck/astconv.rs7
-rw-r--r--src/librustc_typeck/check/_match.rs2
-rw-r--r--src/librustc_typeck/check/cast.rs21
-rw-r--r--src/librustc_typeck/check/mod.rs33
-rw-r--r--src/librustc_typeck/collect.rs4
-rw-r--r--src/test/compile-fail/cast-rfc0401-2.rs20
-rw-r--r--src/test/compile-fail/cast-rfc0401.rs2
-rw-r--r--src/test/compile-fail/derived-errors/issue-30580.rs (renamed from src/test/compile-fail/issue-30580.rs)0
-rw-r--r--src/test/compile-fail/derived-errors/issue-31997.rs27
-rw-r--r--src/test/compile-fail/issue-26480.rs2
13 files changed, 166 insertions, 10 deletions
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<bool>,
+
+    // 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
index 88d4aef6d9d..88d4aef6d9d 100644
--- a/src/test/compile-fail/issue-30580.rs
+++ b/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<F, T>(x: F) -> Result<T, ()>
+    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!
 }