Skip to content

Commit ccaa2f8

Browse files
committed
Suppress fallback and ambiguity errors
If the infcx has observed other errors, then suppress both default type parameter fallback (which can be unreliable, as the full constraint set is not available) and errors related to unresovled variables (annoyingly, integer type variables cannot currently be unified with error, so that has to be a separate mechanism). Also add a flag to `infcx` to allow us to independently indicate when we have observed an error and hence should trigger this suppression mode.
1 parent 9330006 commit ccaa2f8

File tree

13 files changed

+166
-10
lines changed

13 files changed

+166
-10
lines changed

src/librustc/infer/mod.rs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use ty::fold::{TypeFolder, TypeFoldable};
3535
use ty::relate::{Relate, RelateResult, TypeRelation};
3636
use traits::{self, PredicateObligations, ProjectionMode};
3737
use rustc_data_structures::unify::{self, UnificationTable};
38-
use std::cell::{RefCell, Ref};
38+
use std::cell::{Cell, RefCell, Ref};
3939
use std::fmt;
4040
use syntax::ast;
4141
use syntax::codemap;
@@ -110,6 +110,25 @@ pub struct InferCtxt<'a, 'tcx: 'a> {
110110
// documentation for `ProjectionMode`.
111111
projection_mode: ProjectionMode,
112112

113+
// When an error occurs, we want to avoid reporting "derived"
114+
// errors that are due to this original failure. Normally, we
115+
// handle this with the `err_count_on_creation` count, which
116+
// basically just tracks how many errors were reported when we
117+
// started type-checking a fn and checks to see if any new errors
118+
// have been reported since then. Not great, but it works.
119+
//
120+
// However, when errors originated in other passes -- notably
121+
// resolve -- this heuristic breaks down. Therefore, we have this
122+
// auxiliary flag that one can set whenever one creates a
123+
// type-error that is due to an error in a prior pass.
124+
//
125+
// Don't read this flag directly, call `is_tainted_by_errors()`
126+
// and `set_tainted_by_errors()`.
127+
tainted_by_errors_flag: Cell<bool>,
128+
129+
// Track how many errors were reported when this infcx is created.
130+
// If the number of errors increases, that's also a sign (line
131+
// `tained_by_errors`) to avoid reporting certain kinds of errors.
113132
err_count_on_creation: usize,
114133
}
115134

@@ -379,6 +398,7 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
379398
reported_trait_errors: RefCell::new(FnvHashSet()),
380399
normalize: false,
381400
projection_mode: projection_mode,
401+
tainted_by_errors_flag: Cell::new(false),
382402
err_count_on_creation: tcx.sess.err_count()
383403
}
384404
}
@@ -1128,15 +1148,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
11281148
.map(|method| resolve_ty(method.ty)))
11291149
}
11301150

1131-
pub fn errors_since_creation(&self) -> bool {
1132-
self.tcx.sess.err_count() - self.err_count_on_creation != 0
1151+
/// True if errors have been reported since this infcx was
1152+
/// created. This is sometimes used as a heuristic to skip
1153+
/// reporting errors that often occur as a result of earlier
1154+
/// errors, but where it's hard to be 100% sure (e.g., unresolved
1155+
/// inference variables, regionck errors).
1156+
pub fn is_tainted_by_errors(&self) -> bool {
1157+
debug!("is_tainted_by_errors(err_count={}, err_count_on_creation={}, \
1158+
tainted_by_errors_flag={})",
1159+
self.tcx.sess.err_count(),
1160+
self.err_count_on_creation,
1161+
self.tainted_by_errors_flag.get());
1162+
1163+
if self.tcx.sess.err_count() > self.err_count_on_creation {
1164+
return true; // errors reported since this infcx was made
1165+
}
1166+
self.tainted_by_errors_flag.get()
1167+
}
1168+
1169+
/// Set the "tainted by errors" flag to true. We call this when we
1170+
/// observe an error from a prior pass.
1171+
pub fn set_tainted_by_errors(&self) {
1172+
debug!("set_tainted_by_errors()");
1173+
self.tainted_by_errors_flag.set(true)
11331174
}
11341175

11351176
pub fn node_type(&self, id: ast::NodeId) -> Ty<'tcx> {
11361177
match self.tables.borrow().node_types.get(&id) {
11371178
Some(&t) => t,
11381179
// FIXME
1139-
None if self.errors_since_creation() =>
1180+
None if self.is_tainted_by_errors() =>
11401181
self.tcx.types.err,
11411182
None => {
11421183
bug!("no type for node {}: {} in fcx",
@@ -1158,7 +1199,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
11581199
free_regions: &FreeRegionMap,
11591200
subject_node_id: ast::NodeId) {
11601201
let errors = self.region_vars.resolve_regions(free_regions, subject_node_id);
1161-
if !self.errors_since_creation() {
1202+
if !self.is_tainted_by_errors() {
11621203
// As a heuristic, just skip reporting region errors
11631204
// altogether if other errors have been reported while
11641205
// this infcx was in use. This is totally hokey but

src/librustc/infer/sub.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Sub<'a, 'tcx> {
9191
}
9292

9393
(&ty::TyError, _) | (_, &ty::TyError) => {
94+
infcx.set_tainted_by_errors();
9495
Ok(self.tcx().types.err)
9596
}
9697

src/librustc/traits/error_reporting.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,12 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
609609
predicate,
610610
obligation);
611611

612+
// Ambiguity errors are often caused as fallout from earlier
613+
// errors. So just ignore them if this infcx is tainted.
614+
if infcx.is_tainted_by_errors() {
615+
return;
616+
}
617+
612618
match predicate {
613619
ty::Predicate::Trait(ref data) => {
614620
let trait_ref = data.to_poly_trait_ref();

src/librustc_typeck/astconv.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ pub trait AstConv<'tcx> {
154154
_trait_ref: ty::TraitRef<'tcx>,
155155
_item_name: ast::Name)
156156
-> Ty<'tcx>;
157+
158+
/// Invoked when we encounter an error from some prior pass
159+
/// (e.g. resolve) that is translated into a ty-error. This is
160+
/// used to help suppress derived errors typeck might otherwise
161+
/// report.
162+
fn set_tainted_by_errors(&self);
157163
}
158164

159165
pub fn ast_region_to_region(tcx: &TyCtxt, lifetime: &hir::Lifetime)
@@ -1532,6 +1538,7 @@ fn base_def_to_ty<'tcx>(this: &AstConv<'tcx>,
15321538
prim_ty_to_ty(tcx, base_segments, prim_ty)
15331539
}
15341540
Def::Err => {
1541+
this.set_tainted_by_errors();
15351542
return this.tcx().types.err;
15361543
}
15371544
_ => {

src/librustc_typeck/check/_match.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
209209
let self_ty = fcx.to_ty(&qself.ty);
210210
let path_res = if let Some(&d) = tcx.def_map.borrow().get(&pat.id) {
211211
if d.base_def == Def::Err {
212+
fcx.infcx().set_tainted_by_errors();
212213
fcx.write_error(pat.id);
213214
return;
214215
}
@@ -628,6 +629,7 @@ fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
628629
let path_res = match tcx.def_map.borrow().get(&pat.id) {
629630
Some(&path_res) if path_res.base_def != Def::Err => path_res,
630631
_ => {
632+
fcx.infcx().set_tainted_by_errors();
631633
fcx.write_error(pat.id);
632634

633635
if let Some(subpats) = subpats {

src/librustc_typeck/check/cast.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,27 @@ impl<'tcx> CastCheck<'tcx> {
121121
}
122122
}
123123

124-
fn report_cast_error<'a>(&self, fcx: &FnCtxt<'a, 'tcx>,
124+
fn report_cast_error<'a>(&self,
125+
fcx: &FnCtxt<'a, 'tcx>,
125126
e: CastError) {
127+
// As a heuristic, don't report errors if there are unresolved
128+
// inference variables floating around AND we've already
129+
// reported some errors in this fn. It happens often that those
130+
// inference variables are unresolved precisely *because* of
131+
// the errors we've already reported. See #31997.
132+
//
133+
// Note: it's kind of annoying that we need this. Fallback is
134+
// modified to push all unresolved inference variables to
135+
// ty-err, but it's STILL possible to see fallback for
136+
// integral/float variables, because those cannot be unified
137+
// with ty-error.
138+
if
139+
fcx.infcx().is_tainted_by_errors() &&
140+
(self.cast_ty.has_infer_types() || self.expr_ty.has_infer_types())
141+
{
142+
return;
143+
}
144+
126145
match e {
127146
CastError::NeedViaPtr |
128147
CastError::NeedViaThinPtr |

src/librustc_typeck/check/mod.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
12401240
{
12411241
self.normalize_associated_type(span, trait_ref, item_name)
12421242
}
1243+
1244+
fn set_tainted_by_errors(&self) {
1245+
self.infcx().set_tainted_by_errors()
1246+
}
12431247
}
12441248

12451249
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -1771,16 +1775,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17711775
fn default_type_parameters(&self) {
17721776
use rustc::ty::error::UnconstrainedNumeric::Neither;
17731777
use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat};
1778+
1779+
// Defaulting inference variables becomes very dubious if we have
1780+
// encountered type-checking errors. Therefore, if we think we saw
1781+
// some errors in this function, just resolve all uninstanted type
1782+
// varibles to TyError.
1783+
if self.infcx().is_tainted_by_errors() {
1784+
for ty in &self.infcx().unsolved_variables() {
1785+
if let ty::TyInfer(_) = self.infcx().shallow_resolve(ty).sty {
1786+
debug!("default_type_parameters: defaulting `{:?}` to error", ty);
1787+
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.err);
1788+
}
1789+
}
1790+
return;
1791+
}
1792+
17741793
for ty in &self.infcx().unsolved_variables() {
17751794
let resolved = self.infcx().resolve_type_vars_if_possible(ty);
17761795
if self.infcx().type_var_diverges(resolved) {
1796+
debug!("default_type_parameters: defaulting `{:?}` to `()` because it diverges",
1797+
resolved);
17771798
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
17781799
} else {
17791800
match self.infcx().type_is_unconstrained_numeric(resolved) {
17801801
UnconstrainedInt => {
1802+
debug!("default_type_parameters: defaulting `{:?}` to `i32`",
1803+
resolved);
17811804
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
17821805
},
17831806
UnconstrainedFloat => {
1807+
debug!("default_type_parameters: defaulting `{:?}` to `f32`",
1808+
resolved);
17841809
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
17851810
}
17861811
Neither => { }
@@ -3232,6 +3257,7 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
32323257
// Find the relevant variant
32333258
let def = lookup_full_def(tcx, path.span, expr.id);
32343259
if def == Def::Err {
3260+
fcx.infcx().set_tainted_by_errors();
32353261
check_struct_fields_on_error(fcx, expr.id, fields, base_expr);
32363262
return;
32373263
}
@@ -3435,6 +3461,7 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
34353461
expr.span,
34363462
id);
34373463
} else {
3464+
fcx.infcx().set_tainted_by_errors();
34383465
fcx.write_ty(id, fcx.tcx().types.err);
34393466
}
34403467
}
@@ -4408,8 +4435,12 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
44084435
Def::ForeignMod(..) |
44094436
Def::Local(..) |
44104437
Def::Label(..) |
4411-
Def::Upvar(..) |
4438+
Def::Upvar(..) => {
4439+
segment_spaces = vec![None; segments.len()];
4440+
}
4441+
44124442
Def::Err => {
4443+
fcx.infcx().set_tainted_by_errors();
44134444
segment_spaces = vec![None; segments.len()];
44144445
}
44154446
}

src/librustc_typeck/collect.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,10 @@ impl<'a, 'tcx> AstConv<'tcx> for ItemCtxt<'a, 'tcx> {
383383
{
384384
self.tcx().mk_projection(trait_ref, item_name)
385385
}
386+
387+
fn set_tainted_by_errors(&self) {
388+
// no obvious place to track this, just let it go
389+
}
386390
}
387391

388392
/// Interface used to find the bounds on a type parameter from within
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// RFC 401 test extracted into distinct file. This is because some the
12+
// change to suppress "derived" errors wound up suppressing this error
13+
// message, since the fallback for `3` doesn't occur.
14+
15+
fn main() {
16+
let _ = 3 as bool;
17+
//~^ ERROR cannot cast as `bool`
18+
//~| HELP see a detailed explanation
19+
//~| HELP compare with zero
20+
}

src/test/compile-fail/cast-rfc0401.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ fn main()
5858
let _ = f as *const u8;
5959
//~^ ERROR casting
6060
//~^^ HELP through a usize first
61-
let _ = 3 as bool;
61+
let _ = 3_i32 as bool;
6262
//~^ ERROR cannot cast as `bool`
6363
//~^^ HELP compare with zero
6464
//~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that the resolve failure does not lead to downstream type errors.
12+
// See issue #31997.
13+
14+
trait TheTrait { }
15+
16+
fn closure<F, T>(x: F) -> Result<T, ()>
17+
where F: FnMut() -> T, T: TheTrait,
18+
{
19+
unimplemented!()
20+
}
21+
22+
fn foo() -> Result<(), ()> {
23+
try!(closure(|| bar(0 as *mut _))); //~ ERROR unresolved name `bar`
24+
Ok(())
25+
}
26+
27+
fn main() { }

src/test/compile-fail/issue-26480.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ macro_rules! write {
3131

3232
macro_rules! cast {
3333
($x:expr) => ($x as ())
34-
//~^ ERROR non-scalar cast: `i32` as `()`
3534
}
3635

3736
fn main() {
@@ -40,5 +39,4 @@ fn main() {
4039
//~^ NOTE in this expansion of write!
4140

4241
cast!(2);
43-
//~^ NOTE in this expansion of cast!
4442
}

0 commit comments

Comments
 (0)