auto merge of #20374 : nikomatsakis/rust/assoc-types, r=nikomatsakis

These mostly derive from problems that @japaric encountered.

r? @pcwalton
This commit is contained in:
bors 2015-01-01 01:20:56 +00:00
commit 7d4f4876d6
23 changed files with 890 additions and 434 deletions

View File

@ -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,

View File

@ -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);
}
@ -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 predicate = infcx.resolve_type_vars_if_possible(&obligation.predicate);
let self_ty = trait_ref.self_ty();
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",
self_ty.user_string(infcx.tcx)).as_slice());
} else {
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",
"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());
}
}
_ => {
if !infcx.tcx.sess.has_errors() {
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!(
"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());
}
}
@ -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)[]);
}

View File

@ -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;
@ -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;
@ -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>
@ -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);
debug!("normalize_associated_type: result={}", ty_var.repr(infcx.tcx));
for obligation in normalized.obligations.into_iter() {
self.register_predicate_obligation(infcx, obligation);
}
ty_var
debug!("normalize_associated_type: result={}", normalized.value.repr(infcx.tcx));
normalized.value
}
pub fn register_builtin_bound<'a>(&mut self,
@ -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) => { }
}
@ -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;
@ -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);
}
}
@ -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
}
}

View File

@ -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};
@ -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)
}

View File

@ -12,6 +12,8 @@
use super::elaborate_predicates;
use super::Obligation;
use super::ObligationCause;
use super::Overflow;
use super::PredicateObligation;
use super::SelectionContext;
use super::SelectionError;
@ -19,7 +21,8 @@ use super::VtableImplData;
use middle::infer;
use middle::subst::Subst;
use middle::ty::{mod, AsPredicate, ToPolyTraitRef, Ty};
use middle::ty::{mod, AsPredicate, RegionEscape, HasProjectionTypes, ToPolyTraitRef, Ty};
use middle::ty_fold::{mod, TypeFoldable, TypeFolder};
use util::ppaux::Repr;
pub type PolyProjectionObligation<'tcx> =
@ -31,21 +34,11 @@ pub type ProjectionObligation<'tcx> =
pub type ProjectionTyObligation<'tcx> =
Obligation<'tcx, ty::ProjectionTy<'tcx>>;
/// When attempting to resolve `<T as TraitRef>::Name == U`...
pub enum ProjectionError<'tcx> {
/// ...we could not find any helpful information on what `Name`
/// might be. This could occur, for example, if there is a where
/// clause `T : TraitRef` but not `T : TraitRef<Name=V>`. When
/// normalizing, this case is where we opt to normalize back to
/// the projection type `<T as TraitRef>::Name`.
NoCandidate,
/// When attempting to resolve `<T as TraitRef>::Name` ...
pub enum ProjectionTyError<'tcx> {
/// ...we found multiple sources of information and couldn't resolve the ambiguity.
TooManyCandidates,
/// ...`<T as TraitRef::Name>` ws resolved to some type `V` that failed to unify with `U`
MismatchedTypes(MismatchedProjectionTypes<'tcx>),
/// ...an error occurred matching `T : TraitRef`
TraitSelectionError(SelectionError<'tcx>),
}
@ -55,8 +48,6 @@ pub struct MismatchedProjectionTypes<'tcx> {
pub err: ty::type_err<'tcx>
}
pub type ProjectionResult<'tcx, T> = Result<T, ProjectionError<'tcx>>;
enum ProjectionTyCandidate<'tcx> {
ParamEnv(ty::PolyProjectionPredicate<'tcx>),
Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>),
@ -70,24 +61,43 @@ struct ProjectionTyCandidateSet<'tcx> {
pub fn poly_project_and_unify_type<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &PolyProjectionObligation<'tcx>)
-> ProjectionResult<'tcx, ()>
-> Result<Option<Vec<PredicateObligation<'tcx>>>, MismatchedProjectionTypes<'tcx>>
{
debug!("poly_project(obligation={})",
obligation.repr(selcx.tcx()));
let infcx = selcx.infcx();
infcx.try(|snapshot| {
let result = infcx.try(|snapshot| {
let (skol_predicate, skol_map) =
infcx.skolemize_late_bound_regions(&obligation.predicate, snapshot);
let skol_obligation = obligation.with(skol_predicate);
let () = try!(project_and_unify_type(selcx, &skol_obligation));
match infcx.leak_check(&skol_map, snapshot) {
Ok(()) => Ok(()),
Err(e) => Err(ProjectionError::MismatchedTypes(MismatchedProjectionTypes{err: e})),
match project_and_unify_type(selcx, &skol_obligation) {
Ok(Some(obligations)) => {
match infcx.leak_check(&skol_map, snapshot) {
Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &obligations)),
Err(e) => Err(Some(MismatchedProjectionTypes { err: e })),
}
}
Ok(None) => {
// Signal ambiguity using Err just so that infcx.try()
// rolls back the snapshot. We adapt below.
Err(None)
}
Err(e) => {
Err(Some(e))
}
}
})
});
// Above, we use Err(None) to signal ambiguity so that the
// snapshot will be rolled back. But here, we want to translate to
// Ok(None). Kind of weird.
match result {
Ok(obligations) => Ok(Some(obligations)),
Err(None) => Ok(None),
Err(Some(e)) => Err(e),
}
}
/// Compute result of projecting an associated type and unify it with
@ -95,55 +105,281 @@ pub fn poly_project_and_unify_type<'cx,'tcx>(
pub fn project_and_unify_type<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionObligation<'tcx>)
-> ProjectionResult<'tcx, ()>
-> Result<Option<Vec<PredicateObligation<'tcx>>>, MismatchedProjectionTypes<'tcx>>
{
debug!("project_and_unify(obligation={})",
obligation.repr(selcx.tcx()));
let ty_obligation = obligation.with(obligation.predicate.projection_ty.clone());
let projected_ty = try!(project_type(selcx, &ty_obligation));
let Normalized { value: normalized_ty, obligations } =
match opt_normalize_projection_type(selcx,
obligation.predicate.projection_ty.clone(),
obligation.cause.clone(),
obligation.recursion_depth) {
Some(n) => n,
None => { return Ok(None); }
};
debug!("project_and_unify_type: normalized_ty={} obligations={}",
normalized_ty.repr(selcx.tcx()),
obligations.repr(selcx.tcx()));
let infcx = selcx.infcx();
let origin = infer::RelateOutputImplTypes(obligation.cause.span);
debug!("project_and_unify_type: projected_ty = {}", projected_ty.repr(selcx.tcx()));
match infer::mk_eqty(infcx, true, origin, projected_ty, obligation.predicate.ty) {
Ok(()) => Ok(()),
Err(e) => Err(ProjectionError::MismatchedTypes(MismatchedProjectionTypes{err: e})),
match infer::mk_eqty(infcx, true, origin, normalized_ty, obligation.predicate.ty) {
Ok(()) => Ok(Some(obligations)),
Err(err) => Err(MismatchedProjectionTypes { err: err }),
}
}
pub fn normalize<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tcx>,
cause: ObligationCause<'tcx>,
value: &T)
-> Normalized<'tcx, T>
where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone
{
let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, 0);
let result = normalizer.fold(value);
Normalized {
value: result,
obligations: normalizer.obligations,
}
}
struct AssociatedTypeNormalizer<'a,'b:'a,'tcx:'b> {
selcx: &'a mut SelectionContext<'b,'tcx>,
cause: ObligationCause<'tcx>,
obligations: Vec<PredicateObligation<'tcx>>,
depth: uint,
}
impl<'a,'b,'tcx> AssociatedTypeNormalizer<'a,'b,'tcx> {
fn new(selcx: &'a mut SelectionContext<'b,'tcx>,
cause: ObligationCause<'tcx>,
depth: uint)
-> AssociatedTypeNormalizer<'a,'b,'tcx>
{
AssociatedTypeNormalizer {
selcx: selcx,
cause: cause,
obligations: vec!(),
depth: depth,
}
}
fn fold<T:TypeFoldable<'tcx> + HasProjectionTypes + Clone>(&mut self, value: &T) -> T {
let value = self.selcx.infcx().resolve_type_vars_if_possible(value);
if !value.has_projection_types() {
value.clone()
} else {
value.fold_with(self)
}
}
}
impl<'a,'b,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'b,'tcx> {
fn tcx(&self) -> &ty::ctxt<'tcx> {
self.selcx.tcx()
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
// We don't want to normalize associated types that occur inside of region
// binders, because they may contain bound regions, and we can't cope with that.
//
// Example:
//
// for<'a> fn(<T as Foo<&'a>>::A)
//
// Instead of normalizing `<T as Foo<&'a>>::A` here, we'll
// normalize it when we instantiate those bound regions (which
// should occur eventually).
match ty.sty {
ty::ty_projection(ref data) if !data.has_escaping_regions() => { // (*)
// (*) This is kind of hacky -- we need to be able to
// handle normalization within binders because
// otherwise we wind up a need to normalize when doing
// trait matching (since you can have a trait
// obligation like `for<'a> T::B : Fn(&'a int)`), but
// we can't normalize with bound regions in scope. So
// far now we just ignore binders but only normalize
// if all bound regions are gone (and then we still
// have to renormalize whenever we instantiate a
// binder). It would be better to normalize in a
// binding-aware fashion.
let Normalized { value: ty, obligations } =
normalize_projection_type(self.selcx,
data.clone(),
self.cause.clone(),
self.depth);
self.obligations.extend(obligations.into_iter());
ty
}
_ => {
ty_fold::super_fold_ty(self, ty)
}
}
}
}
pub struct Normalized<'tcx,T> {
pub value: T,
pub obligations: Vec<PredicateObligation<'tcx>>,
}
pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>;
pub fn normalize_projection_type<'a,'b,'tcx>(
selcx: &'a mut SelectionContext<'b,'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
depth: uint)
-> NormalizedTy<'tcx>
{
opt_normalize_projection_type(selcx, projection_ty.clone(), cause.clone(), depth)
.unwrap_or_else(move || {
// if we bottom out in ambiguity, create a type variable
// and a deferred predicate to resolve this when more type
// information is available.
let ty_var = selcx.infcx().next_ty_var();
let projection = ty::Binder(ty::ProjectionPredicate {
projection_ty: projection_ty,
ty: ty_var
});
let obligation = Obligation::with_depth(cause, depth+1, projection.as_predicate());
Normalized {
value: ty_var,
obligations: vec!(obligation)
}
})
}
fn opt_normalize_projection_type<'a,'b,'tcx>(
selcx: &'a mut SelectionContext<'b,'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
depth: uint)
-> Option<NormalizedTy<'tcx>>
{
debug!("normalize_projection_type(\
projection_ty={}, \
depth={})",
projection_ty.repr(selcx.tcx()),
depth);
let obligation = Obligation::with_depth(cause.clone(), depth, projection_ty.clone());
match project_type(selcx, &obligation) {
Ok(ProjectedTy::Progress(projected_ty, mut obligations)) => {
// if projection succeeded, then what we get out of this
// is also non-normalized (consider: it was derived from
// an impl, where-clause etc) and hence we must
// re-normalize it
debug!("normalize_projection_type: projected_ty={} depth={}",
projected_ty.repr(selcx.tcx()),
depth);
if ty::type_has_projection(projected_ty) {
let tcx = selcx.tcx();
let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, depth);
let normalized_ty = normalizer.fold(&projected_ty);
debug!("normalize_projection_type: normalized_ty={} depth={}",
normalized_ty.repr(tcx),
depth);
obligations.extend(normalizer.obligations.into_iter());
Some(Normalized {
value: normalized_ty,
obligations: obligations,
})
} else {
Some(Normalized {
value: projected_ty,
obligations: obligations,
})
}
}
Ok(ProjectedTy::NoProgress(projected_ty)) => {
Some(Normalized {
value: projected_ty,
obligations: vec!()
})
}
Err(ProjectionTyError::TooManyCandidates) => {
None
}
Err(ProjectionTyError::TraitSelectionError(_)) => {
// if we got an error processing the `T as Trait` part,
// just return `ty::err` but add the obligation `T :
// Trait`, which when processed will cause the error to be
// reported later
Some(normalize_to_error(selcx, projection_ty, cause, depth))
}
}
}
/// in various error cases, we just set ty_err and return an obligation
/// that, when fulfiled, will lead to an error
fn normalize_to_error<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
depth: uint)
-> NormalizedTy<'tcx>
{
let trait_ref = projection_ty.trait_ref.to_poly_trait_ref();
let trait_obligation = Obligation { cause: cause,
recursion_depth: depth,
predicate: trait_ref.as_predicate() };
Normalized {
value: selcx.tcx().types.err,
obligations: vec!(trait_obligation)
}
}
enum ProjectedTy<'tcx> {
Progress(Ty<'tcx>, Vec<PredicateObligation<'tcx>>),
NoProgress(Ty<'tcx>),
}
/// Compute the result of a projection type (if we can).
pub fn project_type<'cx,'tcx>(
fn project_type<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionTyObligation<'tcx>)
-> ProjectionResult<'tcx, Ty<'tcx>>
-> Result<ProjectedTy<'tcx>, ProjectionTyError<'tcx>>
{
debug!("project(obligation={})",
obligation.repr(selcx.tcx()));
let recursion_limit = selcx.tcx().sess.recursion_limit.get();
if obligation.recursion_depth >= recursion_limit {
debug!("project: overflow!");
return Err(ProjectionTyError::TraitSelectionError(Overflow));
}
let mut candidates = ProjectionTyCandidateSet {
vec: Vec::new(),
ambiguous: false,
};
let () = assemble_candidates_from_param_env(selcx,
obligation,
&mut candidates);
let () = assemble_candidates_from_object_type(selcx,
obligation,
&mut candidates);
assemble_candidates_from_object_type(selcx,
obligation,
&mut candidates);
if candidates.vec.is_empty() {
// FIXME(#20297) -- In `select.rs` there is similar logic that
// gives precedence to where-clauses, but it's a bit more
// fine-grained. I was lazy here and just always give
// precedence to where-clauses or other such sources over
// actually dredging through impls. This logic probably should
// be tightened up.
assemble_candidates_from_param_env(selcx,
obligation,
&mut candidates);
let () = try!(assemble_candidates_from_impls(selcx,
obligation,
&mut candidates));
if let Err(e) = assemble_candidates_from_impls(selcx,
obligation,
&mut candidates) {
return Err(ProjectionTyError::TraitSelectionError(e));
}
}
debug!("{} candidates, ambiguous={}",
@ -153,15 +389,18 @@ pub fn project_type<'cx,'tcx>(
// We probably need some winnowing logic similar to select here.
if candidates.ambiguous || candidates.vec.len() > 1 {
return Err(ProjectionError::TooManyCandidates);
return Err(ProjectionTyError::TooManyCandidates);
}
match candidates.vec.pop() {
Some(candidate) => {
Ok(try!(confirm_candidate(selcx, obligation, candidate)))
let (ty, obligations) = confirm_candidate(selcx, obligation, candidate);
Ok(ProjectedTy::Progress(ty, obligations))
}
None => {
Err(ProjectionError::NoCandidate)
Ok(ProjectedTy::NoProgress(ty::mk_projection(selcx.tcx(),
obligation.predicate.trait_ref.clone(),
obligation.predicate.item_name)))
}
}
}
@ -237,9 +476,9 @@ fn assemble_candidates_from_object_type<'cx,'tcx>(
fn assemble_candidates_from_impls<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
-> ProjectionResult<'tcx, ()>
-> Result<(), SelectionError<'tcx>>
{
// If we are resolving `<T as TraitRef<...>>::Item == Type`,
// start out by selecting the predicate `T as TraitRef<...>`:
@ -256,7 +495,7 @@ fn assemble_candidates_from_impls<'cx,'tcx>(
Err(e) => {
debug!("assemble_candidates_from_impls: selection error {}",
e.repr(selcx.tcx()));
return Err(ProjectionError::TraitSelectionError(e));
return Err(e);
}
};
@ -308,9 +547,9 @@ fn assemble_candidates_from_impls<'cx,'tcx>(
fn confirm_candidate<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate: ProjectionTyCandidate<'tcx>)
-> ProjectionResult<'tcx, Ty<'tcx>>
-> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
{
let infcx = selcx.infcx();
@ -318,7 +557,7 @@ fn confirm_candidate<'cx,'tcx>(
candidate.repr(infcx.tcx),
obligation.repr(infcx.tcx));
let projected_ty = match candidate {
match candidate {
ProjectionTyCandidate::ParamEnv(poly_projection) => {
let projection =
infcx.replace_late_bound_regions_with_fresh_var(
@ -345,7 +584,7 @@ fn confirm_candidate<'cx,'tcx>(
}
}
projection.ty
(projection.ty, vec!())
}
ProjectionTyCandidate::Impl(impl_vtable) => {
@ -371,7 +610,7 @@ fn confirm_candidate<'cx,'tcx>(
}
match impl_ty {
Some(ty) => ty,
Some(ty) => (ty, impl_vtable.nested.to_vec()),
None => {
selcx.tcx().sess.span_bug(
obligation.cause.span,
@ -381,21 +620,15 @@ fn confirm_candidate<'cx,'tcx>(
}
}
}
};
Ok(projected_ty)
}
}
impl<'tcx> Repr<'tcx> for ProjectionError<'tcx> {
impl<'tcx> Repr<'tcx> for ProjectionTyError<'tcx> {
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
match *self {
ProjectionError::NoCandidate =>
ProjectionTyError::TooManyCandidates =>
format!("NoCandidate"),
ProjectionError::TooManyCandidates =>
format!("NoCandidate"),
ProjectionError::MismatchedTypes(ref m) =>
format!("MismatchedTypes({})", m.repr(tcx)),
ProjectionError::TraitSelectionError(ref e) =>
ProjectionTyError::TraitSelectionError(ref e) =>
format!("TraitSelectionError({})", e.repr(tcx)),
}
}
@ -411,4 +644,3 @@ impl<'tcx> Repr<'tcx> for ProjectionTyCandidate<'tcx> {
}
}
}

View File

@ -5449,8 +5449,15 @@ pub fn predicates_for_trait_ref<'tcx>(tcx: &ctxt<'tcx>,
.map(|poly_trait_ref| ty::Binder(poly_trait_ref.0.subst(tcx, trait_ref.substs())))
.collect();
debug!("bounds_for_trait_ref: trait_bounds={}",
trait_bounds.repr(tcx));
let projection_bounds: Vec<_> =
trait_def.bounds.projection_bounds
.iter()
.map(|poly_proj| ty::Binder(poly_proj.0.subst(tcx, trait_ref.substs())))
.collect();
debug!("bounds_for_trait_ref: trait_bounds={} projection_bounds={}",
trait_bounds.repr(tcx),
projection_bounds.repr(tcx));
// The region bounds and builtin bounds do not currently introduce
// binders so we can just substitute in a straightforward way here.
@ -5463,11 +5470,7 @@ pub fn predicates_for_trait_ref<'tcx>(tcx: &ctxt<'tcx>,
trait_bounds: trait_bounds,
region_bounds: region_bounds,
builtin_bounds: builtin_bounds,
// FIXME(#19451) -- if a trait has a bound like `trait Foo :
// Bar<T=X>`, we should probably be returning that, but this
// code here will just ignore it.
projection_bounds: Vec::new(),
projection_bounds: projection_bounds,
};
predicates(tcx, trait_ref.self_ty(), &bounds)
@ -6385,7 +6388,7 @@ pub fn construct_parameter_environment<'tcx>(
}
fn push_types_from_defs<'tcx>(tcx: &ty::ctxt<'tcx>,
types: &mut subst::VecPerParamSpace<Ty<'tcx>>,
types: &mut VecPerParamSpace<Ty<'tcx>>,
defs: &[TypeParameterDef<'tcx>]) {
for def in defs.iter() {
debug!("construct_parameter_environment(): push_types_from_defs: def={}",
@ -6915,12 +6918,49 @@ impl<'tcx> RegionEscape for Ty<'tcx> {
}
}
impl<'tcx,T:RegionEscape> RegionEscape for VecPerParamSpace<T> {
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
self.iter_enumerated().any(|(space, _, t)| {
if space == subst::FnSpace {
t.has_regions_escaping_depth(depth+1)
} else {
t.has_regions_escaping_depth(depth)
}
})
}
}
impl<'tcx> RegionEscape for TypeScheme<'tcx> {
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
self.ty.has_regions_escaping_depth(depth) ||
self.generics.has_regions_escaping_depth(depth)
}
}
impl RegionEscape for Region {
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
self.escapes_depth(depth)
}
}
impl<'tcx> RegionEscape for Generics<'tcx> {
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
self.predicates.has_regions_escaping_depth(depth)
}
}
impl<'tcx> RegionEscape for Predicate<'tcx> {
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
match *self {
Predicate::Trait(ref data) => data.has_regions_escaping_depth(depth),
Predicate::Equate(ref data) => data.has_regions_escaping_depth(depth),
Predicate::RegionOutlives(ref data) => data.has_regions_escaping_depth(depth),
Predicate::TypeOutlives(ref data) => data.has_regions_escaping_depth(depth),
Predicate::Projection(ref data) => data.has_regions_escaping_depth(depth),
}
}
}
impl<'tcx> RegionEscape for TraitRef<'tcx> {
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
self.substs.types.iter().any(|t| t.has_regions_escaping_depth(depth)) ||
@ -6988,9 +7028,15 @@ pub trait HasProjectionTypes {
fn has_projection_types(&self) -> bool;
}
impl<'tcx,T:HasProjectionTypes> HasProjectionTypes for VecPerParamSpace<T> {
fn has_projection_types(&self) -> bool {
self.iter().any(|p| p.has_projection_types())
}
}
impl<'tcx> HasProjectionTypes for ty::GenericBounds<'tcx> {
fn has_projection_types(&self) -> bool {
self.predicates.iter().any(|p| p.has_projection_types())
self.predicates.has_projection_types()
}
}

View File

@ -954,28 +954,47 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// Currently, we use a fulfillment context to completely resolve
// all nested obligations. This is because they can inform the
// inference of the impl's type parameters. However, in principle,
// we only need to do this until the impl's type parameters are
// fully bound. It could be a slight optimization to stop
// iterating early.
// inference of the impl's type parameters.
let mut fulfill_cx = traits::FulfillmentContext::new();
let vtable = selection.map_move_nested(|predicate| {
fulfill_cx.register_predicate(&infcx, predicate);
fulfill_cx.register_predicate_obligation(&infcx, predicate);
});
match fulfill_cx.select_all_or_error(&infcx, &param_env, tcx) {
let vtable = drain_fulfillment_cx(span, &infcx, &param_env, &mut fulfill_cx, &vtable);
info!("Cache miss: {}", trait_ref.repr(ccx.tcx()));
ccx.trait_cache().borrow_mut().insert(trait_ref,
vtable.clone());
vtable
}
pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span,
infcx: &infer::InferCtxt<'a,'tcx>,
param_env: &ty::ParameterEnvironment<'tcx>,
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
result: &T)
-> T
where T : TypeFoldable<'tcx> + Repr<'tcx>
{
debug!("drain_fulfillment_cx(result={})",
result.repr(infcx.tcx));
// In principle, we only need to do this so long as `result`
// contains unbound type parameters. It could be a slight
// optimization to stop iterating early.
match fulfill_cx.select_all_or_error(infcx, param_env, infcx.tcx) {
Ok(()) => { }
Err(errors) => {
if errors.iter().all(|e| e.is_overflow()) {
// See Ok(None) case above.
ccx.sess().span_fatal(
infcx.tcx.sess.span_fatal(
span,
"reached the recursion limit during monomorphization");
} else {
tcx.sess.span_bug(
infcx.tcx.sess.span_bug(
span,
format!("Encountered errors `{}` fulfilling `{}` during trans",
errors.repr(tcx),
trait_ref.repr(tcx))[]);
format!("Encountered errors `{}` fulfilling during trans",
errors.repr(infcx.tcx))[]);
}
}
}
@ -985,13 +1004,7 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// sort of overkill because we do not expect there to be any
// unbound type variables, hence no `TyFresh` types should ever be
// inserted.
let vtable = vtable.fold_with(&mut infcx.freshener());
info!("Cache miss: {}", trait_ref.repr(ccx.tcx()));
ccx.trait_cache().borrow_mut().insert(trait_ref,
vtable.clone());
vtable
result.fold_with(&mut infcx.freshener())
}
// Key used to lookup values supplied for type parameters in an expr.

View File

@ -16,7 +16,7 @@ use middle::infer;
use middle::subst;
use middle::subst::{Subst, Substs};
use middle::traits;
use middle::ty_fold::{mod, TypeFolder, TypeFoldable};
use middle::ty_fold::{TypeFolder, TypeFoldable};
use trans::base::{set_llvm_fn_attrs, set_inline_hint};
use trans::base::{trans_enum_variant, push_ctxt, get_item_val};
use trans::base::{trans_fn, decl_internal_rust_fn};
@ -31,8 +31,8 @@ use syntax::ast;
use syntax::ast_map;
use syntax::ast_util::{local_def, PostExpansionMethod};
use syntax::attr;
use syntax::codemap::DUMMY_SP;
use std::hash::{sip, Hash};
use std::rc::Rc;
pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
fn_id: ast::DefId,
@ -310,13 +310,13 @@ pub fn apply_param_substs<'tcx,T>(tcx: &ty::ctxt<'tcx>,
/// monomorphization, we know that only concrete types are involved,
/// and hence we can be sure that all associated types will be
/// completely normalized away.
pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, t: &T) -> T
pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T
where T : TypeFoldable<'tcx> + Repr<'tcx> + HasProjectionTypes + Clone
{
debug!("normalize_associated_type(t={})", t.repr(tcx));
debug!("normalize_associated_type(t={})", value.repr(tcx));
if !t.has_projection_types() {
return t.clone();
if !value.has_projection_types() {
return value.clone();
}
// FIXME(#20304) -- cache
@ -324,52 +324,19 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, t: &T) -> T
let infcx = infer::new_infer_ctxt(tcx);
let param_env = ty::empty_parameter_environment();
let mut selcx = traits::SelectionContext::new(&infcx, &param_env, tcx);
let mut normalizer = AssociatedTypeNormalizer { selcx: &mut selcx };
let result = t.fold_with(&mut normalizer);
let cause = traits::ObligationCause::dummy();
let traits::Normalized { value: result, obligations } =
traits::normalize(&mut selcx, cause, value);
debug!("normalize_associated_type: t={} result={}",
t.repr(tcx),
result.repr(tcx));
debug!("normalize_associated_type: result={} obligations={}",
result.repr(tcx),
obligations.repr(tcx));
let mut fulfill_cx = traits::FulfillmentContext::new();
for obligation in obligations.into_iter() {
fulfill_cx.register_predicate_obligation(&infcx, obligation);
}
let result = drain_fulfillment_cx(DUMMY_SP, &infcx, &param_env, &mut fulfill_cx, &result);
result
}
struct AssociatedTypeNormalizer<'a,'tcx:'a> {
selcx: &'a mut traits::SelectionContext<'a,'tcx>,
}
impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> {
fn tcx(&self) -> &ty::ctxt<'tcx> { self.selcx.tcx() }
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
match ty.sty {
ty::ty_projection(ref data) => {
debug!("ty_projection({})", data.repr(self.tcx()));
let tcx = self.selcx.tcx();
let substs = data.trait_ref.substs.clone().erase_regions();
let substs = self.tcx().mk_substs(substs);
assert!(substs.types.iter().all(|&t| (!ty::type_has_params(t) &&
!ty::type_has_self(t))));
let trait_ref = Rc::new(ty::TraitRef::new(data.trait_ref.def_id, substs));
let projection_ty = ty::ProjectionTy { trait_ref: trait_ref.clone(),
item_name: data.item_name };
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
projection_ty);
match traits::project_type(self.selcx, &obligation) {
Ok(ty) => ty,
Err(errors) => {
tcx.sess.bug(
format!("Encountered error(s) `{}` selecting `{}` during trans",
errors.repr(tcx),
trait_ref.repr(tcx)).as_slice());
}
}
}
_ => {
ty_fold::super_fold_ty(self, ty)
}
}
}
}

View File

@ -9,85 +9,34 @@
// except according to those terms.
use middle::infer::InferCtxt;
use middle::traits::{ObligationCause, ObligationCauseCode, FulfillmentContext};
use middle::ty::{mod, RegionEscape, HasProjectionTypes, Ty};
use middle::ty_fold::{mod, TypeFoldable, TypeFolder};
use middle::mem_categorization as mc;
use middle::traits::{mod, FulfillmentContext, Normalized, MiscObligation,
SelectionContext, ObligationCause};
use middle::ty::{mod, HasProjectionTypes};
use middle::ty_fold::{TypeFoldable, TypeFolder};
use syntax::ast;
use syntax::codemap::Span;
use util::ppaux::Repr;
pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
param_env: &ty::ParameterEnvironment<'tcx>,
typer: &(mc::Typer<'tcx>+'a),
fulfillment_cx: &mut FulfillmentContext<'tcx>,
span: Span,
body_id: ast::NodeId,
value: &T)
-> T
where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone
where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone + Repr<'tcx>
{
let value = infcx.resolve_type_vars_if_possible(value);
if !value.has_projection_types() {
return value.clone();
}
let mut normalizer = AssociatedTypeNormalizer { span: span,
body_id: body_id,
infcx: infcx,
fulfillment_cx: fulfillment_cx };
value.fold_with(&mut normalizer)
}
struct AssociatedTypeNormalizer<'a,'tcx:'a> {
infcx: &'a InferCtxt<'a, 'tcx>,
fulfillment_cx: &'a mut FulfillmentContext<'tcx>,
span: Span,
body_id: ast::NodeId,
}
impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> {
fn tcx(&self) -> &ty::ctxt<'tcx> {
self.infcx.tcx
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
// We don't want to normalize associated types that occur inside of region
// binders, because they may contain bound regions, and we can't cope with that.
//
// Example:
//
// for<'a> fn(<T as Foo<&'a>>::A)
//
// Instead of normalizing `<T as Foo<&'a>>::A` here, we'll
// normalize it when we instantiate those bound regions (which
// should occur eventually).
match ty.sty {
ty::ty_projection(ref data) if !data.has_escaping_regions() => { // (*)
// (*) This is kind of hacky -- we need to be able to
// handle normalization within binders because
// otherwise we wind up a need to normalize when doing
// trait matching (since you can have a trait
// obligation like `for<'a> T::B : Fn(&'a int)`), but
// we can't normalize with bound regions in scope. So
// far now we just ignore binders but only normalize
// if all bound regions are gone (and then we still
// have to renormalize whenever we instantiate a
// binder). It would be better to normalize in a
// binding-aware fashion.
let cause =
ObligationCause::new(
self.span,
self.body_id,
ObligationCauseCode::MiscObligation);
self.fulfillment_cx
.normalize_projection_type(self.infcx,
data.clone(),
cause)
}
_ => {
ty_fold::super_fold_ty(self, ty)
}
}
debug!("normalize_associated_types_in(value={})", value.repr(infcx.tcx));
let mut selcx = SelectionContext::new(infcx, param_env, typer);
let cause = ObligationCause::new(span, body_id, MiscObligation);
let Normalized { value: result, obligations } = traits::normalize(&mut selcx, cause, value);
debug!("normalize_associated_types_in: result={} predicates={}",
result.repr(infcx.tcx),
obligations.repr(infcx.tcx));
for obligation in obligations.into_iter() {
fulfillment_cx.register_predicate_obligation(infcx, obligation);
}
result
}

View File

@ -216,7 +216,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
//
// Note that as the method comes from a trait, it should not have
// any late-bound regions appearing in its bounds.
let method_bounds = method_ty.generics.to_bounds(fcx.tcx(), trait_ref.substs);
let method_bounds = fcx.instantiate_bounds(span, trait_ref.substs, &method_ty.generics);
assert!(!method_bounds.has_escaping_regions());
fcx.add_obligations_for_parameters(
traits::ObligationCause::misc(span, fcx.body_id),

View File

@ -768,6 +768,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
// Check whether the impl imposes obligations we have to worry about.
let impl_generics = ty::lookup_item_type(self.tcx(), impl_def_id).generics;
let impl_bounds = impl_generics.to_bounds(self.tcx(), substs);
// FIXME(#20378) assoc type normalization here?
// Erase any late-bound regions bound in the impl
// which appear in the bounds.

View File

@ -93,7 +93,7 @@ use middle::subst::{mod, Subst, Substs, VecPerParamSpace, ParamSpace};
use middle::traits;
use middle::ty::{FnSig, VariantInfo, TypeScheme};
use middle::ty::{Disr, ParamTy, ParameterEnvironment};
use middle::ty::{mod, HasProjectionTypes, Ty};
use middle::ty::{mod, HasProjectionTypes, RegionEscape, Ty};
use middle::ty::liberate_late_bound_regions;
use middle::ty::{MethodCall, MethodCallee, MethodMap, ObjectCastMap};
use middle::ty_fold::{TypeFolder, TypeFoldable};
@ -351,11 +351,18 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
}
}
fn normalize_associated_types_in<T>(&self, span: Span, body_id: ast::NodeId, value: &T) -> T
where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes
fn normalize_associated_types_in<T>(&self,
typer: &mc::Typer<'tcx>,
span: Span,
body_id: ast::NodeId,
value: &T)
-> T
where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + Repr<'tcx>
{
let mut fulfillment_cx = self.fulfillment_cx.borrow_mut();
assoc::normalize_associated_types_in(&self.infcx,
&self.param_env,
typer,
&mut *fulfillment_cx, span,
body_id,
value)
@ -439,7 +446,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
let fn_sig =
liberate_late_bound_regions(ccx.tcx, CodeExtent::from_node_id(body.id), &fn_sig);
let fn_sig =
inh.normalize_associated_types_in(body.span, body.id, &fn_sig);
inh.normalize_associated_types_in(ccx.tcx, body.span, body.id, &fn_sig);
let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_sig,
decl, id, body, &inh);
@ -1191,6 +1198,8 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
impl_sig.subst(tcx, impl_to_skol_substs);
let impl_sig =
assoc::normalize_associated_types_in(&infcx,
&impl_param_env,
infcx.tcx,
&mut fulfillment_cx,
impl_m_span,
impl_m_body_id,
@ -1210,6 +1219,8 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_sig.subst(tcx, &trait_to_skol_substs);
let trait_sig =
assoc::normalize_associated_types_in(&infcx,
&impl_param_env,
infcx.tcx,
&mut fulfillment_cx,
impl_m_span,
impl_m_body_id,
@ -1742,10 +1753,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
result
}
fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes
/// As `instantiate_type_scheme`, but for the bounds found in a
/// generic type scheme.
fn instantiate_bounds(&self,
span: Span,
substs: &Substs<'tcx>,
generics: &ty::Generics<'tcx>)
-> ty::GenericBounds<'tcx>
{
self.inh.normalize_associated_types_in(span, self.body_id, value)
ty::GenericBounds {
predicates: self.instantiate_type_scheme(span, substs, &generics.predicates)
}
}
fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + Repr<'tcx>
{
self.inh.normalize_associated_types_in(self, span, self.body_id, value)
}
fn normalize_associated_type(&self,
@ -1760,6 +1785,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.inh.fulfillment_cx
.borrow_mut()
.normalize_projection_type(self.infcx(),
&self.inh.param_env,
self,
ty::ProjectionTy {
trait_ref: trait_ref,
item_name: item_name,
@ -1853,7 +1880,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span,
&type_scheme.generics);
let bounds =
type_scheme.generics.to_bounds(self.tcx(), &substs);
self.instantiate_bounds(span, &substs, &type_scheme.generics);
self.add_obligations_for_parameters(
traits::ObligationCause::new(
span,
@ -1930,7 +1957,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.inh.fulfillment_cx
.borrow_mut()
.register_predicate(self.infcx(), obligation);
.register_predicate_obligation(self.infcx(), obligation);
}
pub fn to_ty(&self, ast_t: &ast::Ty) -> Ty<'tcx> {
@ -4456,7 +4483,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
if let Some(did) = did {
let polytype = ty::lookup_item_type(tcx, did);
let substs = Substs::new_type(vec![idx_type], vec![]);
let bounds = polytype.generics.to_bounds(tcx, &substs);
let bounds = fcx.instantiate_bounds(expr.span, &substs, &polytype.generics);
fcx.add_obligations_for_parameters(
traits::ObligationCause::new(expr.span,
fcx.body_id,
@ -5271,31 +5298,20 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
// The things we are substituting into the type should not contain
// escaping late-bound regions.
// escaping late-bound regions, and nor should the base type scheme.
assert!(!substs.has_regions_escaping_depth(0));
assert!(!type_scheme.has_escaping_regions());
// In the case of static items taken from impls, there may be
// late-bound regions associated with the impl (not declared on
// the fn itself). Those should be replaced with fresh variables
// now. These can appear either on the type being referenced, or
// on the associated bounds.
let bounds = type_scheme.generics.to_bounds(fcx.tcx(), &substs);
let (ty_late_bound, bounds) =
fcx.infcx().replace_late_bound_regions_with_fresh_var(
span,
infer::FnCall,
&ty::Binder((type_scheme.ty, bounds))).0;
debug!("after late-bounds have been replaced: ty_late_bound={}", ty_late_bound.repr(fcx.tcx()));
debug!("after late-bounds have been replaced: bounds={}", bounds.repr(fcx.tcx()));
// Add all the obligations that are required, substituting and
// normalized appropriately.
let bounds = fcx.instantiate_bounds(span, &substs, &type_scheme.generics);
fcx.add_obligations_for_parameters(
traits::ObligationCause::new(span, fcx.body_id, traits::ItemObligation(def.def_id())),
&bounds);
// Substitute the values for the type parameters into the type of
// the referenced item.
let ty_substituted = fcx.instantiate_type_scheme(span, &substs, &ty_late_bound);
let ty_substituted = fcx.instantiate_type_scheme(span, &substs, &type_scheme.ty);
fcx.write_ty(node_id, ty_substituted);
fcx.write_substs(node_id, ty::ItemSubsts { substs: substs });

View File

@ -269,7 +269,8 @@ impl<'cx,'tcx> BoundsChecker<'cx,'tcx> {
pub fn check_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>) {
let trait_def = ty::lookup_trait_def(self.fcx.tcx(), trait_ref.def_id);
let bounds = trait_def.generics.to_bounds(self.tcx(), trait_ref.substs);
let bounds = self.fcx.instantiate_bounds(self.span, trait_ref.substs, &trait_def.generics);
self.fcx.add_obligations_for_parameters(
traits::ObligationCause::new(
self.span,
@ -319,13 +320,14 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
ty::ty_struct(type_id, substs) |
ty::ty_enum(type_id, substs) => {
let type_scheme = ty::lookup_item_type(self.fcx.tcx(), type_id);
let bounds = self.fcx.instantiate_bounds(self.span, substs, &type_scheme.generics);
if self.binding_count == 0 {
self.fcx.add_obligations_for_parameters(
traits::ObligationCause::new(self.span,
self.fcx.body_id,
traits::ItemObligation(type_id)),
&type_scheme.generics.to_bounds(self.tcx(), substs));
&bounds);
} else {
// There are two circumstances in which we ignore
// region obligations.
@ -349,7 +351,6 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
//
// (I believe we should do the same for traits, but
// that will require an RFC. -nmatsakis)
let bounds = type_scheme.generics.to_bounds(self.tcx(), substs);
let bounds = filter_to_trait_obligations(bounds);
self.fcx.add_obligations_for_parameters(
traits::ObligationCause::new(self.span,

View File

@ -0,0 +1,46 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we reliably check the value of the associated type.
#![crate_type = "lib"]
#![feature(associated_types)]
#![no_implicit_prelude]
use std::option::Option::{None, Some, mod};
use std::vec::Vec;
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
fn is_iterator_of<A, I: Iterator<Item=A>>(_: &I) {}
struct Adapter<I> {
iter: I,
found_none: bool,
}
impl<T, I> Iterator for Adapter<I> where I: Iterator<Item=Option<T>> {
type Item = T;
fn next(&mut self) -> Option<T> {
loop {}
}
}
fn test_adapter<T, I: Iterator<Item=Option<T>>>(it: I) {
is_iterator_of::<Option<T>, _>(&it); // Sanity check
let adapter = Adapter { iter: it, found_none: false };
is_iterator_of::<T, _>(&adapter); // OK
is_iterator_of::<Option<T>, _>(&adapter); //~ ERROR type mismatch
}

View File

@ -25,11 +25,30 @@ pub fn f2<T: Foo>(a: T) -> T::A {
panic!();
}
pub fn main() {
f1(2i, 4i); //~ ERROR expected uint, found int
f1(2i, 4u);
f1(2u, 4u); //~ ERROR the trait `Foo` is not implemented
f1(2u, 4i); //~ ERROR the trait `Foo` is not implemented
let _: int = f2(2i); //~ERROR expected `int`, found `uint`
pub fn f1_int_int() {
f1(2i, 4i);
//~^ ERROR expected uint, found int
}
pub fn f1_int_uint() {
f1(2i, 4u);
}
pub fn f1_uint_uint() {
f1(2u, 4u);
//~^ ERROR the trait `Foo` is not implemented
//~| ERROR the trait `Foo` is not implemented
}
pub fn f1_uint_int() {
f1(2u, 4i);
//~^ ERROR the trait `Foo` is not implemented
//~| ERROR the trait `Foo` is not implemented
}
pub fn f2_int() {
let _: int = f2(2i);
//~^ ERROR expected `int`, found `uint`
}
pub fn main() { }

View File

@ -29,9 +29,10 @@ fn bar<'a, 'b, I : for<'x> Foo<&'x int>>(
x: <I as Foo<&'a int>>::A,
y: <I as Foo<&'b int>>::A,
cond: bool)
{ //~ ERROR cannot infer
{
// x and y here have two distinct lifetimes:
let z: I::A = if cond { x } else { y };
//~^ ERROR cannot infer
}
pub fn main() {}

View File

@ -0,0 +1,28 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Check that an associated type cannot be bound in an expression path.
#![feature(associated_types)]
trait Foo {
type A;
fn bar() -> int;
}
impl Foo for int {
type A = uint;
fn bar() -> int { 42 }
}
pub fn main() {
let x: int = Foo::bar();
//~^ ERROR type annotations required
}

View File

@ -33,7 +33,7 @@ where T : Convert<U>
}
fn a() {
test(22_i32, 44); //~ ERROR unable to infer
test(22_i32, 44); //~ ERROR type annotations required
}
fn main() {}

View File

@ -0,0 +1,58 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test how resolving a projection interacts with inference. In this
// case, we were eagerly unifying the type variable for the iterator
// type with `I` from the where clause, ignoring the in-scope `impl`
// for `ByRef`. The right answer was to consider the result ambiguous
// until more type information was available.
// ignore-pretty -- FIXME(#17362)
#![feature(associated_types, lang_items, unboxed_closures)]
#![no_implicit_prelude]
use std::option::Option::{None, Some, mod};
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
trait IteratorExt: Iterator {
fn by_ref(&mut self) -> ByRef<Self> {
ByRef(self)
}
}
impl<I> IteratorExt for I where I: Iterator {}
struct ByRef<'a, I: 'a + Iterator>(&'a mut I);
impl<'a, I: Iterator> Iterator for ByRef<'a, I> {
type Item = I::Item;
fn next(&mut self) -> Option< <I as Iterator>::Item > {
self.0.next()
}
}
fn is_iterator_of<A, I: Iterator<Item=A>>(_: &I) {}
fn test<A, I: Iterator<Item=A>>(mut it: I) {
is_iterator_of::<A, _>(&it.by_ref());
}
fn test2<A, I1: Iterator<Item=A>, I2: Iterator<Item=I1::Item>>(mut it: I2) {
is_iterator_of::<A, _>(&it)
}
fn main() { }

View File

@ -0,0 +1,17 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we are able to have an impl that defines an associated type
// before the actual trait.
#![feature(associated_types)]
impl X for f64 { type Y = int; }
trait X {type Y; }
fn main() {}

View File

@ -0,0 +1,41 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we normalize associated types that appear in bounds; if
// we didn't, the call to `self.split2()` fails to type check.
#![feature(associated_types)]
struct Splits<'a, T, P>;
struct SplitsN<I>;
trait SliceExt2 for Sized? {
type Item;
fn split2<'a, P>(&'a self, pred: P) -> Splits<'a, Self::Item, P>
where P: FnMut(&Self::Item) -> bool;
fn splitn2<'a, P>(&'a self, n: uint, pred: P) -> SplitsN<Splits<'a, Self::Item, P>>
where P: FnMut(&Self::Item) -> bool;
}
impl<T> SliceExt2 for [T] {
type Item = T;
fn split2<P>(&self, pred: P) -> Splits<T, P> where P: FnMut(&T) -> bool {
loop {}
}
fn splitn2<P>(&self, n: uint, pred: P) -> SplitsN<Splits<T, P>> where P: FnMut(&T) -> bool {
SliceExt2::split2(self, pred);
loop {}
}
}
fn main() { }

View File

@ -0,0 +1,33 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we correctly handle projection bounds appearing in the
// supertrait list (and in conjunction with overloaded operators). In
// this case, the `Result=Self` binding in the supertrait listing of
// `Int` was being ignored.
#![feature(associated_types)]
trait Not {
type Result;
fn not(self) -> Self::Result;
}
trait Int: Not<Result=Self> {
fn count_ones(self) -> uint;
fn count_zeros(self) -> uint {
// neither works
let x: Self = self.not();
0
}
}
fn main() { }

View File

@ -0,0 +1,54 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test how resolving a projection interacts with inference. In this
// case, we were eagerly unifying the type variable for the iterator
// type with `I` from the where clause, ignoring the in-scope `impl`
// for `ByRef`. The right answer was to consider the result ambiguous
// until more type information was available.
// ignore-pretty -- FIXME(#17362) pretty prints with `<<` which lexes wrong
#![feature(associated_types, lang_items, unboxed_closures)]
#![no_implicit_prelude]
use std::option::Option::{None, Some, mod};
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
trait IteratorExt: Iterator {
fn by_ref(&mut self) -> ByRef<Self> {
ByRef(self)
}
}
impl<I> IteratorExt for I where I: Iterator {}
struct ByRef<'a, I: 'a + Iterator>(&'a mut I);
impl<'a, A, I> Iterator for ByRef<'a, I> where I: Iterator<Item=A> {
type Item = A;
fn next(&mut self) -> Option< <I as Iterator>::Item > {
self.0.next()
}
}
fn is_iterator_of<A, I: Iterator<Item=A>>(_: &I) {}
fn test<A, I: Iterator<Item=A>>(mut it: I) {
is_iterator_of::<A, _>(&it.by_ref());
}
fn main() { }