diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index db29d0111f446..b3b04a664529d 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -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, diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index a3d92c698a2ad..a0413701abcad 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 self_ty = trait_ref.self_ty(); + let predicate = infcx.resolve_type_vars_if_possible(&obligation.predicate); - debug!("maybe_report_ambiguity(trait_ref={}, self_ty={}, obligation={})", - trait_ref.repr(infcx.tcx), - self_ty.repr(infcx.tcx), + debug!("maybe_report_ambiguity(predicate={}, obligation={})", + predicate.repr(infcx.tcx), obligation.repr(infcx.tcx)); - let all_types = &trait_ref.substs().types; - if all_types.iter().any(|&t| ty::type_is_error(t)) { - } else if all_types.iter().any(|&t| ty::type_needs_infer(t)) { - // This is kind of a hack: it frequently happens that some earlier - // error prevents types from being fully inferred, and then we get - // a bunch of uninteresting errors saying something like " 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", + "coherence failed to report ambiguity: \ + cannot locate the impl of the trait `{}` for \ + the type `{}`", + trait_ref.user_string(infcx.tcx), self_ty.user_string(infcx.tcx)).as_slice()); - } else { + } + } + + _ => { + if !infcx.tcx.sess.has_errors() { infcx.tcx.sess.span_err( obligation.cause.span, format!( - "unable to infer enough type information to \ - locate the impl of the trait `{}` for \ - the type `{}`; type annotations required", - trait_ref.user_string(infcx.tcx), - self_ty.user_string(infcx.tcx)).as_slice()); + "type annotations required: cannot resolve `{}`", + predicate.user_string(infcx.tcx)).as_slice()); note_obligation_cause(infcx, obligation); } } - } else if !infcx.tcx.sess.has_errors() { - // Ambiguity. Coherence should have reported an error. - infcx.tcx.sess.span_bug( - obligation.cause.span, - format!( - "coherence failed to report ambiguity: \ - cannot locate the impl of the trait `{}` for \ - the type `{}`", - trait_ref.user_string(infcx.tcx), - self_ty.user_string(infcx.tcx)).as_slice()); } } @@ -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 7ec221fcfa903..c85baccd6336d 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); + + for obligation in normalized.obligations.into_iter() { + self.register_predicate_obligation(infcx, obligation); + } - debug!("normalize_associated_type: result={}", ty_var.repr(infcx.tcx)); + debug!("normalize_associated_type: result={}", normalized.value.repr(infcx.tcx)); - ty_var + normalized.value } pub fn register_builtin_bound<'a>(&mut self, @@ -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 d4fa0c98ad5d1..b10dfa5b71813 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 435babf168e86..28a826b859b4c 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,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>>, 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_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. - - let () = try!(assemble_candidates_from_impls(selcx, - obligation, - &mut candidates)); + assemble_candidates_from_param_env(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 `>::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>) { 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> { } } } - diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index ab39c761a3861..dab24f6a16380 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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`, 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>, + types: &mut VecPerParamSpace>, 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 { + 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 { + 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() } } diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 06819aac5bc04..7c2585becea8c 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -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, ¶m_env, tcx) { + let vtable = drain_fulfillment_cx(span, &infcx, ¶m_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. diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 3b7043e4f40f0..fdcb232e125fd 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}; @@ -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, ¶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)); - 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) - } - } + 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, ¶m_env, &mut fulfill_cx, &result); + + result } diff --git a/src/librustc_typeck/check/assoc.rs b/src/librustc_typeck/check/assoc.rs index 081959a4efa4a..98081e28f2fd7 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/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 1971be117605c..4e6593dedddf7 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -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), diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 1a9e124521e0f..f00e3e2d4527a 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -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. diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 8069d00dda826..12cebabc997a4 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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}; @@ -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, @@ -1741,10 +1752,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { result } + /// 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> + { + ty::GenericBounds { + predicates: self.instantiate_type_scheme(span, substs, &generics.predicates) + } + } + + 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, @@ -1759,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, @@ -1852,7 +1879,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, @@ -1929,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> { @@ -4455,7 +4482,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, @@ -5270,31 +5297,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 }); diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs index 2a3f528809cfd..410697b0aca2a 100644 --- a/src/librustc_typeck/check/wf.rs +++ b/src/librustc_typeck/check/wf.rs @@ -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, diff --git a/src/test/compile-fail/associated-types-issue-20346.rs b/src/test/compile-fail/associated-types-issue-20346.rs new file mode 100644 index 0000000000000..255b8a25a4cab --- /dev/null +++ b/src/test/compile-fail/associated-types-issue-20346.rs @@ -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 or the MIT license +// , 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; +} + +fn is_iterator_of>(_: &I) {} + +struct Adapter { + iter: I, + found_none: bool, +} + +impl Iterator for Adapter where I: Iterator> { + type Item = T; + + fn next(&mut self) -> Option { + loop {} + } +} + +fn test_adapter>>(it: I) { + is_iterator_of::, _>(&it); // Sanity check + let adapter = Adapter { iter: it, found_none: false }; + is_iterator_of::(&adapter); // OK + is_iterator_of::, _>(&adapter); //~ ERROR type mismatch +} diff --git a/src/test/compile-fail/associated-types-path-2.rs b/src/test/compile-fail/associated-types-path-2.rs index 486d3d38c60af..989214d81da9e 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 +pub fn f1_int_int() { + f1(2i, 4i); + //~^ ERROR expected uint, found int +} + +pub fn f1_int_uint() { f1(2i, 4u); - f1(2u, 4u); //~ ERROR the trait `Foo` is not implemented - f1(2u, 4i); //~ ERROR the trait `Foo` is not implemented +} + +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 +} - let _: int = f2(2i); //~ERROR expected `int`, found `uint` +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 8cdca50d9b6bd..0e13efdebc955 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 0000000000000..02e1121880678 --- /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 9ceae41d1a465..3c461fd5b4b70 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 0000000000000..a28cf34633684 --- /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() { } diff --git a/src/test/run-pass/associated-types-issue-20371.rs b/src/test/run-pass/associated-types-issue-20371.rs new file mode 100644 index 0000000000000..a6fb2b9e2ea53 --- /dev/null +++ b/src/test/run-pass/associated-types-issue-20371.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/run-pass/associated-types-normalize-in-bounds-ufcs.rs b/src/test/run-pass/associated-types-normalize-in-bounds-ufcs.rs new file mode 100644 index 0000000000000..0fd477204215e --- /dev/null +++ b/src/test/run-pass/associated-types-normalize-in-bounds-ufcs.rs @@ -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 or the MIT license +// , 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; + +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> + where P: FnMut(&Self::Item) -> bool; +} + +impl SliceExt2 for [T] { + type Item = T; + + fn split2

(&self, pred: P) -> Splits where P: FnMut(&T) -> bool { + loop {} + } + + fn splitn2

(&self, n: uint, pred: P) -> SplitsN> where P: FnMut(&T) -> bool { + SliceExt2::split2(self, pred); + loop {} + } +} + +fn main() { } diff --git a/src/test/run-pass/associated-types-projection-bound-in-supertraits.rs b/src/test/run-pass/associated-types-projection-bound-in-supertraits.rs new file mode 100644 index 0000000000000..83686d92a7cd9 --- /dev/null +++ b/src/test/run-pass/associated-types-projection-bound-in-supertraits.rs @@ -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 or the MIT license +// , 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 { + fn count_ones(self) -> uint; + fn count_zeros(self) -> uint { + // neither works + let x: Self = self.not(); + 0 + } +} + +fn main() { } diff --git a/src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs b/src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs new file mode 100644 index 0000000000000..062d37556ec29 --- /dev/null +++ b/src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs @@ -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 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) 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; +} + +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, A, I> Iterator for ByRef<'a, I> where I: Iterator { + type Item = A; + + 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 main() { }