Skip to content

Commit 786b26d

Browse files
committed
Auto merge of #33491 - arielb1:obligation-jungle, r=nikomatsakis
Replace the obligation forest with a graph In the presence of caching, arbitrary nodes in the obligation forest can be merged, which makes it a general graph. Handle it as such, using cycle-detection algorithms in the processing. I should do performance measurements sometime. This was pretty much written as a proof-of-concept. Please help me write this in a less-ugly way. I should also add comments explaining what is going on. r? @nikomatsakis
2 parents cd6a400 + 65ad935 commit 786b26d

18 files changed

+794
-1038
lines changed

src/librustc/traits/error_reporting.rs

+43-284
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,53 @@ 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);
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;
283161

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())
309-
}
310-
},
311-
None => None,
162+
let trait_self_ty = tcx.erase_late_bound_regions(&trait_ref).self_ty();
163+
164+
if trait_self_ty.is_ty_var() {
165+
return None;
312166
}
313-
}
314167

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());
322-
}
168+
self.tcx.lookup_trait_def(trait_ref.def_id())
169+
.for_each_relevant_impl(self.tcx, trait_self_ty, |def_id| {
170+
let impl_self_ty = tcx
171+
.impl_trait_ref(def_id)
172+
.unwrap()
173+
.self_ty()
174+
.subst(tcx, &self.impl_substs(def_id, obligation.clone()));
175+
176+
if !tcx.has_attr(def_id, "rustc_on_unimplemented") {
177+
return;
178+
}
179+
180+
if let Ok(..) = self.can_equate(&trait_self_ty, &impl_self_ty) {
181+
ambiguous = result.is_some();
182+
result = Some(def_id);
183+
}
184+
});
185+
186+
if ambiguous {
187+
None
188+
} else {
189+
result
323190
}
324-
None
325191
}
326192

327193
fn on_unimplemented_note(&self,
328194
trait_ref: ty::PolyTraitRef<'tcx>,
329195
obligation: &PredicateObligation<'tcx>) -> Option<String> {
196+
let def_id = self.impl_with_self_type_of(trait_ref, obligation)
197+
.unwrap_or(trait_ref.def_id());
330198
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-
};
199+
341200
let span = obligation.cause.span;
342201
let mut report = None;
343202
for item in self.tcx.get_attrs(def_id).iter() {
@@ -511,115 +370,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
511370
/// that we can give a more helpful error message (and, in particular,
512371
/// we do not suggest increasing the overflow limit, which is not
513372
/// going to help).
514-
pub fn report_overflow_error_cycle(&self, cycle: &Vec<PredicateObligation<'tcx>>) -> ! {
515-
assert!(cycle.len() > 1);
516-
517-
debug!("report_overflow_error_cycle(cycle length = {})", cycle.len());
518-
519-
let cycle = self.resolve_type_vars_if_possible(cycle);
373+
pub fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
374+
let cycle = self.resolve_type_vars_if_possible(&cycle.to_owned());
375+
assert!(cycle.len() > 0);
520376

521377
debug!("report_overflow_error_cycle: cycle={:?}", cycle);
522378

523-
assert_eq!(&cycle[0].predicate, &cycle.last().unwrap().predicate);
524-
525-
self.try_report_overflow_error_type_of_infinite_size(&cycle);
526379
self.report_overflow_error(&cycle[0], false);
527380
}
528381

529-
/// If a cycle results from evaluated whether something is Sized, that
530-
/// is a particular special case that always results from a struct or
531-
/// enum definition that lacks indirection (e.g., `struct Foo { x: Foo
532-
/// }`). We wish to report a targeted error for this case.
533-
pub fn try_report_overflow_error_type_of_infinite_size(&self,
534-
cycle: &[PredicateObligation<'tcx>])
535-
{
536-
let sized_trait = match self.tcx.lang_items.sized_trait() {
537-
Some(v) => v,
538-
None => return,
539-
};
540-
let top_is_sized = {
541-
match cycle[0].predicate {
542-
ty::Predicate::Trait(ref data) => data.def_id() == sized_trait,
543-
_ => false,
544-
}
545-
};
546-
if !top_is_sized {
547-
return;
548-
}
549-
550-
// The only way to have a type of infinite size is to have,
551-
// somewhere, a struct/enum type involved. Identify all such types
552-
// and report the cycle to the user.
553-
554-
let struct_enum_tys: Vec<_> =
555-
cycle.iter()
556-
.flat_map(|obligation| match obligation.predicate {
557-
ty::Predicate::Trait(ref data) => {
558-
assert_eq!(data.def_id(), sized_trait);
559-
let self_ty = data.skip_binder().trait_ref.self_ty(); // (*)
560-
// (*) ok to skip binder because this is just
561-
// error reporting and regions don't really
562-
// matter
563-
match self_ty.sty {
564-
ty::TyEnum(..) | ty::TyStruct(..) => Some(self_ty),
565-
_ => None,
566-
}
567-
}
568-
_ => {
569-
span_bug!(obligation.cause.span,
570-
"Sized cycle involving non-trait-ref: {:?}",
571-
obligation.predicate);
572-
}
573-
})
574-
.collect();
575-
576-
assert!(!struct_enum_tys.is_empty());
577-
578-
// This is a bit tricky. We want to pick a "main type" in the
579-
// listing that is local to the current crate, so we can give a
580-
// good span to the user. But it might not be the first one in our
581-
// cycle list. So find the first one that is local and then
582-
// rotate.
583-
let (main_index, main_def_id) =
584-
struct_enum_tys.iter()
585-
.enumerate()
586-
.filter_map(|(index, ty)| match ty.sty {
587-
ty::TyEnum(adt_def, _) | ty::TyStruct(adt_def, _)
588-
if adt_def.did.is_local() =>
589-
Some((index, adt_def.did)),
590-
_ =>
591-
None,
592-
})
593-
.next()
594-
.unwrap(); // should always be SOME local type involved!
595-
596-
// Rotate so that the "main" type is at index 0.
597-
let struct_enum_tys: Vec<_> =
598-
struct_enum_tys.iter()
599-
.cloned()
600-
.skip(main_index)
601-
.chain(struct_enum_tys.iter().cloned().take(main_index))
602-
.collect();
603-
604-
let tcx = self.tcx;
605-
let mut err = tcx.recursive_type_with_infinite_size_error(main_def_id);
606-
let len = struct_enum_tys.len();
607-
if len > 2 {
608-
err.note(&format!("type `{}` is embedded within `{}`...",
609-
struct_enum_tys[0],
610-
struct_enum_tys[1]));
611-
for &next_ty in &struct_enum_tys[1..len-1] {
612-
err.note(&format!("...which in turn is embedded within `{}`...", next_ty));
613-
}
614-
err.note(&format!("...which in turn is embedded within `{}`, \
615-
completing the cycle.",
616-
struct_enum_tys[len-1]));
617-
}
618-
err.emit();
619-
self.tcx.sess.abort_if_errors();
620-
bug!();
621-
}
622-
623382
pub fn report_selection_error(&self,
624383
obligation: &PredicateObligation<'tcx>,
625384
error: &SelectionError<'tcx>,

0 commit comments

Comments
 (0)