From 518ec1259a967142fcfbf7cb6dd2d4a3dd5610cf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Dec 2014 06:29:59 -0500 Subject: [PATCH] Normalize associated types in bounds too. Also, make the workaround for lack of impl-trait-for-trait just a bit more targeted (don't substitute err, just drop the troublesome bound for now) -- otherwise substituting false types leads us into trouble when we normalize etc. --- src/librustc/middle/ty.rs | 98 ++++++++++++++----- src/librustc_typeck/check/assoc.rs | 30 +++--- src/librustc_typeck/check/method/confirm.rs | 44 +++------ src/librustc_typeck/collect.rs | 2 +- .../associated-types-normalize-in-bounds.rs | 41 ++++++++ 5 files changed, 149 insertions(+), 66 deletions(-) create mode 100644 src/test/run-pass/associated-types-normalize-in-bounds.rs diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index caec56800c6..d6ab0201d19 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -6970,13 +6970,67 @@ pub trait HasProjectionTypes { fn has_projection_types(&self) -> bool; } +impl<'tcx> HasProjectionTypes for ty::GenericBounds<'tcx> { + fn has_projection_types(&self) -> bool { + self.predicates.iter().any(|p| p.has_projection_types()) + } +} + +impl<'tcx> HasProjectionTypes for Predicate<'tcx> { + fn has_projection_types(&self) -> bool { + match *self { + Predicate::Trait(ref data) => data.has_projection_types(), + Predicate::Equate(ref data) => data.has_projection_types(), + Predicate::RegionOutlives(ref data) => data.has_projection_types(), + Predicate::TypeOutlives(ref data) => data.has_projection_types(), + Predicate::Projection(ref data) => data.has_projection_types(), + } + } +} + +impl<'tcx> HasProjectionTypes for TraitPredicate<'tcx> { + fn has_projection_types(&self) -> bool { + self.trait_ref.has_projection_types() + } +} + +impl<'tcx> HasProjectionTypes for EquatePredicate<'tcx> { + fn has_projection_types(&self) -> bool { + self.0.has_projection_types() || self.1.has_projection_types() + } +} + +impl HasProjectionTypes for Region { + fn has_projection_types(&self) -> bool { + false + } +} + +impl HasProjectionTypes for OutlivesPredicate { + fn has_projection_types(&self) -> bool { + self.0.has_projection_types() || self.1.has_projection_types() + } +} + +impl<'tcx> HasProjectionTypes for ProjectionPredicate<'tcx> { + fn has_projection_types(&self) -> bool { + self.projection_ty.has_projection_types() || self.ty.has_projection_types() + } +} + +impl<'tcx> HasProjectionTypes for ProjectionTy<'tcx> { + fn has_projection_types(&self) -> bool { + self.trait_ref.has_projection_types() + } +} + impl<'tcx> HasProjectionTypes for Ty<'tcx> { fn has_projection_types(&self) -> bool { ty::type_has_projection(*self) } } -impl<'tcx> HasProjectionTypes for ty::TraitRef<'tcx> { +impl<'tcx> HasProjectionTypes for TraitRef<'tcx> { fn has_projection_types(&self) -> bool { self.substs.has_projection_types() } @@ -7012,7 +7066,7 @@ impl<'tcx,T> HasProjectionTypes for Box } } -impl HasProjectionTypes for ty::Binder +impl HasProjectionTypes for Binder where T : HasProjectionTypes { fn has_projection_types(&self) -> bool { @@ -7020,23 +7074,23 @@ impl HasProjectionTypes for ty::Binder } } -impl<'tcx> HasProjectionTypes for ty::FnOutput<'tcx> { +impl<'tcx> HasProjectionTypes for FnOutput<'tcx> { fn has_projection_types(&self) -> bool { match *self { - ty::FnConverging(t) => t.has_projection_types(), - ty::FnDiverging => false, + FnConverging(t) => t.has_projection_types(), + FnDiverging => false, } } } -impl<'tcx> HasProjectionTypes for ty::FnSig<'tcx> { +impl<'tcx> HasProjectionTypes for FnSig<'tcx> { fn has_projection_types(&self) -> bool { self.inputs.iter().any(|t| t.has_projection_types()) || self.output.has_projection_types() } } -impl<'tcx> HasProjectionTypes for ty::BareFnTy<'tcx> { +impl<'tcx> HasProjectionTypes for BareFnTy<'tcx> { fn has_projection_types(&self) -> bool { self.sig.has_projection_types() } @@ -7046,7 +7100,7 @@ pub trait ReferencesError { fn references_error(&self) -> bool; } -impl ReferencesError for ty::Binder { +impl ReferencesError for Binder { fn references_error(&self) -> bool { self.0.references_error() } @@ -7058,43 +7112,43 @@ impl ReferencesError for Rc { } } -impl<'tcx> ReferencesError for ty::TraitPredicate<'tcx> { +impl<'tcx> ReferencesError for TraitPredicate<'tcx> { fn references_error(&self) -> bool { self.trait_ref.references_error() } } -impl<'tcx> ReferencesError for ty::ProjectionPredicate<'tcx> { +impl<'tcx> ReferencesError for ProjectionPredicate<'tcx> { fn references_error(&self) -> bool { self.projection_ty.trait_ref.references_error() || self.ty.references_error() } } -impl<'tcx> ReferencesError for ty::TraitRef<'tcx> { +impl<'tcx> ReferencesError for TraitRef<'tcx> { fn references_error(&self) -> bool { self.input_types().iter().any(|t| t.references_error()) } } -impl<'tcx> ReferencesError for ty::Ty<'tcx> { +impl<'tcx> ReferencesError for Ty<'tcx> { fn references_error(&self) -> bool { - ty::type_is_error(*self) + type_is_error(*self) } } -impl<'tcx> ReferencesError for ty::Predicate<'tcx> { +impl<'tcx> ReferencesError for Predicate<'tcx> { fn references_error(&self) -> bool { match *self { - ty::Predicate::Trait(ref data) => data.references_error(), - ty::Predicate::Equate(ref data) => data.references_error(), - ty::Predicate::RegionOutlives(ref data) => data.references_error(), - ty::Predicate::TypeOutlives(ref data) => data.references_error(), - ty::Predicate::Projection(ref data) => data.references_error(), + Predicate::Trait(ref data) => data.references_error(), + Predicate::Equate(ref data) => data.references_error(), + Predicate::RegionOutlives(ref data) => data.references_error(), + Predicate::TypeOutlives(ref data) => data.references_error(), + Predicate::Projection(ref data) => data.references_error(), } } } -impl ReferencesError for ty::OutlivesPredicate +impl ReferencesError for OutlivesPredicate where A : ReferencesError, B : ReferencesError { fn references_error(&self) -> bool { @@ -7102,14 +7156,14 @@ impl ReferencesError for ty::OutlivesPredicate } } -impl<'tcx> ReferencesError for ty::EquatePredicate<'tcx> +impl<'tcx> ReferencesError for EquatePredicate<'tcx> { fn references_error(&self) -> bool { self.0.references_error() || self.1.references_error() } } -impl ReferencesError for ty::Region +impl ReferencesError for Region { fn references_error(&self) -> bool { false diff --git a/src/librustc_typeck/check/assoc.rs b/src/librustc_typeck/check/assoc.rs index c20dd5aebad..0d7ce2f871a 100644 --- a/src/librustc_typeck/check/assoc.rs +++ b/src/librustc_typeck/check/assoc.rs @@ -10,7 +10,7 @@ use middle::infer::InferCtxt; use middle::traits::{ObligationCause, ObligationCauseCode, FulfillmentContext}; -use middle::ty::{mod, HasProjectionTypes, Ty}; +use middle::ty::{mod, RegionEscape, HasProjectionTypes, Ty}; use middle::ty_fold::{mod, TypeFoldable, TypeFolder}; use syntax::ast; use syntax::codemap::Span; @@ -32,8 +32,7 @@ pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, let mut normalizer = AssociatedTypeNormalizer { span: span, body_id: body_id, infcx: infcx, - fulfillment_cx: fulfillment_cx, - region_binders: 0 }; + fulfillment_cx: fulfillment_cx }; value.fold_with(&mut normalizer) } @@ -42,7 +41,6 @@ struct AssociatedTypeNormalizer<'a,'tcx:'a> { fulfillment_cx: &'a mut FulfillmentContext<'tcx>, span: Span, body_id: ast::NodeId, - region_binders: uint, } impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> { @@ -50,14 +48,6 @@ impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> { self.infcx.tcx } - fn enter_region_binder(&mut self) { - self.region_binders += 1; - } - - fn exit_region_binder(&mut self) { - self.region_binders -= 1; - } - 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. @@ -69,10 +59,22 @@ impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> { // Instead of normalizing `>::A` here, we'll // normalize it when we instantiate those bound regions (which // should occur eventually). - let no_region_binders = self.region_binders == 0; match ty.sty { - ty::ty_projection(ref data) if no_region_binders => { + 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, diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 9704439c468..a189f780b0c 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -42,12 +42,6 @@ struct InstantiatedMethodSig<'tcx> { /// the method. all_substs: subst::Substs<'tcx>, - /// Substitution to use when adding obligations from the method - /// bounds. Normally equal to `all_substs` except for object - /// receivers. See FIXME in instantiate_method_sig() for - /// explanation. - method_bounds_substs: subst::Substs<'tcx>, - /// Generic bounds on the method's parameters which must be added /// as pending obligations. method_bounds: ty::GenericBounds<'tcx>, @@ -103,7 +97,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { // Create the final signature for the method, replacing late-bound regions. let InstantiatedMethodSig { - method_sig, all_substs, method_bounds_substs, method_bounds + method_sig, all_substs, method_bounds } = self.instantiate_method_sig(&pick, all_substs); let method_self_ty = method_sig.inputs[0]; @@ -111,7 +105,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { self.unify_receivers(self_ty, method_self_ty); // Add any trait/regions obligations specified on the method's type parameters. - self.add_obligations(&pick, &method_bounds_substs, &method_bounds); + self.add_obligations(&pick, &all_substs, &method_bounds); // Create the final `MethodCallee`. let fty = ty::mk_bare_fn(self.tcx(), None, self.tcx().mk_bare_fn(ty::BareFnTy { @@ -403,24 +397,17 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { // type `Trait`, this leads to an obligation // `Trait:Trait`. Until such time we DST is fully implemented, // that obligation is not necessarily satisfied. (In the - // future, it would be.) - // - // To sidestep this, we overwrite the binding for `Self` with - // `err` (just for trait objects) when we generate the - // obligations. This causes us to generate the obligation - // `err:Trait`, and the error type is considered to implement - // all traits, so we're all good. Hack hack hack. - let method_bounds_substs = match pick.kind { + // future, it would be.) But we know that the true `Self` DOES implement + // the trait. So we just delete this requirement. Hack hack hack. + let mut method_bounds = pick.method_ty.generics.to_bounds(self.tcx(), &all_substs); + match pick.kind { probe::ObjectPick(..) => { - let mut temp_substs = all_substs.clone(); - temp_substs.types.get_mut_slice(subst::SelfSpace)[0] = self.tcx().types.err; - temp_substs + assert_eq!(method_bounds.predicates.get_slice(subst::SelfSpace).len(), 1); + method_bounds.predicates.pop(subst::SelfSpace); } - _ => { - all_substs.clone() - } - }; - let method_bounds = pick.method_ty.generics.to_bounds(self.tcx(), &method_bounds_substs); + _ => { } + } + let method_bounds = self.fcx.normalize_associated_types_in(self.span, &method_bounds); debug!("method_bounds after subst = {}", method_bounds.repr(self.tcx())); @@ -442,18 +429,17 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { InstantiatedMethodSig { method_sig: method_sig, all_substs: all_substs, - method_bounds_substs: method_bounds_substs, method_bounds: method_bounds, } } fn add_obligations(&mut self, pick: &probe::Pick<'tcx>, - method_bounds_substs: &subst::Substs<'tcx>, + all_substs: &subst::Substs<'tcx>, method_bounds: &ty::GenericBounds<'tcx>) { - debug!("add_obligations: pick={} method_bounds_substs={} method_bounds={}", + debug!("add_obligations: pick={} all_substs={} method_bounds={}", pick.repr(self.tcx()), - method_bounds_substs.repr(self.tcx()), + all_substs.repr(self.tcx()), method_bounds.repr(self.tcx())); self.fcx.add_obligations_for_parameters( @@ -461,7 +447,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { method_bounds); self.fcx.add_default_region_param_bounds( - method_bounds_substs, + all_substs, self.call_expr); } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 8f70966dd14..47473564254 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1071,7 +1071,7 @@ fn ty_generics_for_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, debug!("ty_generics_for_trait: assoc_predicates={}", assoc_predicates.repr(ccx.tcx)); for assoc_predicate in assoc_predicates.into_iter() { - generics.predicates.push(subst::SelfSpace, assoc_predicate); + generics.predicates.push(subst::TypeSpace, assoc_predicate); } return generics; diff --git a/src/test/run-pass/associated-types-normalize-in-bounds.rs b/src/test/run-pass/associated-types-normalize-in-bounds.rs new file mode 100644 index 00000000000..f09c27029d7 --- /dev/null +++ b/src/test/run-pass/associated-types-normalize-in-bounds.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 { + self.split2(pred); + loop {} + } +} + +fn main() { }