diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2017-11-02 06:06:09 -0400 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2017-11-15 16:49:21 -0500 |
| commit | d73be851fbcaa2887d390192c6774b3792411c9f (patch) | |
| tree | 6cad96481c59f9ee2784df07f12b4e68fedbfd96 | |
| parent | c925008a5ce44fd5f4755279793c64bb9ccb50f4 (diff) | |
| download | rust-d73be851fbcaa2887d390192c6774b3792411c9f.tar.gz rust-d73be851fbcaa2887d390192c6774b3792411c9f.zip | |
extract `regionck_outlives` into a separate helper function
This helps make its inputs and outputs more clear.
| -rw-r--r-- | src/librustc/infer/mod.rs | 21 | ||||
| -rw-r--r-- | src/librustc_typeck/check/mod.rs | 32 | ||||
| -rw-r--r-- | src/librustc_typeck/check/regionck.rs | 366 | ||||
| -rw-r--r-- | src/librustc_typeck/check/regionck_outlives.rs | 445 | ||||
| -rw-r--r-- | src/librustc_typeck/lib.rs | 1 |
5 files changed, 498 insertions, 367 deletions
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index ba99ff5291a..c76d098bd69 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1451,6 +1451,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tcx.generator_sig(def_id) } + + /// Normalizes associated types in `value`, potentially returning + /// new obligations that must further be processed. + pub fn partially_normalize_associated_types_in<T>(&self, + span: Span, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'tcx>, + value: &T) + -> InferOk<'tcx, T> + where T : TypeFoldable<'tcx> + { + debug!("partially_normalize_associated_types_in(value={:?})", value); + let mut selcx = traits::SelectionContext::new(self); + let cause = ObligationCause::misc(span, body_id); + let traits::Normalized { value, obligations } = + traits::normalize(&mut selcx, param_env, cause, value); + debug!("partially_normalize_associated_types_in: result={:?} predicates={:?}", + value, + obligations); + InferOk { value, obligations } + } } impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 19ea1b17950..0c4a5512dd6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -136,7 +136,8 @@ mod autoderef; pub mod dropck; pub mod _match; pub mod writeback; -pub mod regionck; +mod regionck; +mod regionck_outlives; pub mod coercion; pub mod demand; pub mod method; @@ -657,29 +658,10 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> { value: &T) -> T where T : TypeFoldable<'tcx> { - let ok = self.normalize_associated_types_in_as_infer_ok(span, body_id, param_env, value); + let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value); self.register_infer_ok_obligations(ok) } - fn normalize_associated_types_in_as_infer_ok<T>(&self, - span: Span, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'tcx>, - value: &T) - -> InferOk<'tcx, T> - where T : TypeFoldable<'tcx> - { - debug!("normalize_associated_types_in(value={:?})", value); - let mut selcx = traits::SelectionContext::new(self); - let cause = ObligationCause::misc(span, body_id); - let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, param_env, cause, value); - debug!("normalize_associated_types_in: result={:?} predicates={:?}", - value, - obligations); - InferOk { value, obligations } - } - /// Replace any late-bound regions bound in `value` with /// free variants attached to `all_outlive_scope`. fn liberate_late_bound_regions<T>(&self, @@ -1970,10 +1952,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { -> InferOk<'tcx, T> where T : TypeFoldable<'tcx> { - self.inh.normalize_associated_types_in_as_infer_ok(span, - self.body_id, - self.param_env, - value) + self.inh.partially_normalize_associated_types_in(span, + self.body_id, + self.param_env, + value) } pub fn require_type_meets(&self, diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index b1ac3abe230..77a34023df3 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -92,7 +92,7 @@ use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use rustc::traits; use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::infer::{self, GenericKind, SubregionOrigin, VerifyBound}; +use rustc::infer::{self, GenericKind, SubregionOrigin}; use rustc::ty::adjustment; use rustc::ty::outlives::Component; use rustc::ty::wf; @@ -105,6 +105,8 @@ use syntax_pos::Span; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::{self, PatKind}; +use super::regionck_outlives::RegionckOutlives; + // a variation on try that just returns unit macro_rules! ignore_err { ($e:expr) => (match $e { Ok(e) => e, Err(_) => return () }) @@ -1132,6 +1134,27 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_must_outlive(origin, ty, minimum_lifetime); } + /// Adds constraints to inference such that `T: 'a` holds (or + /// reports an error if it cannot). + /// + /// # Parameters + /// + /// - `origin`, the reason we need this constraint + /// - `ty`, the type `T` + /// - `region`, the region `'a` + pub fn type_must_outlive(&self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>) + { + let outlives = RegionckOutlives::new(&self.infcx, + &self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + self.body_id); + self.register_infer_ok_obligations(outlives.type_must_outlive(origin, ty, region)); + } + /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the /// resulting pointer is linked to the lifetime of its guarantor (if any). fn link_addr_of(&mut self, expr: &hir::Expr, @@ -1487,345 +1510,4 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_must_outlive(origin.clone(), ty, expr_region); } } - - /// Ensures that type is well-formed in `region`, which implies (among - /// other things) that all borrowed data reachable via `ty` outlives - /// `region`. - pub fn type_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - ty: Ty<'tcx>, - region: ty::Region<'tcx>) - { - let ty = self.resolve_type(ty); - - debug!("type_must_outlive(ty={:?}, region={:?}, origin={:?})", - ty, - region, - origin); - - assert!(!ty.has_escaping_regions()); - - let components = self.tcx.outlives_components(ty); - self.components_must_outlive(origin, components, region); - } - - fn components_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - components: Vec<Component<'tcx>>, - region: ty::Region<'tcx>) - { - for component in components { - let origin = origin.clone(); - match component { - Component::Region(region1) => { - self.sub_regions(origin, region, region1); - } - Component::Param(param_ty) => { - self.param_ty_must_outlive(origin, region, param_ty); - } - Component::Projection(projection_ty) => { - self.projection_must_outlive(origin, region, projection_ty); - } - Component::EscapingProjection(subcomponents) => { - self.components_must_outlive(origin, subcomponents, region); - } - Component::UnresolvedInferenceVariable(v) => { - // ignore this, we presume it will yield an error - // later, since if a type variable is not resolved by - // this point it never will be - self.tcx.sess.delay_span_bug( - origin.span(), - &format!("unresolved inference variable in outlives: {:?}", v)); - } - } - } - } - - fn param_ty_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - region: ty::Region<'tcx>, - param_ty: ty::ParamTy) { - debug!("param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})", - region, param_ty, origin); - - let verify_bound = self.param_bound(param_ty); - let generic = GenericKind::Param(param_ty); - self.verify_generic_bound(origin, generic, region, verify_bound); - } - - fn projection_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - region: ty::Region<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>) - { - debug!("projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", - region, projection_ty, origin); - - // This case is thorny for inference. The fundamental problem is - // that there are many cases where we have choice, and inference - // doesn't like choice (the current region inference in - // particular). :) First off, we have to choose between using the - // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and - // OutlivesProjectionComponent rules, any one of which is - // sufficient. If there are no inference variables involved, it's - // not hard to pick the right rule, but if there are, we're in a - // bit of a catch 22: if we picked which rule we were going to - // use, we could add constraints to the region inference graph - // that make it apply, but if we don't add those constraints, the - // rule might not apply (but another rule might). For now, we err - // on the side of adding too few edges into the graph. - - // Compute the bounds we can derive from the environment or trait - // definition. We know that the projection outlives all the - // regions in this list. - let env_bounds = self.projection_declared_bounds(origin.span(), projection_ty); - - debug!("projection_must_outlive: env_bounds={:?}", - env_bounds); - - // If we know that the projection outlives 'static, then we're - // done here. - if env_bounds.contains(&&ty::ReStatic) { - debug!("projection_must_outlive: 'static as declared bound"); - return; - } - - // If declared bounds list is empty, the only applicable rule is - // OutlivesProjectionComponent. If there are inference variables, - // then, we can break down the outlives into more primitive - // components without adding unnecessary edges. - // - // If there are *no* inference variables, however, we COULD do - // this, but we choose not to, because the error messages are less - // good. For example, a requirement like `T::Item: 'r` would be - // translated to a requirement that `T: 'r`; when this is reported - // to the user, it will thus say "T: 'r must hold so that T::Item: - // 'r holds". But that makes it sound like the only way to fix - // the problem is to add `T: 'r`, which isn't true. So, if there are no - // inference variables, we use a verify constraint instead of adding - // edges, which winds up enforcing the same condition. - let needs_infer = projection_ty.needs_infer(); - if env_bounds.is_empty() && needs_infer { - debug!("projection_must_outlive: no declared bounds"); - - for component_ty in projection_ty.substs.types() { - self.type_must_outlive(origin.clone(), component_ty, region); - } - - for r in projection_ty.substs.regions() { - self.sub_regions(origin.clone(), region, r); - } - - return; - } - - // If we find that there is a unique declared bound `'b`, and this bound - // appears in the trait reference, then the best action is to require that `'b:'r`, - // so do that. This is best no matter what rule we use: - // - // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to - // the requirement that `'b:'r` - // - OutlivesProjectionComponent: this would require `'b:'r` in addition to - // other conditions - if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) { - let unique_bound = env_bounds[0]; - debug!("projection_must_outlive: unique declared bound = {:?}", unique_bound); - if projection_ty.substs.regions().any(|r| env_bounds.contains(&r)) { - debug!("projection_must_outlive: unique declared bound appears in trait ref"); - self.sub_regions(origin.clone(), region, unique_bound); - return; - } - } - - // Fallback to verifying after the fact that there exists a - // declared bound, or that all the components appearing in the - // projection outlive; in some cases, this may add insufficient - // edges into the inference graph, leading to inference failures - // even though a satisfactory solution exists. - let verify_bound = self.projection_bound(origin.span(), env_bounds, projection_ty); - let generic = GenericKind::Projection(projection_ty); - self.verify_generic_bound(origin, generic.clone(), region, verify_bound); - } - - fn type_bound(&self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - match ty.sty { - ty::TyParam(p) => { - self.param_bound(p) - } - ty::TyProjection(data) => { - let declared_bounds = self.projection_declared_bounds(span, data); - self.projection_bound(span, declared_bounds, data) - } - _ => { - self.recursive_type_bound(span, ty) - } - } - } - - fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { - debug!("param_bound(param_ty={:?})", - param_ty); - - let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); - - // Add in the default bound of fn body that applies to all in - // scope type parameters: - param_bounds.extend(self.implicit_region_bound); - - VerifyBound::AnyRegion(param_bounds) - } - - fn projection_declared_bounds(&self, - span: Span, - projection_ty: ty::ProjectionTy<'tcx>) - -> Vec<ty::Region<'tcx>> - { - // First assemble bounds from where clauses and traits. - - let mut declared_bounds = - self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); - - declared_bounds.extend_from_slice( - &self.declared_projection_bounds_from_trait(span, projection_ty)); - - declared_bounds - } - - fn projection_bound(&self, - span: Span, - declared_bounds: Vec<ty::Region<'tcx>>, - projection_ty: ty::ProjectionTy<'tcx>) - -> VerifyBound<'tcx> { - debug!("projection_bound(declared_bounds={:?}, projection_ty={:?})", - declared_bounds, projection_ty); - - // see the extensive comment in projection_must_outlive - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - let recursive_bound = self.recursive_type_bound(span, ty); - - VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) - } - - fn recursive_type_bound(&self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - let mut bounds = vec![]; - - for subty in ty.walk_shallow() { - bounds.push(self.type_bound(span, subty)); - } - - let mut regions = ty.regions(); - regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions - bounds.push(VerifyBound::AllRegions(regions)); - - // remove bounds that must hold, since they are not interesting - bounds.retain(|b| !b.must_hold()); - - if bounds.len() == 1 { - bounds.pop().unwrap() - } else { - VerifyBound::AllBounds(bounds) - } - } - - fn declared_generic_bounds_from_env(&self, generic: GenericKind<'tcx>) - -> Vec<ty::Region<'tcx>> - { - let param_env = &self.param_env; - - // To start, collect bounds from user: - let mut param_bounds = self.tcx.required_region_bounds(generic.to_ty(self.tcx), - param_env.caller_bounds.to_vec()); - - // Next, collect regions we scraped from the well-formedness - // constraints in the fn signature. To do that, we walk the list - // of known relations from the fn ctxt. - // - // This is crucial because otherwise code like this fails: - // - // fn foo<'a, A>(x: &'a A) { x.bar() } - // - // The problem is that the type of `x` is `&'a A`. To be - // well-formed, then, A must be lower-generic by `'a`, but we - // don't know that this holds from first principles. - for &(r, p) in &self.region_bound_pairs { - debug!("generic={:?} p={:?}", - generic, - p); - if generic == p { - param_bounds.push(r); - } - } - - param_bounds - } - - fn declared_projection_bounds_from_trait(&self, - span: Span, - projection_ty: ty::ProjectionTy<'tcx>) - -> Vec<ty::Region<'tcx>> - { - debug!("projection_bounds(projection_ty={:?})", - projection_ty); - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - - // Say we have a projection `<T as SomeTrait<'a>>::SomeType`. We are interested - // in looking for a trait definition like: - // - // ``` - // trait SomeTrait<'a> { - // type SomeType : 'a; - // } - // ``` - // - // we can thus deduce that `<T as SomeTrait<'a>>::SomeType : 'a`. - let trait_predicates = self.tcx.predicates_of(projection_ty.trait_ref(self.tcx).def_id); - assert_eq!(trait_predicates.parent, None); - let predicates = trait_predicates.predicates.as_slice().to_vec(); - traits::elaborate_predicates(self.tcx, predicates) - .filter_map(|predicate| { - // we're only interesting in `T : 'a` style predicates: - let outlives = match predicate { - ty::Predicate::TypeOutlives(data) => data, - _ => { return None; } - }; - - debug!("projection_bounds: outlives={:?} (1)", - outlives); - - // apply the substitutions (and normalize any projected types) - let outlives = self.instantiate_type_scheme(span, - projection_ty.substs, - &outlives); - - debug!("projection_bounds: outlives={:?} (2)", - outlives); - - let region_result = self.commit_if_ok(|_| { - let (outlives, _) = - self.replace_late_bound_regions_with_fresh_var( - span, - infer::AssocTypeProjection(projection_ty.item_def_id), - &outlives); - - debug!("projection_bounds: outlives={:?} (3)", - outlives); - - // check whether this predicate applies to our current projection - let cause = self.fcx.misc(span); - match self.at(&cause, self.fcx.param_env).eq(outlives.0, ty) { - Ok(ok) => Ok((ok, outlives.1)), - Err(_) => Err(()) - } - }).map(|(ok, result)| { - self.register_infer_ok_obligations(ok); - result - }); - - debug!("projection_bounds: region_result={:?}", - region_result); - - region_result.ok() - }) - .collect() - } } diff --git a/src/librustc_typeck/check/regionck_outlives.rs b/src/librustc_typeck/check/regionck_outlives.rs new file mode 100644 index 00000000000..9bd7384a48e --- /dev/null +++ b/src/librustc_typeck/check/regionck_outlives.rs @@ -0,0 +1,445 @@ +//! Temporary holding spot for some code I want to factor out. + +use rustc::traits::{self, ObligationCause, ObligationCauseCode, PredicateObligations}; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::infer::{self, GenericKind, InferCtxt, InferOk, VerifyBound}; +use rustc::ty::subst::Subst; +use rustc::ty::outlives::Component; +use syntax::ast; +use syntax_pos::Span; + +pub struct RegionckOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + // Context provided by the caller: + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option<ty::Region<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + body_id: ast::NodeId, + + // Obligations that we accrue as we go: + obligations: PredicateObligations<'tcx>, +} + +impl<'cx, 'gcx, 'tcx> RegionckOutlives<'cx, 'gcx, 'tcx> { + pub fn new( + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option<ty::Region<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + body_id: ast::NodeId, + ) -> Self { + Self { + infcx, + region_bound_pairs, + implicit_region_bound, + param_env, + body_id, + obligations: vec![], + } + } + + /// Adds constraints to inference such that `T: 'a` holds (or + /// reports an error if it cannot). + /// + /// # Parameters + /// + /// - `origin`, the reason we need this constraint + /// - `ty`, the type `T` + /// - `region`, the region `'a` + pub fn type_must_outlive( + mut self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) -> InferOk<'tcx, ()> { + self.type_must_outlive_pushing_obligations(origin, ty, region); + InferOk { + value: (), + obligations: self.obligations, + } + } + + /// Internal helper: ensure that `ty_must_outlive` and push obligations onto + /// our internal vector. + fn type_must_outlive_pushing_obligations( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) { + let ty = self.infcx.resolve_type_vars_if_possible(&ty); + + debug!( + "type_must_outlive(ty={:?}, region={:?}, origin={:?})", + ty, + region, + origin + ); + + assert!(!ty.has_escaping_regions()); + + let components = self.tcx().outlives_components(ty); + self.components_must_outlive(origin, components, region); + } + + fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> { + self.infcx.tcx + } + + fn components_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + components: Vec<Component<'tcx>>, + region: ty::Region<'tcx>, + ) { + for component in components { + let origin = origin.clone(); + match component { + Component::Region(region1) => { + self.infcx.sub_regions(origin, region, region1); + } + Component::Param(param_ty) => { + self.param_ty_must_outlive(origin, region, param_ty); + } + Component::Projection(projection_ty) => { + self.projection_must_outlive(origin, region, projection_ty); + } + Component::EscapingProjection(subcomponents) => { + self.components_must_outlive(origin, subcomponents, region); + } + Component::UnresolvedInferenceVariable(v) => { + // ignore this, we presume it will yield an error + // later, since if a type variable is not resolved by + // this point it never will be + self.infcx.tcx.sess.delay_span_bug( + origin.span(), + &format!("unresolved inference variable in outlives: {:?}", v), + ); + } + } + } + } + + fn param_ty_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + param_ty: ty::ParamTy, + ) { + debug!( + "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})", + region, + param_ty, + origin + ); + + let verify_bound = self.param_bound(param_ty); + let generic = GenericKind::Param(param_ty); + self.infcx + .verify_generic_bound(origin, generic, region, verify_bound); + } + + fn projection_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + ) { + debug!( + "projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", + region, + projection_ty, + origin + ); + + // This case is thorny for inference. The fundamental problem is + // that there are many cases where we have choice, and inference + // doesn't like choice (the current region inference in + // particular). :) First off, we have to choose between using the + // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and + // OutlivesProjectionComponent rules, any one of which is + // sufficient. If there are no inference variables involved, it's + // not hard to pick the right rule, but if there are, we're in a + // bit of a catch 22: if we picked which rule we were going to + // use, we could add constraints to the region inference graph + // that make it apply, but if we don't add those constraints, the + // rule might not apply (but another rule might). For now, we err + // on the side of adding too few edges into the graph. + + // Compute the bounds we can derive from the environment or trait + // definition. We know that the projection outlives all the + // regions in this list. + let env_bounds = self.projection_declared_bounds(origin.span(), projection_ty); + + debug!("projection_must_outlive: env_bounds={:?}", env_bounds); + + // If we know that the projection outlives 'static, then we're + // done here. + if env_bounds.contains(&&ty::ReStatic) { + debug!("projection_must_outlive: 'static as declared bound"); + return; + } + + // If declared bounds list is empty, the only applicable rule is + // OutlivesProjectionComponent. If there are inference variables, + // then, we can break down the outlives into more primitive + // components without adding unnecessary edges. + // + // If there are *no* inference variables, however, we COULD do + // this, but we choose not to, because the error messages are less + // good. For example, a requirement like `T::Item: 'r` would be + // translated to a requirement that `T: 'r`; when this is reported + // to the user, it will thus say "T: 'r must hold so that T::Item: + // 'r holds". But that makes it sound like the only way to fix + // the problem is to add `T: 'r`, which isn't true. So, if there are no + // inference variables, we use a verify constraint instead of adding + // edges, which winds up enforcing the same condition. + let needs_infer = projection_ty.needs_infer(); + if env_bounds.is_empty() && needs_infer { + debug!("projection_must_outlive: no declared bounds"); + + for component_ty in projection_ty.substs.types() { + self.type_must_outlive_pushing_obligations(origin.clone(), component_ty, region); + } + + for r in projection_ty.substs.regions() { + self.infcx.sub_regions(origin.clone(), region, r); + } + + return; + } + + // If we find that there is a unique declared bound `'b`, and this bound + // appears in the trait reference, then the best action is to require that `'b:'r`, + // so do that. This is best no matter what rule we use: + // + // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to + // the requirement that `'b:'r` + // - OutlivesProjectionComponent: this would require `'b:'r` in addition to + // other conditions + if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) { + let unique_bound = env_bounds[0]; + debug!( + "projection_must_outlive: unique declared bound = {:?}", + unique_bound + ); + if projection_ty + .substs + .regions() + .any(|r| env_bounds.contains(&r)) + { + debug!("projection_must_outlive: unique declared bound appears in trait ref"); + self.infcx.sub_regions(origin.clone(), region, unique_bound); + return; + } + } + + // Fallback to verifying after the fact that there exists a + // declared bound, or that all the components appearing in the + // projection outlive; in some cases, this may add insufficient + // edges into the inference graph, leading to inference failures + // even though a satisfactory solution exists. + let verify_bound = self.projection_bound(origin.span(), env_bounds, projection_ty); + let generic = GenericKind::Projection(projection_ty); + self.infcx + .verify_generic_bound(origin, generic.clone(), region, verify_bound); + } + + fn type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + match ty.sty { + ty::TyParam(p) => self.param_bound(p), + ty::TyProjection(data) => { + let declared_bounds = self.projection_declared_bounds(span, data); + self.projection_bound(span, declared_bounds, data) + } + _ => self.recursive_type_bound(span, ty), + } + } + + fn param_bound(&mut self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { + debug!("param_bound(param_ty={:?})", param_ty); + + let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); + + // Add in the default bound of fn body that applies to all in + // scope type parameters: + param_bounds.extend(self.implicit_region_bound); + + VerifyBound::AnyRegion(param_bounds) + } + + fn projection_declared_bounds( + &mut self, + span: Span, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec<ty::Region<'tcx>> { + // First assemble bounds from where clauses and traits. + + let mut declared_bounds = + self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); + + declared_bounds + .extend_from_slice(&mut self.declared_projection_bounds_from_trait(span, projection_ty)); + + declared_bounds + } + + fn projection_bound( + &mut self, + span: Span, + declared_bounds: Vec<ty::Region<'tcx>>, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> VerifyBound<'tcx> { + debug!( + "projection_bound(declared_bounds={:?}, projection_ty={:?})", + declared_bounds, + projection_ty + ); + + // see the extensive comment in projection_must_outlive + let ty = self.infcx + .tcx + .mk_projection(projection_ty.item_def_id, projection_ty.substs); + let recursive_bound = self.recursive_type_bound(span, ty); + + VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) + } + + fn recursive_type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + let mut bounds = vec![]; + + for subty in ty.walk_shallow() { + bounds.push(self.type_bound(span, subty)); + } + + let mut regions = ty.regions(); + regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions + bounds.push(VerifyBound::AllRegions(regions)); + + // remove bounds that must hold, since they are not interesting + bounds.retain(|b| !b.must_hold()); + + if bounds.len() == 1 { + bounds.pop().unwrap() + } else { + VerifyBound::AllBounds(bounds) + } + } + + fn declared_generic_bounds_from_env( + &mut self, + generic: GenericKind<'tcx>, + ) -> Vec<ty::Region<'tcx>> { + let tcx = self.tcx(); + + // To start, collect bounds from user: + let mut param_bounds = + tcx.required_region_bounds(generic.to_ty(tcx), self.param_env.caller_bounds.to_vec()); + + // Next, collect regions we scraped from the well-formedness + // constraints in the fn signature. To do that, we walk the list + // of known relations from the fn ctxt. + // + // This is crucial because otherwise code like this fails: + // + // fn foo<'a, A>(x: &'a A) { x.bar() } + // + // The problem is that the type of `x` is `&'a A`. To be + // well-formed, then, A must be lower-generic by `'a`, but we + // don't know that this holds from first principles. + for &(r, p) in self.region_bound_pairs { + debug!("generic={:?} p={:?}", generic, p); + if generic == p { + param_bounds.push(r); + } + } + + param_bounds + } + + fn declared_projection_bounds_from_trait( + &mut self, + span: Span, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec<ty::Region<'tcx>> { + debug!("projection_bounds(projection_ty={:?})", projection_ty); + let ty = self.tcx() + .mk_projection(projection_ty.item_def_id, projection_ty.substs); + + // Say we have a projection `<T as SomeTrait<'a>>::SomeType`. We are interested + // in looking for a trait definition like: + // + // ``` + // trait SomeTrait<'a> { + // type SomeType : 'a; + // } + // ``` + // + // we can thus deduce that `<T as SomeTrait<'a>>::SomeType : 'a`. + let trait_predicates = self.tcx() + .predicates_of(projection_ty.trait_ref(self.tcx()).def_id); + assert_eq!(trait_predicates.parent, None); + let predicates = trait_predicates.predicates.as_slice().to_vec(); + traits::elaborate_predicates(self.tcx(), predicates) + .filter_map(|predicate| { + // we're only interesting in `T : 'a` style predicates: + let outlives = match predicate { + ty::Predicate::TypeOutlives(data) => data, + _ => { + return None; + } + }; + + debug!("projection_bounds: outlives={:?} (1)", outlives); + + // apply the substitutions (and normalize any projected types) + let outlives = outlives.subst(self.tcx(), projection_ty.substs); + let outlives = self.infcx.partially_normalize_associated_types_in( + span, + self.body_id, + self.param_env, + &outlives, + ); + let outlives = self.register_infer_ok_obligations(outlives); + + debug!("projection_bounds: outlives={:?} (2)", outlives); + + let region_result = self.infcx + .commit_if_ok(|_| { + let (outlives, _) = self.infcx.replace_late_bound_regions_with_fresh_var( + span, + infer::AssocTypeProjection(projection_ty.item_def_id), + &outlives, + ); + + debug!("projection_bounds: outlives={:?} (3)", outlives); + + // check whether this predicate applies to our current projection + let cause = ObligationCause::new( + span, + self.body_id, + ObligationCauseCode::MiscObligation, + ); + match self.infcx.at(&cause, self.param_env).eq(outlives.0, ty) { + Ok(ok) => Ok((ok, outlives.1)), + Err(_) => Err(()), + } + }) + .map(|(ok, result)| { + self.register_infer_ok_obligations(ok); + result + }); + + debug!("projection_bounds: region_result={:?}", region_result); + + region_result.ok() + }) + .collect() + } + + fn register_infer_ok_obligations<T>(&mut self, infer_ok: InferOk<'tcx, T>) -> T { + let InferOk { value, obligations } = infer_ok; + self.obligations.extend(obligations); + value + } +} diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 5227955d7b9..014b8b14edb 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -75,6 +75,7 @@ This API is completely unstable and subject to change. #![feature(advanced_slice_patterns)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(crate_visibility_modifier)] #![feature(conservative_impl_trait)] #![feature(match_default_bindings)] #![feature(never_type)] |
