From 6cb425d964637da3ffa99cac902bf8fe696baf08 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Dec 2014 17:42:02 -0500 Subject: [PATCH] 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. --- src/librustc/middle/traits/error_reporting.rs | 136 +++---- src/librustc/middle/traits/fulfill.rs | 135 ++----- src/librustc/middle/traits/mod.rs | 14 +- src/librustc/middle/traits/project.rs | 367 ++++++++++++++---- src/librustc_trans/trans/common.rs | 2 +- src/librustc_trans/trans/monomorphize.rs | 64 +-- src/librustc_typeck/check/assoc.rs | 89 +---- src/librustc_typeck/check/mod.rs | 25 +- .../compile-fail/associated-types-path-2.rs | 33 +- ...ated-types-project-from-hrtb-in-fn-body.rs | 3 +- .../associated-types-unconstrained.rs | 28 ++ ...traits-multidispatch-convert-ambig-dest.rs | 2 +- .../associated-types-impl-redirect.rs | 58 +++ 13 files changed, 578 insertions(+), 378 deletions(-) create mode 100644 src/test/compile-fail/associated-types-unconstrained.rs create mode 100644 src/test/run-pass/associated-types-impl-redirect.rs diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index a3d92c698a2..a0413701abc 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -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 " 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 " 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)[]); +} diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index 7ec221fcfa9..c85baccd633 100644 --- a/src/librustc/middle/traits/fulfill.rs +++ b/src/librustc/middle/traits/fulfill.rs @@ -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 `::name = U` but we couldn't find any more - // information. This could just be that we're in a - // function like: - // - // fn foo(...) - // - // 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 `::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 `::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> 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 `::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 } } diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index d4fa0c98ad5..b10dfa5b718 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -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) } diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 0fa171523f4..28a826b859b 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -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 `::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`. When - /// normalizing, this case is where we opt to normalize back to - /// the projection type `::Name`. - NoCandidate, - +/// When attempting to resolve `::Name` ... +pub enum ProjectionTyError<'tcx> { /// ...we found multiple sources of information and couldn't resolve the ambiguity. TooManyCandidates, - /// ...`` 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>; - 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>>, 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,48 +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>>, 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>, + 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 + 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(>::A) + // + // Instead of normalizing `>::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>, +} + +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> +{ + 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>), + 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, 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_object_type(selcx, - obligation, - &mut candidates); + assemble_candidates_from_object_type(selcx, + obligation, + &mut candidates); if candidates.vec.is_empty() { - let () = assemble_candidates_from_param_env(selcx, - obligation, - &mut candidates); + 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={}", @@ -146,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))) } } } @@ -230,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 `>::Item == Type`, // start out by selecting the predicate `T as TraitRef<...>`: @@ -249,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); } }; @@ -301,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>) { let infcx = selcx.infcx(); @@ -311,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( @@ -338,7 +584,7 @@ fn confirm_candidate<'cx,'tcx>( } } - projection.ty + (projection.ty, vec!()) } ProjectionTyCandidate::Impl(impl_vtable) => { @@ -363,10 +609,8 @@ fn confirm_candidate<'cx,'tcx>( break; } - // TODO we need the impl_vtable items here - match impl_ty { - Some(ty) => ty, + Some(ty) => (ty, impl_vtable.nested.to_vec()), None => { selcx.tcx().sess.span_bug( obligation.cause.span, @@ -376,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)), } } @@ -406,4 +644,3 @@ impl<'tcx> Repr<'tcx> for ProjectionTyCandidate<'tcx> { } } } - diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 06819aac5bc..1b00a77fe51 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -960,7 +960,7 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // iterating early. 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, ¶m_env, tcx) { Ok(()) => { } diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 3b7043e4f40..c693c6ea428 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -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}; @@ -32,7 +32,6 @@ use syntax::ast_map; use syntax::ast_util::{local_def, PostExpansionMethod}; use syntax::attr; 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 +309,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 +323,15 @@ 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, ¶m_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)); + + assert_eq!(obligations.len(), 0); // TODO not good enough 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) - } - } - } -} diff --git a/src/librustc_typeck/check/assoc.rs b/src/librustc_typeck/check/assoc.rs index 081959a4efa..98081e28f2f 100644 --- a/src/librustc_typeck/check/assoc.rs +++ b/src/librustc_typeck/check/assoc.rs @@ -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(>::A) - // - // Instead of normalizing `>::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 } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e2691a778ac..12cebabc997 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -350,11 +350,18 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { } } - fn normalize_associated_types_in(&self, span: Span, body_id: ast::NodeId, value: &T) -> T - where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + fn normalize_associated_types_in(&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) @@ -438,7 +445,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); @@ -1190,6 +1197,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, @@ -1209,6 +1218,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, @@ -1756,9 +1767,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn normalize_associated_types_in(&self, span: Span, value: &T) -> T - where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + Repr<'tcx> { - self.inh.normalize_associated_types_in(span, self.body_id, value) + self.inh.normalize_associated_types_in(self, span, self.body_id, value) } fn normalize_associated_type(&self, @@ -1773,6 +1784,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, @@ -1943,7 +1956,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> { diff --git a/src/test/compile-fail/associated-types-path-2.rs b/src/test/compile-fail/associated-types-path-2.rs index 486d3d38c60..989214d81da 100644 --- a/src/test/compile-fail/associated-types-path-2.rs +++ b/src/test/compile-fail/associated-types-path-2.rs @@ -25,11 +25,30 @@ pub fn f2(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() { } diff --git a/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs b/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs index 8cdca50d9b6..0e13efdebc9 100644 --- a/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs +++ b/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs @@ -29,9 +29,10 @@ fn bar<'a, 'b, I : for<'x> Foo<&'x int>>( x: >::A, y: >::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() {} diff --git a/src/test/compile-fail/associated-types-unconstrained.rs b/src/test/compile-fail/associated-types-unconstrained.rs new file mode 100644 index 00000000000..02e11218806 --- /dev/null +++ b/src/test/compile-fail/associated-types-unconstrained.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs b/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs index 9ceae41d1a4..3c461fd5b4b 100644 --- a/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs +++ b/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs @@ -33,7 +33,7 @@ where T : Convert } fn a() { - test(22_i32, 44); //~ ERROR unable to infer + test(22_i32, 44); //~ ERROR type annotations required } fn main() {} diff --git a/src/test/run-pass/associated-types-impl-redirect.rs b/src/test/run-pass/associated-types-impl-redirect.rs new file mode 100644 index 00000000000..a28cf346336 --- /dev/null +++ b/src/test/run-pass/associated-types-impl-redirect.rs @@ -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 or the MIT license +// , 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; +} + +trait IteratorExt: Iterator { + fn by_ref(&mut self) -> ByRef { + ByRef(self) + } +} + +impl 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< ::Item > { + self.0.next() + } +} + +fn is_iterator_of>(_: &I) {} + +fn test>(mut it: I) { + is_iterator_of::(&it.by_ref()); +} + +fn test2, I2: Iterator>(mut it: I2) { + is_iterator_of::(&it) +} + +fn main() { }