Skip to content

Commit

Permalink
auto merge of #20374 : nikomatsakis/rust/assoc-types, r=nikomatsakis
Browse files Browse the repository at this point in the history
These mostly derive from problems that @japaric encountered.

r? @pcwalton
  • Loading branch information
bors committed Jan 1, 2015
2 parents 10d99a9 + 004a567 commit 7d4f487
Show file tree
Hide file tree
Showing 23 changed files with 886 additions and 430 deletions.
1 change: 1 addition & 0 deletions src/librustc/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1402,6 +1402,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
}
ty::TypeTraitItem(associated_type) => {
encode_name(rbml_w, associated_type.name);
encode_def_id(rbml_w, associated_type.def_id);

let elem = ast_map::PathName(associated_type.name);
encode_path(rbml_w,
Expand Down
132 changes: 69 additions & 63 deletions src/librustc/middle/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
"overflow evaluating the requirement `{}`",
predicate.user_string(infcx.tcx)).as_slice());

let current_limit = infcx.tcx.sess.recursion_limit.get();
let suggested_limit = current_limit * 2;
infcx.tcx.sess.span_note(
obligation.cause.span,
format!(
"consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
suggested_limit)[]);
suggest_new_overflow_limit(infcx, obligation.cause.span);

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

let trait_ref = match obligation.predicate {
ty::Predicate::Trait(ref trait_predicate) => {
infcx.resolve_type_vars_if_possible(
&trait_predicate.to_poly_trait_ref())
}
_ => {
infcx.tcx.sess.span_bug(
obligation.cause.span,
format!("ambiguity from something other than a trait: {}",
obligation.predicate.repr(infcx.tcx)).as_slice());
}
};

let self_ty = trait_ref.self_ty();
let predicate = infcx.resolve_type_vars_if_possible(&obligation.predicate);

debug!("maybe_report_ambiguity(trait_ref={}, self_ty={}, obligation={})",
trait_ref.repr(infcx.tcx),
self_ty.repr(infcx.tcx),
debug!("maybe_report_ambiguity(predicate={}, obligation={})",
predicate.repr(infcx.tcx),
obligation.repr(infcx.tcx));
let all_types = &trait_ref.substs().types;
if all_types.iter().any(|&t| ty::type_is_error(t)) {
} else if all_types.iter().any(|&t| ty::type_needs_infer(t)) {
// This is kind of a hack: it frequently happens that some earlier
// error prevents types from being fully inferred, and then we get
// a bunch of uninteresting errors saying something like "<generic
// #0> doesn't implement Sized". It may even be true that we
// could just skip over all checks where the self-ty is an
// inference variable, but I was afraid that there might be an
// inference variable created, registered as an obligation, and
// then never forced by writeback, and hence by skipping here we'd
// be ignoring the fact that we don't KNOW the type works
// out. Though even that would probably be harmless, given that
// we're only talking about builtin traits, which are known to be
// inhabited. But in any case I just threw in this check for
// has_errors() to be sure that compilation isn't happening
// anyway. In that case, why inundate the user.
if !infcx.tcx.sess.has_errors() {
if infcx.tcx.lang_items.sized_trait()
.map_or(false, |sized_id| sized_id == trait_ref.def_id()) {
infcx.tcx.sess.span_err(

match predicate {
ty::Predicate::Trait(ref data) => {
let trait_ref = data.to_poly_trait_ref();
let self_ty = trait_ref.self_ty();
let all_types = &trait_ref.substs().types;
if all_types.iter().any(|&t| ty::type_is_error(t)) {
} else if all_types.iter().any(|&t| ty::type_needs_infer(t)) {
// This is kind of a hack: it frequently happens that some earlier
// error prevents types from being fully inferred, and then we get
// a bunch of uninteresting errors saying something like "<generic
// #0> doesn't implement Sized". It may even be true that we
// could just skip over all checks where the self-ty is an
// inference variable, but I was afraid that there might be an
// inference variable created, registered as an obligation, and
// then never forced by writeback, and hence by skipping here we'd
// be ignoring the fact that we don't KNOW the type works
// out. Though even that would probably be harmless, given that
// we're only talking about builtin traits, which are known to be
// inhabited. But in any case I just threw in this check for
// has_errors() to be sure that compilation isn't happening
// anyway. In that case, why inundate the user.
if !infcx.tcx.sess.has_errors() {
if
infcx.tcx.lang_items.sized_trait()
.map_or(false, |sized_id| sized_id == trait_ref.def_id())
{
infcx.tcx.sess.span_err(
obligation.cause.span,
format!(
"unable to infer enough type information about `{}`; \
type annotations required",
self_ty.user_string(infcx.tcx)).as_slice());
} else {
infcx.tcx.sess.span_err(
obligation.cause.span,
format!(
"type annotations required: cannot resolve `{}`",
predicate.user_string(infcx.tcx)).as_slice());
note_obligation_cause(infcx, obligation);
}
}
} else if !infcx.tcx.sess.has_errors() {
// Ambiguity. Coherence should have reported an error.
infcx.tcx.sess.span_bug(
obligation.cause.span,
format!(
"unable to infer enough type information about `{}`; type annotations \
required",
"coherence failed to report ambiguity: \
cannot locate the impl of the trait `{}` for \
the type `{}`",
trait_ref.user_string(infcx.tcx),
self_ty.user_string(infcx.tcx)).as_slice());
} else {
}
}

_ => {
if !infcx.tcx.sess.has_errors() {
infcx.tcx.sess.span_err(
obligation.cause.span,
format!(
"unable to infer enough type information to \
locate the impl of the trait `{}` for \
the type `{}`; type annotations required",
trait_ref.user_string(infcx.tcx),
self_ty.user_string(infcx.tcx)).as_slice());
"type annotations required: cannot resolve `{}`",
predicate.user_string(infcx.tcx)).as_slice());
note_obligation_cause(infcx, obligation);
}
}
} else if !infcx.tcx.sess.has_errors() {
// Ambiguity. Coherence should have reported an error.
infcx.tcx.sess.span_bug(
obligation.cause.span,
format!(
"coherence failed to report ambiguity: \
cannot locate the impl of the trait `{}` for \
the type `{}`",
trait_ref.user_string(infcx.tcx),
self_ty.user_string(infcx.tcx)).as_slice());
}
}

Expand Down Expand Up @@ -335,3 +332,12 @@ fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
}
}

pub fn suggest_new_overflow_limit(infcx: &InferCtxt, span: Span) {
let current_limit = infcx.tcx.sess.recursion_limit.get();
let suggested_limit = current_limit * 2;
infcx.tcx.sess.span_note(
span,
format!(
"consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
suggested_limit)[]);
}
135 changes: 26 additions & 109 deletions src/librustc/middle/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use middle::infer::{mod, InferCtxt};
use middle::infer::{InferCtxt};
use middle::mem_categorization::Typer;
use middle::ty::{mod, AsPredicate, RegionEscape, Ty, ToPolyTraitRef};
use middle::ty::{mod, RegionEscape, Ty};
use std::collections::HashSet;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::default::Default;
Expand All @@ -23,7 +23,6 @@ use super::CodeAmbiguity;
use super::CodeProjectionError;
use super::CodeSelectionError;
use super::FulfillmentError;
use super::Obligation;
use super::ObligationCause;
use super::PredicateObligation;
use super::project;
Expand Down Expand Up @@ -110,6 +109,8 @@ impl<'tcx> FulfillmentContext<'tcx> {
/// `projection_ty` again.
pub fn normalize_projection_type<'a>(&mut self,
infcx: &InferCtxt<'a,'tcx>,
param_env: &ty::ParameterEnvironment<'tcx>,
typer: &Typer<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>)
-> Ty<'tcx>
Expand All @@ -121,18 +122,16 @@ impl<'tcx> FulfillmentContext<'tcx> {

// FIXME(#20304) -- cache

let ty_var = infcx.next_ty_var();
let projection =
ty::Binder(ty::ProjectionPredicate {
projection_ty: projection_ty,
ty: ty_var
});
let obligation = Obligation::new(cause, projection.as_predicate());
self.register_predicate(infcx, obligation);
let mut selcx = SelectionContext::new(infcx, param_env, typer);
let normalized = project::normalize_projection_type(&mut selcx, projection_ty, cause, 0);

for obligation in normalized.obligations.into_iter() {
self.register_predicate_obligation(infcx, obligation);
}

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

ty_var
normalized.value
}

pub fn register_builtin_bound<'a>(&mut self,
Expand All @@ -143,7 +142,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
{
match predicate_for_builtin_bound(infcx.tcx, cause, builtin_bound, 0, ty) {
Ok(predicate) => {
self.register_predicate(infcx, predicate);
self.register_predicate_obligation(infcx, predicate);
}
Err(ErrorReported) => { }
}
Expand All @@ -158,10 +157,14 @@ impl<'tcx> FulfillmentContext<'tcx> {
register_region_obligation(infcx.tcx, t_a, r_b, cause, &mut self.region_obligations);
}

pub fn register_predicate<'a>(&mut self,
infcx: &InferCtxt<'a,'tcx>,
obligation: PredicateObligation<'tcx>)
pub fn register_predicate_obligation<'a>(&mut self,
infcx: &InferCtxt<'a,'tcx>,
obligation: PredicateObligation<'tcx>)
{
// this helps to reduce duplicate errors, as well as making
// debug output much nicer to read and so on.
let obligation = infcx.resolve_type_vars_if_possible(&obligation);

if !self.duplicate_set.insert(obligation.predicate.clone()) {
debug!("register_predicate({}) -- already seen, skip", obligation.repr(infcx.tcx));
return;
Expand Down Expand Up @@ -290,7 +293,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
// Now go through all the successful ones,
// registering any nested obligations for the future.
for new_obligation in new_obligations.into_iter() {
self.register_predicate(selcx.infcx(), new_obligation);
self.register_predicate_obligation(selcx.infcx(), new_obligation);
}
}

Expand Down Expand Up @@ -398,104 +401,18 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
project_obligation.repr(tcx),
result.repr(tcx));
match result {
Ok(()) => {
Ok(Some(obligations)) => {
new_obligations.extend(obligations.into_iter());
true
}
Err(project::ProjectionError::TooManyCandidates) => {
// Without more type information, we can't say much.
Ok(None) => {
false
}
Err(project::ProjectionError::NoCandidate) => {
// This means that we have a type like `<T as
// Trait>::name = U` but we couldn't find any more
// information. This could just be that we're in a
// function like:
//
// fn foo<T:Trait>(...)
//
// in which case this is not an error. But it
// might also mean we're in a situation where we
// don't actually know that `T : Trait` holds,
// which would be weird (e.g., if `T` was not a
// parameter type but a normal type, like `int`).
//
// So what we do is to (1) add a requirement that
// `T : Trait` (just in case) and (2) try to unify
// `U` with `<T as Trait>::name`.

if !ty::binds_late_bound_regions(selcx.tcx(), data) {
// Check that `T : Trait` holds.
let trait_ref = data.to_poly_trait_ref();
new_obligations.push(obligation.with(trait_ref.as_predicate()));

// Fallback to `<T as Trait>::name`. If this
// fails, then the output must be at least
// somewhat constrained, and we cannot verify
// that constraint, so yield an error.
let ty_projection = ty::mk_projection(tcx,
trait_ref.0.clone(),
data.0.projection_ty.item_name);

debug!("process_predicate: falling back to projection {}",
ty_projection.repr(selcx.tcx()));

match infer::mk_eqty(selcx.infcx(),
true,
infer::EquatePredicate(obligation.cause.span),
ty_projection,
data.0.ty) {
Ok(()) => { }
Err(_) => {
debug!("process_predicate: fallback failed to unify; error");
errors.push(
FulfillmentError::new(
obligation.clone(),
CodeSelectionError(Unimplemented)));
}
}

true
} else {
// If we have something like
//
// for<'a> <T<'a> as Trait>::name == &'a int
//
// there is no "canonical form" for us to
// make, so just report the lack of candidates
// as an error.

debug!("process_predicate: can't fallback, higher-ranked");
errors.push(
FulfillmentError::new(
obligation.clone(),
CodeSelectionError(Unimplemented)));

true
}
}
Err(project::ProjectionError::MismatchedTypes(e)) => {
Err(err) => {
errors.push(
FulfillmentError::new(
obligation.clone(),
CodeProjectionError(e)));
true
}
Err(project::ProjectionError::TraitSelectionError(_)) => {
// There was an error matching `T : Trait` (which
// is a pre-requisite for `<T as Trait>::Name`
// being valid). We could just report the error
// now, but that tends to lead to double error
// reports for the user (one for the obligation `T
// : Trait`, typically incurred somewhere else,
// and one from here). Instead, we'll create the
// `T : Trait` obligation and add THAT as a
// requirement. This will (eventually) trigger the
// same error, but it will also wind up flagged as
// a duplicate if another requirement that `T :
// Trait` arises from somewhere else.
let trait_predicate = data.to_poly_trait_ref();
let trait_obligation = obligation.with(trait_predicate.as_predicate());
new_obligations.push(trait_obligation);
CodeProjectionError(err)));
true
}
}
Expand Down
14 changes: 12 additions & 2 deletions src/librustc/middle/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use util::ppaux::Repr;
pub use self::error_reporting::report_fulfillment_errors;
pub use self::fulfill::{FulfillmentContext, RegionObligation};
pub use self::project::MismatchedProjectionTypes;
pub use self::project::project_type;
pub use self::project::ProjectionResult;
pub use self::project::normalize;
pub use self::project::Normalized;
pub use self::select::SelectionContext;
pub use self::select::SelectionCache;
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
Expand Down Expand Up @@ -320,6 +320,16 @@ impl<'tcx,O> Obligation<'tcx,O> {
predicate: trait_ref }
}

fn with_depth(cause: ObligationCause<'tcx>,
recursion_depth: uint,
trait_ref: O)
-> Obligation<'tcx, O>
{
Obligation { cause: cause,
recursion_depth: recursion_depth,
predicate: trait_ref }
}

pub fn misc(span: Span, body_id: ast::NodeId, trait_ref: O) -> Obligation<'tcx, O> {
Obligation::new(ObligationCause::misc(span, body_id), trait_ref)
}
Expand Down
Loading

0 comments on commit 7d4f487

Please sign in to comment.