Skip to content

Commit 6cb425d

Browse files
committed
Rework normalization so that it works recursively, since the types extracted from an impl are potentially in need of normalization. This also lays groundwork for further cleanup in other areas by disconnecting normalization from the fulfillment context.
1 parent 0aa7ba9 commit 6cb425d

13 files changed

+575
-375
lines changed

src/librustc/middle/traits/error_reporting.rs

+69-63
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
7777
"overflow evaluating the requirement `{}`",
7878
predicate.user_string(infcx.tcx)).as_slice());
7979

80-
let current_limit = infcx.tcx.sess.recursion_limit.get();
81-
let suggested_limit = current_limit * 2;
82-
infcx.tcx.sess.span_note(
83-
obligation.cause.span,
84-
format!(
85-
"consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
86-
suggested_limit)[]);
80+
suggest_new_overflow_limit(infcx, obligation.cause.span);
8781

8882
note_obligation_cause(infcx, obligation);
8983
}
@@ -165,73 +159,76 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
165159
// ambiguous impls. The latter *ought* to be a
166160
// coherence violation, so we don't report it here.
167161

168-
let trait_ref = match obligation.predicate {
169-
ty::Predicate::Trait(ref trait_predicate) => {
170-
infcx.resolve_type_vars_if_possible(
171-
&trait_predicate.to_poly_trait_ref())
172-
}
173-
_ => {
174-
infcx.tcx.sess.span_bug(
175-
obligation.cause.span,
176-
format!("ambiguity from something other than a trait: {}",
177-
obligation.predicate.repr(infcx.tcx)).as_slice());
178-
}
179-
};
180-
181-
let self_ty = trait_ref.self_ty();
162+
let predicate = infcx.resolve_type_vars_if_possible(&obligation.predicate);
182163

183-
debug!("maybe_report_ambiguity(trait_ref={}, self_ty={}, obligation={})",
184-
trait_ref.repr(infcx.tcx),
185-
self_ty.repr(infcx.tcx),
164+
debug!("maybe_report_ambiguity(predicate={}, obligation={})",
165+
predicate.repr(infcx.tcx),
186166
obligation.repr(infcx.tcx));
187-
let all_types = &trait_ref.substs().types;
188-
if all_types.iter().any(|&t| ty::type_is_error(t)) {
189-
} else if all_types.iter().any(|&t| ty::type_needs_infer(t)) {
190-
// This is kind of a hack: it frequently happens that some earlier
191-
// error prevents types from being fully inferred, and then we get
192-
// a bunch of uninteresting errors saying something like "<generic
193-
// #0> doesn't implement Sized". It may even be true that we
194-
// could just skip over all checks where the self-ty is an
195-
// inference variable, but I was afraid that there might be an
196-
// inference variable created, registered as an obligation, and
197-
// then never forced by writeback, and hence by skipping here we'd
198-
// be ignoring the fact that we don't KNOW the type works
199-
// out. Though even that would probably be harmless, given that
200-
// we're only talking about builtin traits, which are known to be
201-
// inhabited. But in any case I just threw in this check for
202-
// has_errors() to be sure that compilation isn't happening
203-
// anyway. In that case, why inundate the user.
204-
if !infcx.tcx.sess.has_errors() {
205-
if infcx.tcx.lang_items.sized_trait()
206-
.map_or(false, |sized_id| sized_id == trait_ref.def_id()) {
207-
infcx.tcx.sess.span_err(
167+
168+
match predicate {
169+
ty::Predicate::Trait(ref data) => {
170+
let trait_ref = data.to_poly_trait_ref();
171+
let self_ty = trait_ref.self_ty();
172+
let all_types = &trait_ref.substs().types;
173+
if all_types.iter().any(|&t| ty::type_is_error(t)) {
174+
} else if all_types.iter().any(|&t| ty::type_needs_infer(t)) {
175+
// This is kind of a hack: it frequently happens that some earlier
176+
// error prevents types from being fully inferred, and then we get
177+
// a bunch of uninteresting errors saying something like "<generic
178+
// #0> doesn't implement Sized". It may even be true that we
179+
// could just skip over all checks where the self-ty is an
180+
// inference variable, but I was afraid that there might be an
181+
// inference variable created, registered as an obligation, and
182+
// then never forced by writeback, and hence by skipping here we'd
183+
// be ignoring the fact that we don't KNOW the type works
184+
// out. Though even that would probably be harmless, given that
185+
// we're only talking about builtin traits, which are known to be
186+
// inhabited. But in any case I just threw in this check for
187+
// has_errors() to be sure that compilation isn't happening
188+
// anyway. In that case, why inundate the user.
189+
if !infcx.tcx.sess.has_errors() {
190+
if
191+
infcx.tcx.lang_items.sized_trait()
192+
.map_or(false, |sized_id| sized_id == trait_ref.def_id())
193+
{
194+
infcx.tcx.sess.span_err(
195+
obligation.cause.span,
196+
format!(
197+
"unable to infer enough type information about `{}`; \
198+
type annotations required",
199+
self_ty.user_string(infcx.tcx)).as_slice());
200+
} else {
201+
infcx.tcx.sess.span_err(
202+
obligation.cause.span,
203+
format!(
204+
"type annotations required: cannot resolve `{}`",
205+
predicate.user_string(infcx.tcx)).as_slice());
206+
note_obligation_cause(infcx, obligation);
207+
}
208+
}
209+
} else if !infcx.tcx.sess.has_errors() {
210+
// Ambiguity. Coherence should have reported an error.
211+
infcx.tcx.sess.span_bug(
208212
obligation.cause.span,
209213
format!(
210-
"unable to infer enough type information about `{}`; type annotations \
211-
required",
214+
"coherence failed to report ambiguity: \
215+
cannot locate the impl of the trait `{}` for \
216+
the type `{}`",
217+
trait_ref.user_string(infcx.tcx),
212218
self_ty.user_string(infcx.tcx)).as_slice());
213-
} else {
219+
}
220+
}
221+
222+
_ => {
223+
if !infcx.tcx.sess.has_errors() {
214224
infcx.tcx.sess.span_err(
215225
obligation.cause.span,
216226
format!(
217-
"unable to infer enough type information to \
218-
locate the impl of the trait `{}` for \
219-
the type `{}`; type annotations required",
220-
trait_ref.user_string(infcx.tcx),
221-
self_ty.user_string(infcx.tcx)).as_slice());
227+
"type annotations required: cannot resolve `{}`",
228+
predicate.user_string(infcx.tcx)).as_slice());
222229
note_obligation_cause(infcx, obligation);
223230
}
224231
}
225-
} else if !infcx.tcx.sess.has_errors() {
226-
// Ambiguity. Coherence should have reported an error.
227-
infcx.tcx.sess.span_bug(
228-
obligation.cause.span,
229-
format!(
230-
"coherence failed to report ambiguity: \
231-
cannot locate the impl of the trait `{}` for \
232-
the type `{}`",
233-
trait_ref.user_string(infcx.tcx),
234-
self_ty.user_string(infcx.tcx)).as_slice());
235232
}
236233
}
237234

@@ -335,3 +332,12 @@ fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
335332
}
336333
}
337334

335+
pub fn suggest_new_overflow_limit(infcx: &InferCtxt, span: Span) {
336+
let current_limit = infcx.tcx.sess.recursion_limit.get();
337+
let suggested_limit = current_limit * 2;
338+
infcx.tcx.sess.span_note(
339+
span,
340+
format!(
341+
"consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
342+
suggested_limit)[]);
343+
}

src/librustc/middle/traits/fulfill.rs

+26-109
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use middle::infer::{mod, InferCtxt};
11+
use middle::infer::{InferCtxt};
1212
use middle::mem_categorization::Typer;
13-
use middle::ty::{mod, AsPredicate, RegionEscape, Ty, ToPolyTraitRef};
13+
use middle::ty::{mod, RegionEscape, Ty};
1414
use std::collections::HashSet;
1515
use std::collections::hash_map::Entry::{Occupied, Vacant};
1616
use std::default::Default;
@@ -23,7 +23,6 @@ use super::CodeAmbiguity;
2323
use super::CodeProjectionError;
2424
use super::CodeSelectionError;
2525
use super::FulfillmentError;
26-
use super::Obligation;
2726
use super::ObligationCause;
2827
use super::PredicateObligation;
2928
use super::project;
@@ -110,6 +109,8 @@ impl<'tcx> FulfillmentContext<'tcx> {
110109
/// `projection_ty` again.
111110
pub fn normalize_projection_type<'a>(&mut self,
112111
infcx: &InferCtxt<'a,'tcx>,
112+
param_env: &ty::ParameterEnvironment<'tcx>,
113+
typer: &Typer<'tcx>,
113114
projection_ty: ty::ProjectionTy<'tcx>,
114115
cause: ObligationCause<'tcx>)
115116
-> Ty<'tcx>
@@ -121,18 +122,16 @@ impl<'tcx> FulfillmentContext<'tcx> {
121122

122123
// FIXME(#20304) -- cache
123124

124-
let ty_var = infcx.next_ty_var();
125-
let projection =
126-
ty::Binder(ty::ProjectionPredicate {
127-
projection_ty: projection_ty,
128-
ty: ty_var
129-
});
130-
let obligation = Obligation::new(cause, projection.as_predicate());
131-
self.register_predicate(infcx, obligation);
125+
let mut selcx = SelectionContext::new(infcx, param_env, typer);
126+
let normalized = project::normalize_projection_type(&mut selcx, projection_ty, cause, 0);
127+
128+
for obligation in normalized.obligations.into_iter() {
129+
self.register_predicate_obligation(infcx, obligation);
130+
}
132131

133-
debug!("normalize_associated_type: result={}", ty_var.repr(infcx.tcx));
132+
debug!("normalize_associated_type: result={}", normalized.value.repr(infcx.tcx));
134133

135-
ty_var
134+
normalized.value
136135
}
137136

138137
pub fn register_builtin_bound<'a>(&mut self,
@@ -143,7 +142,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
143142
{
144143
match predicate_for_builtin_bound(infcx.tcx, cause, builtin_bound, 0, ty) {
145144
Ok(predicate) => {
146-
self.register_predicate(infcx, predicate);
145+
self.register_predicate_obligation(infcx, predicate);
147146
}
148147
Err(ErrorReported) => { }
149148
}
@@ -158,10 +157,14 @@ impl<'tcx> FulfillmentContext<'tcx> {
158157
register_region_obligation(infcx.tcx, t_a, r_b, cause, &mut self.region_obligations);
159158
}
160159

161-
pub fn register_predicate<'a>(&mut self,
162-
infcx: &InferCtxt<'a,'tcx>,
163-
obligation: PredicateObligation<'tcx>)
160+
pub fn register_predicate_obligation<'a>(&mut self,
161+
infcx: &InferCtxt<'a,'tcx>,
162+
obligation: PredicateObligation<'tcx>)
164163
{
164+
// this helps to reduce duplicate errors, as well as making
165+
// debug output much nicer to read and so on.
166+
let obligation = infcx.resolve_type_vars_if_possible(&obligation);
167+
165168
if !self.duplicate_set.insert(obligation.predicate.clone()) {
166169
debug!("register_predicate({}) -- already seen, skip", obligation.repr(infcx.tcx));
167170
return;
@@ -290,7 +293,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
290293
// Now go through all the successful ones,
291294
// registering any nested obligations for the future.
292295
for new_obligation in new_obligations.into_iter() {
293-
self.register_predicate(selcx.infcx(), new_obligation);
296+
self.register_predicate_obligation(selcx.infcx(), new_obligation);
294297
}
295298
}
296299

@@ -398,104 +401,18 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
398401
project_obligation.repr(tcx),
399402
result.repr(tcx));
400403
match result {
401-
Ok(()) => {
404+
Ok(Some(obligations)) => {
405+
new_obligations.extend(obligations.into_iter());
402406
true
403407
}
404-
Err(project::ProjectionError::TooManyCandidates) => {
405-
// Without more type information, we can't say much.
408+
Ok(None) => {
406409
false
407410
}
408-
Err(project::ProjectionError::NoCandidate) => {
409-
// This means that we have a type like `<T as
410-
// Trait>::name = U` but we couldn't find any more
411-
// information. This could just be that we're in a
412-
// function like:
413-
//
414-
// fn foo<T:Trait>(...)
415-
//
416-
// in which case this is not an error. But it
417-
// might also mean we're in a situation where we
418-
// don't actually know that `T : Trait` holds,
419-
// which would be weird (e.g., if `T` was not a
420-
// parameter type but a normal type, like `int`).
421-
//
422-
// So what we do is to (1) add a requirement that
423-
// `T : Trait` (just in case) and (2) try to unify
424-
// `U` with `<T as Trait>::name`.
425-
426-
if !ty::binds_late_bound_regions(selcx.tcx(), data) {
427-
// Check that `T : Trait` holds.
428-
let trait_ref = data.to_poly_trait_ref();
429-
new_obligations.push(obligation.with(trait_ref.as_predicate()));
430-
431-
// Fallback to `<T as Trait>::name`. If this
432-
// fails, then the output must be at least
433-
// somewhat constrained, and we cannot verify
434-
// that constraint, so yield an error.
435-
let ty_projection = ty::mk_projection(tcx,
436-
trait_ref.0.clone(),
437-
data.0.projection_ty.item_name);
438-
439-
debug!("process_predicate: falling back to projection {}",
440-
ty_projection.repr(selcx.tcx()));
441-
442-
match infer::mk_eqty(selcx.infcx(),
443-
true,
444-
infer::EquatePredicate(obligation.cause.span),
445-
ty_projection,
446-
data.0.ty) {
447-
Ok(()) => { }
448-
Err(_) => {
449-
debug!("process_predicate: fallback failed to unify; error");
450-
errors.push(
451-
FulfillmentError::new(
452-
obligation.clone(),
453-
CodeSelectionError(Unimplemented)));
454-
}
455-
}
456-
457-
true
458-
} else {
459-
// If we have something like
460-
//
461-
// for<'a> <T<'a> as Trait>::name == &'a int
462-
//
463-
// there is no "canonical form" for us to
464-
// make, so just report the lack of candidates
465-
// as an error.
466-
467-
debug!("process_predicate: can't fallback, higher-ranked");
468-
errors.push(
469-
FulfillmentError::new(
470-
obligation.clone(),
471-
CodeSelectionError(Unimplemented)));
472-
473-
true
474-
}
475-
}
476-
Err(project::ProjectionError::MismatchedTypes(e)) => {
411+
Err(err) => {
477412
errors.push(
478413
FulfillmentError::new(
479414
obligation.clone(),
480-
CodeProjectionError(e)));
481-
true
482-
}
483-
Err(project::ProjectionError::TraitSelectionError(_)) => {
484-
// There was an error matching `T : Trait` (which
485-
// is a pre-requisite for `<T as Trait>::Name`
486-
// being valid). We could just report the error
487-
// now, but that tends to lead to double error
488-
// reports for the user (one for the obligation `T
489-
// : Trait`, typically incurred somewhere else,
490-
// and one from here). Instead, we'll create the
491-
// `T : Trait` obligation and add THAT as a
492-
// requirement. This will (eventually) trigger the
493-
// same error, but it will also wind up flagged as
494-
// a duplicate if another requirement that `T :
495-
// Trait` arises from somewhere else.
496-
let trait_predicate = data.to_poly_trait_ref();
497-
let trait_obligation = obligation.with(trait_predicate.as_predicate());
498-
new_obligations.push(trait_obligation);
415+
CodeProjectionError(err)));
499416
true
500417
}
501418
}

src/librustc/middle/traits/mod.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ use util::ppaux::Repr;
2727
pub use self::error_reporting::report_fulfillment_errors;
2828
pub use self::fulfill::{FulfillmentContext, RegionObligation};
2929
pub use self::project::MismatchedProjectionTypes;
30-
pub use self::project::project_type;
31-
pub use self::project::ProjectionResult;
30+
pub use self::project::normalize;
31+
pub use self::project::Normalized;
3232
pub use self::select::SelectionContext;
3333
pub use self::select::SelectionCache;
3434
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
@@ -320,6 +320,16 @@ impl<'tcx,O> Obligation<'tcx,O> {
320320
predicate: trait_ref }
321321
}
322322

323+
fn with_depth(cause: ObligationCause<'tcx>,
324+
recursion_depth: uint,
325+
trait_ref: O)
326+
-> Obligation<'tcx, O>
327+
{
328+
Obligation { cause: cause,
329+
recursion_depth: recursion_depth,
330+
predicate: trait_ref }
331+
}
332+
323333
pub fn misc(span: Span, body_id: ast::NodeId, trait_ref: O) -> Obligation<'tcx, O> {
324334
Obligation::new(ObligationCause::misc(span, body_id), trait_ref)
325335
}

0 commit comments

Comments
 (0)