Skip to content

Commit fe6ad09

Browse files
Ariel Ben-Yehudaarielb1
authored andcommitted
deduplicate trait errors before they are displayed
Because of type inference, duplicate obligations exist and cause duplicate errors. To avoid this, only display the first error for each (predicate,span). The inclusion of the span is somewhat bikesheddy, but *is* the more conservative option (it does not remove some instability, as duplicate obligations are ignored by `duplicate_set` under some inference conditions). Fixes #28098 cc #21528 (is it a dupe?)
1 parent 9169e6c commit fe6ad09

26 files changed

+77
-40
lines changed

src/librustc/middle/infer/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use std::rc::Rc;
4343
use syntax::ast;
4444
use syntax::codemap;
4545
use syntax::codemap::{Span, DUMMY_SP};
46-
use util::nodemap::{FnvHashMap, NodeMap};
46+
use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap};
4747

4848
use self::combine::CombineFields;
4949
use self::region_inference::{RegionVarBindings, RegionSnapshot};
@@ -92,6 +92,10 @@ pub struct InferCtxt<'a, 'tcx: 'a> {
9292

9393
pub fulfillment_cx: RefCell<traits::FulfillmentContext<'tcx>>,
9494

95+
// the set of predicates on which errors have been reported, to
96+
// avoid reporting the same error twice.
97+
pub reported_trait_errors: RefCell<FnvHashSet<traits::TraitErrorKey<'tcx>>>,
98+
9599
// This is a temporary field used for toggling on normalization in the inference context,
96100
// as we move towards the approach described here:
97101
// https://internals.rust-lang.org/t/flattening-the-contexts-for-fun-and-profit/2293
@@ -374,6 +378,7 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>,
374378
region_vars: RegionVarBindings::new(tcx),
375379
parameter_environment: param_env.unwrap_or(tcx.empty_parameter_environment()),
376380
fulfillment_cx: RefCell::new(traits::FulfillmentContext::new(errors_will_be_reported)),
381+
reported_trait_errors: RefCell::new(FnvHashSet()),
377382
normalize: false,
378383
err_count_on_creation: tcx.sess.err_count()
379384
}

src/librustc/middle/traits/error_reporting.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,26 @@ use std::fmt;
3333
use syntax::codemap::Span;
3434
use syntax::attr::{AttributeMethods, AttrMetaMethods};
3535

36+
#[derive(Debug, PartialEq, Eq, Hash)]
37+
pub struct TraitErrorKey<'tcx> {
38+
is_warning: bool,
39+
span: Span,
40+
predicate: ty::Predicate<'tcx>
41+
}
42+
43+
impl<'tcx> TraitErrorKey<'tcx> {
44+
fn from_error<'a>(infcx: &InferCtxt<'a, 'tcx>,
45+
e: &FulfillmentError<'tcx>) -> Self {
46+
let predicate =
47+
infcx.resolve_type_vars_if_possible(&e.obligation.predicate);
48+
TraitErrorKey {
49+
is_warning: is_warning(&e.obligation),
50+
span: e.obligation.cause.span,
51+
predicate: infcx.tcx.erase_regions(&predicate)
52+
}
53+
}
54+
}
55+
3656
pub fn report_fulfillment_errors<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
3757
errors: &Vec<FulfillmentError<'tcx>>) {
3858
for error in errors {
@@ -42,6 +62,13 @@ pub fn report_fulfillment_errors<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
4262

4363
fn report_fulfillment_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
4464
error: &FulfillmentError<'tcx>) {
65+
let error_key = TraitErrorKey::from_error(infcx, error);
66+
debug!("report_fulfillment_errors({:?}) - key={:?}",
67+
error, error_key);
68+
if !infcx.reported_trait_errors.borrow_mut().insert(error_key) {
69+
debug!("report_fulfillment_errors: skipping duplicate");
70+
return;
71+
}
4572
match error.code {
4673
FulfillmentErrorCode::CodeSelectionError(ref e) => {
4774
report_selection_error(infcx, &error.obligation, e);

src/librustc/middle/traits/fulfill.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ pub struct FulfillmentContext<'tcx> {
4949
// than the `SelectionCache`: it avoids duplicate errors and
5050
// permits recursive obligations, which are often generated from
5151
// traits like `Send` et al.
52+
//
53+
// Note that because of type inference, a predicate can still
54+
// occur twice in the predicates list, for example when 2
55+
// initially-distinct type variables are unified after being
56+
// inserted. Deduplicating the predicate set on selection had a
57+
// significant performance cost the last time I checked.
5258
duplicate_set: FulfilledPredicates<'tcx>,
5359

5460
// A list of all obligations that have been registered with this

src/librustc/middle/traits/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ use middle::subst;
2121
use middle::ty::{self, HasTypeFlags, Ty};
2222
use middle::ty::fold::TypeFoldable;
2323
use middle::infer::{self, fixup_err_to_string, InferCtxt};
24+
2425
use std::rc::Rc;
2526
use syntax::ast;
2627
use syntax::codemap::{Span, DUMMY_SP};
2728

29+
pub use self::error_reporting::TraitErrorKey;
2830
pub use self::error_reporting::report_fulfillment_errors;
2931
pub use self::error_reporting::report_overflow_error;
3032
pub use self::error_reporting::report_selection_error;

src/test/compile-fail/associated-types-ICE-when-projecting-out-of-err.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,4 @@ fn ice<A>(a: A) {
3232
let r = loop {};
3333
r = r + a;
3434
//~^ ERROR not implemented
35-
//~| ERROR not implemented
3635
}

src/test/compile-fail/associated-types-path-2.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ pub fn f1_int_uint() {
3939
pub fn f1_uint_uint() {
4040
f1(2u32, 4u32);
4141
//~^ ERROR the trait `Foo` is not implemented
42-
//~| ERROR the trait `Foo` is not implemented
4342
}
4443

4544
pub fn f1_uint_int() {

src/test/compile-fail/coerce-unsafe-to-closure.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@
1111
fn main() {
1212
let x: Option<&[u8]> = Some("foo").map(std::mem::transmute);
1313
//~^ ERROR E0277
14-
//~| ERROR E0277
1514
}

src/test/compile-fail/const-eval-overflow-4b.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ const A_I8_T
2323
: [u32; (i8::MAX as i8 + 1u8) as usize]
2424
//~^ ERROR mismatched types
2525
//~| the trait `core::ops::Add<u8>` is not implemented for the type `i8`
26-
//~| the trait `core::ops::Add<u8>` is not implemented for the type `i8`
2726
= [0; (i8::MAX as usize) + 1];
2827

2928
fn main() {
@@ -33,4 +32,3 @@ fn main() {
3332
fn foo<T:fmt::Debug>(x: T) {
3433
println!("{:?}", x);
3534
}
36-

src/test/compile-fail/fn-variance-1.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@ fn main() {
2020
apply(&3, takes_imm);
2121
apply(&3, takes_mut);
2222
//~^ ERROR (values differ in mutability)
23-
//~| ERROR (values differ in mutability)
2423

2524
apply(&mut 3, takes_mut);
2625
apply(&mut 3, takes_imm);
2726
//~^ ERROR (values differ in mutability)
28-
//~| ERROR (values differ in mutability)
2927
}

src/test/compile-fail/for-loop-bogosity.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ pub fn main() {
2525
y: 2,
2626
};
2727
for x in bogus { //~ ERROR `core::iter::Iterator` is not implemented for the type `MyStruct`
28-
//~^ ERROR
29-
//~^^ ERROR
30-
// FIXME(#21528) not fulfilled obligation error should be reported once, not thrice
3128
drop(x);
3229
}
3330
}

0 commit comments

Comments
 (0)