提交 ccaa2f85 编写于 作者: N Niko Matsakis

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.
上级 93300061
......@@ -35,7 +35,7 @@
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 @@ pub fn adjust_expr_ty(&self,
.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 @@ pub fn resolve_regions_and_report_errors(&self,
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
......
......@@ -91,6 +91,7 @@ fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
}
(&ty::TyError, _) | (_, &ty::TyError) => {
infcx.set_tainted_by_errors();
Ok(self.tcx().types.err)
}
......
......@@ -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();
......
......@@ -154,6 +154,12 @@ fn projected_ty(&self,
_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;
}
_ => {
......
......@@ -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 {
......
......@@ -121,8 +121,27 @@ pub fn new(expr: &'tcx hir::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Sp
}
}
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 |
......
......@@ -1240,6 +1240,10 @@ fn projected_ty(&self,
{
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 @@ fn check_casts(&self) {
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_struct<'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_struct<'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()];
}
}
......
......@@ -383,6 +383,10 @@ fn projected_ty(&self,
{
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
......
// 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;
//~^ 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
......
// 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 @@ fn size_of<T>(_: T) -> usize {
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!
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册