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.
This commit is contained in:
parent
933000613b
commit
ccaa2f855e
@ -35,7 +35,7 @@ use ty::fold::{TypeFolder, TypeFoldable};
|
|||||||
use ty::relate::{Relate, RelateResult, TypeRelation};
|
use ty::relate::{Relate, RelateResult, TypeRelation};
|
||||||
use traits::{self, PredicateObligations, ProjectionMode};
|
use traits::{self, PredicateObligations, ProjectionMode};
|
||||||
use rustc_data_structures::unify::{self, UnificationTable};
|
use rustc_data_structures::unify::{self, UnificationTable};
|
||||||
use std::cell::{RefCell, Ref};
|
use std::cell::{Cell, RefCell, Ref};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::codemap;
|
use syntax::codemap;
|
||||||
@ -110,6 +110,25 @@ pub struct InferCtxt<'a, 'tcx: 'a> {
|
|||||||
// documentation for `ProjectionMode`.
|
// documentation for `ProjectionMode`.
|
||||||
projection_mode: 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,
|
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()),
|
reported_trait_errors: RefCell::new(FnvHashSet()),
|
||||||
normalize: false,
|
normalize: false,
|
||||||
projection_mode: projection_mode,
|
projection_mode: projection_mode,
|
||||||
|
tainted_by_errors_flag: Cell::new(false),
|
||||||
err_count_on_creation: tcx.sess.err_count()
|
err_count_on_creation: tcx.sess.err_count()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1128,15 +1148,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||||||
.map(|method| resolve_ty(method.ty)))
|
.map(|method| resolve_ty(method.ty)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn errors_since_creation(&self) -> bool {
|
/// True if errors have been reported since this infcx was
|
||||||
self.tcx.sess.err_count() - self.err_count_on_creation != 0
|
/// 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> {
|
pub fn node_type(&self, id: ast::NodeId) -> Ty<'tcx> {
|
||||||
match self.tables.borrow().node_types.get(&id) {
|
match self.tables.borrow().node_types.get(&id) {
|
||||||
Some(&t) => t,
|
Some(&t) => t,
|
||||||
// FIXME
|
// FIXME
|
||||||
None if self.errors_since_creation() =>
|
None if self.is_tainted_by_errors() =>
|
||||||
self.tcx.types.err,
|
self.tcx.types.err,
|
||||||
None => {
|
None => {
|
||||||
bug!("no type for node {}: {} in fcx",
|
bug!("no type for node {}: {} in fcx",
|
||||||
@ -1158,7 +1199,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||||||
free_regions: &FreeRegionMap,
|
free_regions: &FreeRegionMap,
|
||||||
subject_node_id: ast::NodeId) {
|
subject_node_id: ast::NodeId) {
|
||||||
let errors = self.region_vars.resolve_regions(free_regions, subject_node_id);
|
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
|
// As a heuristic, just skip reporting region errors
|
||||||
// altogether if other errors have been reported while
|
// altogether if other errors have been reported while
|
||||||
// this infcx was in use. This is totally hokey but
|
// this infcx was in use. This is totally hokey but
|
||||||
|
@ -91,6 +91,7 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Sub<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(&ty::TyError, _) | (_, &ty::TyError) => {
|
(&ty::TyError, _) | (_, &ty::TyError) => {
|
||||||
|
infcx.set_tainted_by_errors();
|
||||||
Ok(self.tcx().types.err)
|
Ok(self.tcx().types.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,6 +609,12 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
|||||||
predicate,
|
predicate,
|
||||||
obligation);
|
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 {
|
match predicate {
|
||||||
ty::Predicate::Trait(ref data) => {
|
ty::Predicate::Trait(ref data) => {
|
||||||
let trait_ref = data.to_poly_trait_ref();
|
let trait_ref = data.to_poly_trait_ref();
|
||||||
|
@ -154,6 +154,12 @@ pub trait AstConv<'tcx> {
|
|||||||
_trait_ref: ty::TraitRef<'tcx>,
|
_trait_ref: ty::TraitRef<'tcx>,
|
||||||
_item_name: ast::Name)
|
_item_name: ast::Name)
|
||||||
-> Ty<'tcx>;
|
-> 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)
|
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)
|
prim_ty_to_ty(tcx, base_segments, prim_ty)
|
||||||
}
|
}
|
||||||
Def::Err => {
|
Def::Err => {
|
||||||
|
this.set_tainted_by_errors();
|
||||||
return this.tcx().types.err;
|
return this.tcx().types.err;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -209,6 +209,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
|
|||||||
let self_ty = fcx.to_ty(&qself.ty);
|
let self_ty = fcx.to_ty(&qself.ty);
|
||||||
let path_res = if let Some(&d) = tcx.def_map.borrow().get(&pat.id) {
|
let path_res = if let Some(&d) = tcx.def_map.borrow().get(&pat.id) {
|
||||||
if d.base_def == Def::Err {
|
if d.base_def == Def::Err {
|
||||||
|
fcx.infcx().set_tainted_by_errors();
|
||||||
fcx.write_error(pat.id);
|
fcx.write_error(pat.id);
|
||||||
return;
|
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) {
|
let path_res = match tcx.def_map.borrow().get(&pat.id) {
|
||||||
Some(&path_res) if path_res.base_def != Def::Err => path_res,
|
Some(&path_res) if path_res.base_def != Def::Err => path_res,
|
||||||
_ => {
|
_ => {
|
||||||
|
fcx.infcx().set_tainted_by_errors();
|
||||||
fcx.write_error(pat.id);
|
fcx.write_error(pat.id);
|
||||||
|
|
||||||
if let Some(subpats) = subpats {
|
if let Some(subpats) = subpats {
|
||||||
|
@ -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) {
|
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 {
|
match e {
|
||||||
CastError::NeedViaPtr |
|
CastError::NeedViaPtr |
|
||||||
CastError::NeedViaThinPtr |
|
CastError::NeedViaThinPtr |
|
||||||
|
@ -1240,6 +1240,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
|
|||||||
{
|
{
|
||||||
self.normalize_associated_type(span, trait_ref, item_name)
|
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> {
|
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
@ -1771,16 +1775,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
fn default_type_parameters(&self) {
|
fn default_type_parameters(&self) {
|
||||||
use rustc::ty::error::UnconstrainedNumeric::Neither;
|
use rustc::ty::error::UnconstrainedNumeric::Neither;
|
||||||
use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat};
|
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() {
|
for ty in &self.infcx().unsolved_variables() {
|
||||||
let resolved = self.infcx().resolve_type_vars_if_possible(ty);
|
let resolved = self.infcx().resolve_type_vars_if_possible(ty);
|
||||||
if self.infcx().type_var_diverges(resolved) {
|
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());
|
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
|
||||||
} else {
|
} else {
|
||||||
match self.infcx().type_is_unconstrained_numeric(resolved) {
|
match self.infcx().type_is_unconstrained_numeric(resolved) {
|
||||||
UnconstrainedInt => {
|
UnconstrainedInt => {
|
||||||
|
debug!("default_type_parameters: defaulting `{:?}` to `i32`",
|
||||||
|
resolved);
|
||||||
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
|
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
|
||||||
},
|
},
|
||||||
UnconstrainedFloat => {
|
UnconstrainedFloat => {
|
||||||
|
debug!("default_type_parameters: defaulting `{:?}` to `f32`",
|
||||||
|
resolved);
|
||||||
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
|
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
|
||||||
}
|
}
|
||||||
Neither => { }
|
Neither => { }
|
||||||
@ -3232,6 +3257,7 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
|||||||
// Find the relevant variant
|
// Find the relevant variant
|
||||||
let def = lookup_full_def(tcx, path.span, expr.id);
|
let def = lookup_full_def(tcx, path.span, expr.id);
|
||||||
if def == Def::Err {
|
if def == Def::Err {
|
||||||
|
fcx.infcx().set_tainted_by_errors();
|
||||||
check_struct_fields_on_error(fcx, expr.id, fields, base_expr);
|
check_struct_fields_on_error(fcx, expr.id, fields, base_expr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3435,6 +3461,7 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
|||||||
expr.span,
|
expr.span,
|
||||||
id);
|
id);
|
||||||
} else {
|
} else {
|
||||||
|
fcx.infcx().set_tainted_by_errors();
|
||||||
fcx.write_ty(id, fcx.tcx().types.err);
|
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::ForeignMod(..) |
|
||||||
Def::Local(..) |
|
Def::Local(..) |
|
||||||
Def::Label(..) |
|
Def::Label(..) |
|
||||||
Def::Upvar(..) |
|
Def::Upvar(..) => {
|
||||||
|
segment_spaces = vec![None; segments.len()];
|
||||||
|
}
|
||||||
|
|
||||||
Def::Err => {
|
Def::Err => {
|
||||||
|
fcx.infcx().set_tainted_by_errors();
|
||||||
segment_spaces = vec![None; segments.len()];
|
segment_spaces = vec![None; segments.len()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -383,6 +383,10 @@ impl<'a, 'tcx> AstConv<'tcx> for ItemCtxt<'a, 'tcx> {
|
|||||||
{
|
{
|
||||||
self.tcx().mk_projection(trait_ref, item_name)
|
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
|
/// Interface used to find the bounds on a type parameter from within
|
||||||
|
20
src/test/compile-fail/cast-rfc0401-2.rs
Normal file
20
src/test/compile-fail/cast-rfc0401-2.rs
Normal file
@ -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
|
||||||
|
}
|
@ -58,7 +58,7 @@ fn main()
|
|||||||
let _ = f as *const u8;
|
let _ = f as *const u8;
|
||||||
//~^ ERROR casting
|
//~^ ERROR casting
|
||||||
//~^^ HELP through a usize first
|
//~^^ HELP through a usize first
|
||||||
let _ = 3 as bool;
|
let _ = 3_i32 as bool;
|
||||||
//~^ ERROR cannot cast as `bool`
|
//~^ ERROR cannot cast as `bool`
|
||||||
//~^^ HELP compare with zero
|
//~^^ HELP compare with zero
|
||||||
//~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
|
//~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
|
||||||
|
27
src/test/compile-fail/derived-errors/issue-31997.rs
Normal file
27
src/test/compile-fail/derived-errors/issue-31997.rs
Normal file
@ -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() { }
|
@ -31,7 +31,6 @@ macro_rules! write {
|
|||||||
|
|
||||||
macro_rules! cast {
|
macro_rules! cast {
|
||||||
($x:expr) => ($x as ())
|
($x:expr) => ($x as ())
|
||||||
//~^ ERROR non-scalar cast: `i32` as `()`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -40,5 +39,4 @@ fn main() {
|
|||||||
//~^ NOTE in this expansion of write!
|
//~^ NOTE in this expansion of write!
|
||||||
|
|
||||||
cast!(2);
|
cast!(2);
|
||||||
//~^ NOTE in this expansion of cast!
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user