Skip to content

Commit fbb1a0b

Browse files
committed
Rollup merge of rust-lang#32258 - nikomatsakis:fewer-errors, r=arielb1
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. Fixes rust-lang#31997 cc @alexcrichton r? @arielb1
2 parents db8e996 + 89bbd2c commit fbb1a0b

20 files changed

+349
-99
lines changed

src/librustc/infer/mod.rs

+46-5
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use ty::fold::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

+1
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

+6
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,12 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
624624
predicate,
625625
obligation);
626626

627+
// Ambiguity errors are often caused as fallout from earlier
628+
// errors. So just ignore them if this infcx is tainted.
629+
if infcx.is_tainted_by_errors() {
630+
return;
631+
}
632+
627633
match predicate {
628634
ty::Predicate::Trait(ref data) => {
629635
let trait_ref = data.to_poly_trait_ref();

src/librustc_typeck/astconv.rs

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

160166
pub fn ast_region_to_region(tcx: &TyCtxt, lifetime: &hir::Lifetime)
@@ -1533,6 +1539,7 @@ fn base_def_to_ty<'tcx>(this: &AstConv<'tcx>,
15331539
prim_ty_to_ty(tcx, base_segments, prim_ty)
15341540
}
15351541
Def::Err => {
1542+
this.set_tainted_by_errors();
15361543
return this.tcx().types.err;
15371544
}
15381545
_ => {

src/librustc_typeck/check/_match.rs

+2
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

+101-8
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,21 @@ use super::structurally_resolved_type;
4545

4646
use lint;
4747
use hir::def_id::DefId;
48+
use rustc::hir;
49+
use rustc::traits;
4850
use rustc::ty::{self, Ty, TypeFoldable};
4951
use rustc::ty::cast::{CastKind, CastTy};
50-
use syntax::codemap::Span;
51-
use rustc::hir;
5252
use syntax::ast;
53-
53+
use syntax::codemap::Span;
54+
use util::common::ErrorReported;
5455

5556
/// Reifies a cast check to be checked once we have full type information for
5657
/// a function context.
5758
pub struct CastCheck<'tcx> {
5859
expr: &'tcx hir::Expr,
5960
expr_ty: Ty<'tcx>,
6061
cast_ty: Ty<'tcx>,
62+
cast_span: Span,
6163
span: Span,
6264
}
6365

@@ -111,17 +113,37 @@ enum CastError {
111113
}
112114

113115
impl<'tcx> CastCheck<'tcx> {
114-
pub fn new(expr: &'tcx hir::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Span)
115-
-> CastCheck<'tcx> {
116-
CastCheck {
116+
pub fn new<'a>(fcx: &FnCtxt<'a, 'tcx>,
117+
expr: &'tcx hir::Expr,
118+
expr_ty: Ty<'tcx>,
119+
cast_ty: Ty<'tcx>,
120+
cast_span: Span,
121+
span: Span)
122+
-> Result<CastCheck<'tcx>, ErrorReported> {
123+
let check = CastCheck {
117124
expr: expr,
118125
expr_ty: expr_ty,
119126
cast_ty: cast_ty,
127+
cast_span: cast_span,
120128
span: span,
129+
};
130+
131+
// For better error messages, check for some obviously unsized
132+
// cases now. We do a more thorough check at the end, once
133+
// inference is more completely known.
134+
match cast_ty.sty {
135+
ty::TyTrait(..) | ty::TySlice(..) => {
136+
check.report_cast_to_unsized_type(fcx);
137+
Err(ErrorReported)
138+
}
139+
_ => {
140+
Ok(check)
141+
}
121142
}
122143
}
123144

124-
fn report_cast_error<'a>(&self, fcx: &FnCtxt<'a, 'tcx>,
145+
fn report_cast_error<'a>(&self,
146+
fcx: &FnCtxt<'a, 'tcx>,
125147
e: CastError) {
126148
match e {
127149
CastError::NeedViaPtr |
@@ -186,6 +208,61 @@ impl<'tcx> CastCheck<'tcx> {
186208
}
187209
}
188210

211+
fn report_cast_to_unsized_type<'a>(&self,
212+
fcx: &FnCtxt<'a, 'tcx>) {
213+
if
214+
self.cast_ty.references_error() ||
215+
self.expr_ty.references_error()
216+
{
217+
return;
218+
}
219+
220+
let tstr = fcx.infcx().ty_to_string(self.cast_ty);
221+
let mut err = fcx.type_error_struct(self.span, |actual| {
222+
format!("cast to unsized type: `{}` as `{}`", actual, tstr)
223+
}, self.expr_ty, None);
224+
match self.expr_ty.sty {
225+
ty::TyRef(_, ty::TypeAndMut { mutbl: mt, .. }) => {
226+
let mtstr = match mt {
227+
hir::MutMutable => "mut ",
228+
hir::MutImmutable => ""
229+
};
230+
if self.cast_ty.is_trait() {
231+
match fcx.tcx().sess.codemap().span_to_snippet(self.cast_span) {
232+
Ok(s) => {
233+
err.span_suggestion(self.cast_span,
234+
"try casting to a reference instead:",
235+
format!("&{}{}", mtstr, s));
236+
},
237+
Err(_) =>
238+
span_help!(err, self.cast_span,
239+
"did you mean `&{}{}`?", mtstr, tstr),
240+
}
241+
} else {
242+
span_help!(err, self.span,
243+
"consider using an implicit coercion to `&{}{}` instead",
244+
mtstr, tstr);
245+
}
246+
}
247+
ty::TyBox(..) => {
248+
match fcx.tcx().sess.codemap().span_to_snippet(self.cast_span) {
249+
Ok(s) => {
250+
err.span_suggestion(self.cast_span,
251+
"try casting to a `Box` instead:",
252+
format!("Box<{}>", s));
253+
},
254+
Err(_) =>
255+
span_help!(err, self.cast_span, "did you mean `Box<{}>`?", tstr),
256+
}
257+
}
258+
_ => {
259+
span_help!(err, self.expr.span,
260+
"consider using a box or reference as appropriate");
261+
}
262+
}
263+
err.emit();
264+
}
265+
189266
fn trivial_cast_lint<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) {
190267
let t_cast = self.cast_ty;
191268
let t_expr = self.expr_ty;
@@ -218,7 +295,9 @@ impl<'tcx> CastCheck<'tcx> {
218295
debug!("check_cast({}, {:?} as {:?})", self.expr.id, self.expr_ty,
219296
self.cast_ty);
220297

221-
if self.expr_ty.references_error() || self.cast_ty.references_error() {
298+
if !fcx.type_is_known_to_be_sized(self.cast_ty, self.span) {
299+
self.report_cast_to_unsized_type(fcx);
300+
} else if self.expr_ty.references_error() || self.cast_ty.references_error() {
222301
// No sense in giving duplicate error messages
223302
} else if self.try_coercion_cast(fcx) {
224303
self.trivial_cast_lint(fcx);
@@ -403,3 +482,17 @@ impl<'tcx> CastCheck<'tcx> {
403482
}
404483

405484
}
485+
486+
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
487+
fn type_is_known_to_be_sized(&self,
488+
ty: Ty<'tcx>,
489+
span: Span)
490+
-> bool
491+
{
492+
traits::type_known_to_meet_builtin_bound(self.infcx(),
493+
ty,
494+
ty::BoundSized,
495+
span)
496+
}
497+
}
498+

0 commit comments

Comments
 (0)