Skip to content

Various fixes to associated types #20374

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 1, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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