Skip to content

Commit 5458d8b

Browse files
committed
rewrite fuzzy on_unimplemented matching to avoid ICEs
1 parent f0f5ef5 commit 5458d8b

File tree

2 files changed

+33
-245
lines changed

2 files changed

+33
-245
lines changed

src/librustc/traits/error_reporting.rs

+33-181
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ use super::{
2626

2727
use fmt_macros::{Parser, Piece, Position};
2828
use hir::def_id::DefId;
29-
use infer::{InferCtxt, TypeOrigin};
30-
use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVariants};
29+
use infer::{InferCtxt};
30+
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
3131
use ty::fast_reject;
3232
use ty::fold::TypeFolder;
33-
use ty::subst::{self, ParamSpace, Subst};
33+
use ty::subst::{self, Subst};
3434
use util::nodemap::{FnvHashMap, FnvHashSet};
3535

3636
use std::cmp;
@@ -61,128 +61,6 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> {
6161
}
6262
}
6363

64-
// Enum used to differentiate the "big" and "little" weights.
65-
enum Weight {
66-
Coarse,
67-
Precise,
68-
}
69-
70-
trait AssociatedWeight {
71-
fn get_weight(&self) -> (u32, u32);
72-
}
73-
74-
impl<'a> AssociatedWeight for TypeVariants<'a> {
75-
// Left number is for "global"/"big" weight and right number is for better precision.
76-
fn get_weight(&self) -> (u32, u32) {
77-
match *self {
78-
TypeVariants::TyBool => (1, 1),
79-
TypeVariants::TyChar => (1, 2),
80-
TypeVariants::TyStr => (1, 3),
81-
82-
TypeVariants::TyInt(_) => (2, 1),
83-
TypeVariants::TyUint(_) => (2, 2),
84-
TypeVariants::TyFloat(_) => (2, 3),
85-
TypeVariants::TyRawPtr(_) => (2, 4),
86-
87-
TypeVariants::TyEnum(_, _) => (3, 1),
88-
TypeVariants::TyStruct(_, _) => (3, 2),
89-
TypeVariants::TyBox(_) => (3, 3),
90-
TypeVariants::TyTuple(_) => (3, 4),
91-
92-
TypeVariants::TyArray(_, _) => (4, 1),
93-
TypeVariants::TySlice(_) => (4, 2),
94-
95-
TypeVariants::TyRef(_, _) => (5, 1),
96-
TypeVariants::TyFnDef(_, _, _) => (5, 2),
97-
TypeVariants::TyFnPtr(_) => (5, 3),
98-
99-
TypeVariants::TyTrait(_) => (6, 1),
100-
101-
TypeVariants::TyClosure(_, _) => (7, 1),
102-
103-
TypeVariants::TyProjection(_) => (8, 1),
104-
TypeVariants::TyParam(_) => (8, 2),
105-
TypeVariants::TyInfer(_) => (8, 3),
106-
107-
TypeVariants::TyError => (9, 1),
108-
}
109-
}
110-
}
111-
112-
// The "closer" the types are, the lesser the weight.
113-
fn get_weight_diff(a: &ty::TypeVariants, b: &TypeVariants, weight: Weight) -> u32 {
114-
let (w1, w2) = match weight {
115-
Weight::Coarse => (a.get_weight().0, b.get_weight().0),
116-
Weight::Precise => (a.get_weight().1, b.get_weight().1),
117-
};
118-
if w1 < w2 {
119-
w2 - w1
120-
} else {
121-
w1 - w2
122-
}
123-
}
124-
125-
// Once we have "globally matching" types, we need to run another filter on them.
126-
//
127-
// In the function `get_best_matching_type`, we got the types which might fit the
128-
// most to the type we're looking for. This second filter now intends to get (if
129-
// possible) the type which fits the most.
130-
//
131-
// For example, the trait expects an `usize` and here you have `u32` and `i32`.
132-
// Obviously, the "correct" one is `u32`.
133-
fn filter_matching_types<'tcx>(weights: &[(usize, u32)],
134-
imps: &[(DefId, subst::Substs<'tcx>)],
135-
trait_types: &[ty::Ty<'tcx>])
136-
-> usize {
137-
let matching_weight = weights[0].1;
138-
let iter = weights.iter().filter(|&&(_, weight)| weight == matching_weight);
139-
let mut filtered_weights = vec!();
140-
141-
for &(pos, _) in iter {
142-
let mut weight = 0;
143-
for (type_to_compare, original_type) in imps[pos].1
144-
.types
145-
.get_slice(ParamSpace::TypeSpace)
146-
.iter()
147-
.zip(trait_types.iter()) {
148-
weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, Weight::Precise);
149-
}
150-
filtered_weights.push((pos, weight));
151-
}
152-
filtered_weights.sort_by(|a, b| a.1.cmp(&b.1));
153-
filtered_weights[0].0
154-
}
155-
156-
// Here, we run the "big" filter. Little example:
157-
//
158-
// We receive a `String`, an `u32` and an `i32`.
159-
// The trait expected an `usize`.
160-
// From human point of view, it's easy to determine that `String` doesn't correspond to
161-
// the expected type at all whereas `u32` and `i32` could.
162-
//
163-
// This first filter intends to only keep the types which match the most.
164-
fn get_best_matching_type<'tcx>(imps: &[(DefId, subst::Substs<'tcx>)],
165-
trait_types: &[ty::Ty<'tcx>]) -> usize {
166-
let mut weights = vec!();
167-
for (pos, imp) in imps.iter().enumerate() {
168-
let mut weight = 0;
169-
for (type_to_compare, original_type) in imp.1
170-
.types
171-
.get_slice(ParamSpace::TypeSpace)
172-
.iter()
173-
.zip(trait_types.iter()) {
174-
weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, Weight::Coarse);
175-
}
176-
weights.push((pos, weight));
177-
}
178-
weights.sort_by(|a, b| a.1.cmp(&b.1));
179-
if weights[0].1 == weights[1].1 {
180-
filter_matching_types(&weights, &imps, trait_types)
181-
} else {
182-
weights[0].0
183-
}
184-
}
185-
18664
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
18765
pub fn report_fulfillment_errors(&self, errors: &Vec<FulfillmentError<'tcx>>) {
18866
for error in errors {
@@ -272,72 +150,46 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
272150
substs
273151
}
274152

275-
fn get_current_failing_impl(&self,
276-
trait_ref: &TraitRef<'tcx>,
277-
obligation: &PredicateObligation<'tcx>)
278-
-> Option<(DefId, subst::Substs<'tcx>)> {
279-
let simp = fast_reject::simplify_type(self.tcx,
280-
trait_ref.self_ty(),
281-
true);
282-
let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id);
283-
284-
match simp {
285-
Some(_) => {
286-
let mut matching_impls = Vec::new();
287-
trait_def.for_each_impl(self.tcx, |def_id| {
288-
let imp = self.tcx.impl_trait_ref(def_id).unwrap();
289-
let substs = self.impl_substs(def_id, obligation.clone());
290-
let imp = imp.subst(self.tcx, &substs);
291-
292-
if self.eq_types(true,
293-
TypeOrigin::Misc(obligation.cause.span),
294-
trait_ref.self_ty(),
295-
imp.self_ty()).is_ok() {
296-
matching_impls.push((def_id, imp.substs.clone()));
297-
}
298-
});
299-
if matching_impls.len() == 0 {
300-
None
301-
} else if matching_impls.len() == 1 {
302-
Some(matching_impls[0].clone())
303-
} else {
304-
let end = trait_ref.input_types().len() - 1;
305-
// we need to determine which type is the good one!
306-
Some(matching_impls[get_best_matching_type(&matching_impls,
307-
&trait_ref.input_types()[0..end])]
308-
.clone())
153+
fn impl_with_self_type_of(&self,
154+
trait_ref: ty::PolyTraitRef<'tcx>,
155+
obligation: &PredicateObligation<'tcx>)
156+
-> Option<DefId>
157+
{
158+
let tcx = self.tcx;
159+
let mut result = None;
160+
let mut ambiguous = false;
161+
162+
let trait_self_ty = tcx.erase_late_bound_regions(&trait_ref).self_ty();
163+
164+
self.tcx.lookup_trait_def(trait_ref.def_id())
165+
.for_each_relevant_impl(self.tcx, trait_self_ty, |def_id| {
166+
let impl_self_ty = tcx
167+
.impl_trait_ref(def_id)
168+
.unwrap()
169+
.self_ty()
170+
.subst(tcx, &self.impl_substs(def_id, obligation.clone()));
171+
172+
if let Ok(..) = self.can_equate(&trait_self_ty, &impl_self_ty) {
173+
ambiguous = result.is_some();
174+
result = Some(def_id);
309175
}
310-
},
311-
None => None,
312-
}
313-
}
176+
});
314177

315-
fn find_attr(&self,
316-
def_id: DefId,
317-
attr_name: &str)
318-
-> Option<ast::Attribute> {
319-
for item in self.tcx.get_attrs(def_id).iter() {
320-
if item.check_name(attr_name) {
321-
return Some(item.clone());
178+
match result {
179+
Some(def_id) if !ambiguous && tcx.has_attr(def_id, "rustc_on_unimplemented") => {
180+
result
322181
}
182+
_ => None
323183
}
324-
None
325184
}
326185

327186
fn on_unimplemented_note(&self,
328187
trait_ref: ty::PolyTraitRef<'tcx>,
329188
obligation: &PredicateObligation<'tcx>) -> Option<String> {
189+
let def_id = self.impl_with_self_type_of(trait_ref, obligation)
190+
.unwrap_or(trait_ref.def_id());
330191
let trait_ref = trait_ref.skip_binder();
331-
let def_id = match self.get_current_failing_impl(trait_ref, obligation) {
332-
Some((def_id, _)) => {
333-
if let Some(_) = self.find_attr(def_id, "rustc_on_unimplemented") {
334-
def_id
335-
} else {
336-
trait_ref.def_id
337-
}
338-
},
339-
None => trait_ref.def_id,
340-
};
192+
341193
let span = obligation.cause.span;
342194
let mut report = None;
343195
for item in self.tcx.get_attrs(def_id).iter() {

src/test/compile-fail/on_unimplemented.rs

-64
This file was deleted.

0 commit comments

Comments
 (0)