Skip to content

Commit

Permalink
Make the existential type errors a little bit more helpful
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Feb 1, 2019
1 parent 984688a commit f2241f6
Show file tree
Hide file tree
Showing 7 changed files with 32 additions and 12 deletions.
30 changes: 25 additions & 5 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1355,10 +1355,9 @@ fn find_existential_constraints<'a, 'tcx>(
let mut index_map: FxHashMap<ty::ParamTy, usize> = FxHashMap::default();
// skip binder is ok, since we only use this to find generic parameters and their
// positions.
for subst in substs.iter() {
for (idx, subst) in substs.iter().enumerate() {
if let UnpackedKind::Type(ty) = subst.unpack() {
if let ty::Param(p) = ty.sty {
let idx = index_map.len();
if index_map.insert(p, idx).is_some() {
// there was already an entry for `p`, meaning a generic parameter
// was used twice
Expand Down Expand Up @@ -1391,20 +1390,24 @@ fn find_existential_constraints<'a, 'tcx>(
}).collect();
if let Some((prev_span, prev_ty, ref prev_indices)) = self.found {
let mut ty = concrete_type.walk().fuse();
let mut prev_ty = prev_ty.walk().fuse();
let iter_eq = (&mut ty).zip(&mut prev_ty).all(|(t, p)| match (&t.sty, &p.sty) {
let mut p_ty = prev_ty.walk().fuse();
let iter_eq = (&mut ty).zip(&mut p_ty).all(|(t, p)| match (&t.sty, &p.sty) {
// type parameters are equal to any other type parameter for the purpose of
// concrete type equality, as it is possible to obtain the same type just
// by passing matching parameters to a function.
(ty::Param(_), ty::Param(_)) => true,
_ => t == p,
});
if !iter_eq || ty.next().is_some() || prev_ty.next().is_some() {
if !iter_eq || ty.next().is_some() || p_ty.next().is_some() {
// found different concrete types for the existential type
let mut err = self.tcx.sess.struct_span_err(
span,
"concrete type differs from previous defining existential type use",
);
err.span_label(
span,
format!("expected `{}`, got `{}`", prev_ty, concrete_type),
);
err.span_note(prev_span, "previous use here");
err.emit();
} else if indices != *prev_indices {
Expand All @@ -1413,6 +1416,23 @@ fn find_existential_constraints<'a, 'tcx>(
span,
"concrete type's generic parameters differ from previous defining use",
);
use std::fmt::Write;
let mut s = String::new();
write!(s, "expected [").unwrap();
let list = |s: &mut String, indices: &Vec<usize>| {
let mut indices = indices.iter().cloned();
if let Some(first) = indices.next() {
write!(s, "`{}`", substs[first]).unwrap();
for i in indices {
write!(s, ", `{}`", substs[i]).unwrap();
}
}
};
list(&mut s, prev_indices);
write!(s, "], got [").unwrap();
list(&mut s, &indices);
write!(s, "]").unwrap();
err.span_label(span, s);
err.span_note(prev_span, "previous use here");
err.emit();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error: concrete type differs from previous defining existential type use
LL | / fn bar() -> Foo { //~ ERROR concrete type differs from previous
LL | | 42i32
LL | | }
| |_^
| |_^ expected `&'static str`, got `i32`
|
note: previous use here
--> $DIR/different_defining_uses.rs:8:1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error: concrete type differs from previous defining existential type use
LL | / fn bar() -> Foo { //~ ERROR concrete type differs from previous
LL | | panic!()
LL | | }
| |_^
| |_^ expected `&'static str`, got `()`
|
note: previous use here
--> $DIR/different_defining_uses_never_type.rs:8:1
Expand All @@ -20,7 +20,7 @@ error: concrete type differs from previous defining existential type use
LL | / fn boo() -> Foo { //~ ERROR concrete type differs from previous
LL | | loop {}
LL | | }
| |_^
| |_^ expected `&'static str`, got `()`
|
note: previous use here
--> $DIR/different_defining_uses_never_type.rs:8:1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error: concrete type differs from previous defining existential type use
LL | / fn my_iter2<T>(t: T) -> MyIter<T> { //~ ERROR concrete type differs from previous
LL | | Some(t).into_iter()
LL | | }
| |_^
| |_^ expected `std::iter::Once<T>`, got `std::option::IntoIter<T>`
|
note: previous use here
--> $DIR/generic_different_defining_uses.rs:7:1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ LL | / fn three<T, U: Debug>(_: T, u: U) -> Two<T, U> {
LL | | //~^ concrete type's generic parameters differ from previous defining use
LL | | u
LL | | }
| |_^
| |_^ expected [`T`], got [`U`]
|
note: previous use here
--> $DIR/generic_duplicate_param_use3.rs:15:1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ LL | / fn three<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> {
LL | | //~^ concrete type differs from previous
LL | | (u, t)
LL | | }
| |_^
| |_^ expected `(T, U)`, got `(U, T)`
|
note: previous use here
--> $DIR/generic_duplicate_param_use5.rs:10:1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ LL | / fn three<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> {
LL | | //~^ concrete type differs from previous
LL | | (u, t)
LL | | }
| |_^
| |_^ expected `(T, T)`, got `(U, T)`
|
note: previous use here
--> $DIR/generic_duplicate_param_use6.rs:10:1
Expand Down

0 comments on commit f2241f6

Please sign in to comment.