From c45307fae1f261eac006816329780a7a01b2d0dc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 12 Nov 2017 05:25:13 -0500 Subject: [PATCH] extract the code to create `OutlivesBounds` into its own module Now it can be reused by the NLL code. --- src/librustc/infer/outlives/env.rs | 105 ++++++++---------- src/librustc/infer/outlives/implied_bounds.rs | 103 ++++++++++------- 2 files changed, 110 insertions(+), 98 deletions(-) diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 9f00fc78cc0a..43e782ac1301 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, GenericKind}; +use infer::{GenericKind, InferCtxt}; use infer::outlives::free_region_map::FreeRegionMap; -use infer::outlives::implied_bounds::ImpliedBound; +use infer::outlives::implied_bounds::{self, OutlivesBound}; use ty::{self, Ty}; use syntax::ast; @@ -50,7 +50,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { region_bound_pairs: vec![], }; - env.init_free_regions_from_predicates(); + env.add_outlives_bounds(None, implied_bounds::explicit_outlives_bounds(param_env)); env } @@ -144,65 +144,54 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { for &ty in fn_sig_tys { let ty = infcx.resolve_type_vars_if_possible(&ty); debug!("add_implied_bounds: ty = {}", ty); - let implied_bounds = infcx.implied_bounds(self.param_env, body_id, ty, span); - - // But also record other relationships, such as `T:'x`, - // that don't go into the free-region-map but which we use - // here. - for implication in implied_bounds { - debug!("add_implied_bounds: implication={:?}", implication); - match implication { - ImpliedBound::RegionSubRegion( - r_a @ &ty::ReEarlyBound(_), - &ty::ReVar(vid_b), - ) | - ImpliedBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => { - infcx.add_given(r_a, vid_b); - } - ImpliedBound::RegionSubParam(r_a, param_b) => { - self.region_bound_pairs - .push((r_a, GenericKind::Param(param_b))); - } - ImpliedBound::RegionSubProjection(r_a, projection_b) => { - self.region_bound_pairs - .push((r_a, GenericKind::Projection(projection_b))); - } - ImpliedBound::RegionSubRegion(r_a, r_b) => { - // In principle, we could record (and take - // advantage of) every relationship here, but - // we are also free not to -- it simply means - // strictly less that we can successfully type - // check. Right now we only look for things - // relationships between free regions. (It may - // also be that we should revise our inference - // system to be more general and to make use - // of *every* relationship that arises here, - // but presently we do not.) - self.free_region_map.relate_regions(r_a, r_b); - } - } - } + let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span); + self.add_outlives_bounds(Some(infcx), implied_bounds) } } - fn init_free_regions_from_predicates(&mut self) { - debug!("init_free_regions_from_predicates()"); - for predicate in self.param_env.caller_bounds { - debug!("init_free_regions_from_predicates: predicate={:?}", predicate); - match *predicate { - ty::Predicate::Projection(..) | - ty::Predicate::Trait(..) | - ty::Predicate::Equate(..) | - ty::Predicate::Subtype(..) | - ty::Predicate::WellFormed(..) | - ty::Predicate::ObjectSafe(..) | - ty::Predicate::ClosureKind(..) | - ty::Predicate::TypeOutlives(..) | - ty::Predicate::ConstEvaluatable(..) => { - // No region bounds here + /// Processes outlives bounds that are known to hold, whether from implied or other sources. + /// + /// The `infcx` parameter is optional; if the implied bounds may + /// contain inference variables, it should be supplied, in which + /// case we will register "givens" on the inference context. (See + /// `RegionConstraintData`.) + fn add_outlives_bounds( + &mut self, + infcx: Option<&InferCtxt<'a, 'gcx, 'tcx>>, + outlives_bounds: I, + ) where + I: IntoIterator>, + { + // But also record other relationships, such as `T:'x`, + // that don't go into the free-region-map but which we use + // here. + for outlives_bound in outlives_bounds { + debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); + match outlives_bound { + OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b)) | + OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => { + infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b); } - ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => { - self.free_region_map.relate_regions(r_b, r_a); + OutlivesBound::RegionSubParam(r_a, param_b) => { + self.region_bound_pairs + .push((r_a, GenericKind::Param(param_b))); + } + OutlivesBound::RegionSubProjection(r_a, projection_b) => { + self.region_bound_pairs + .push((r_a, GenericKind::Projection(projection_b))); + } + OutlivesBound::RegionSubRegion(r_a, r_b) => { + // In principle, we could record (and take + // advantage of) every relationship here, but + // we are also free not to -- it simply means + // strictly less that we can successfully type + // check. Right now we only look for things + // relationships between free regions. (It may + // also be that we should revise our inference + // system to be more general and to make use + // of *every* relationship that arises here, + // but presently we do not.) + self.free_region_map.relate_regions(r_a, r_b); } } } diff --git a/src/librustc/infer/outlives/implied_bounds.rs b/src/librustc/infer/outlives/implied_bounds.rs index 452ceddd7d6e..8a562471ac5d 100644 --- a/src/librustc/infer/outlives/implied_bounds.rs +++ b/src/librustc/infer/outlives/implied_bounds.rs @@ -16,28 +16,32 @@ use ty::{self, Ty, TypeFoldable}; use ty::outlives::Component; use ty::wf; -/// Implied bounds are region relationships that we deduce -/// automatically. The idea is that (e.g.) a caller must check that a -/// function's argument types are well-formed immediately before -/// calling that fn, and hence the *callee* can assume that its -/// argument types are well-formed. This may imply certain relationships -/// between generic parameters. For example: -/// -/// fn foo<'a,T>(x: &'a T) -/// -/// can only be called with a `'a` and `T` such that `&'a T` is WF. -/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. +/// Outlives bounds are relationships between generic parameters, +/// whether they both be regions (`'a: 'b`) or whether types are +/// involved (`T: 'a`). These relationships can be extracted from the +/// full set of predicates we understand or also from types (in which +/// case they are called implied bounds). They are fed to the +/// `OutlivesEnv` which in turn is supplied to the region checker and +/// other parts of the inference system. #[derive(Debug)] -pub enum ImpliedBound<'tcx> { +pub enum OutlivesBound<'tcx> { RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), RegionSubParam(ty::Region<'tcx>, ty::ParamTy), RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), } impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { - /// Compute the implied bounds that a callee/impl can assume based on - /// the fact that caller/projector has ensured that `ty` is WF. See - /// the `ImpliedBound` type for more details. + /// Implied bounds are region relationships that we deduce + /// automatically. The idea is that (e.g.) a caller must check that a + /// function's argument types are well-formed immediately before + /// calling that fn, and hence the *callee* can assume that its + /// argument types are well-formed. This may imply certain relationships + /// between generic parameters. For example: + /// + /// fn foo<'a,T>(x: &'a T) + /// + /// can only be called with a `'a` and `T` such that `&'a T` is WF. + /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. /// /// # Parameters /// @@ -48,13 +52,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// - `ty`, the type that we are supposed to assume is WF. /// - `span`, a span to use when normalizing, hopefully not important, /// might be useful if a `bug!` occurs. - pub fn implied_bounds( + pub fn implied_outlives_bounds( &self, param_env: ty::ParamEnv<'tcx>, body_id: ast::NodeId, ty: Ty<'tcx>, span: Span, - ) -> Vec> { + ) -> Vec> { let tcx = self.tcx; // Sometimes when we ask what it takes for T: WF, we get back that @@ -76,8 +80,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { // than the ultimate set. (Note: normally there won't be // unresolved inference variables here anyway, but there might be // during typeck under some circumstances.) - let obligations = - wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]); + let obligations = wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]); // NB: All of these predicates *ought* to be easily proven // true. In fact, their correctness is (mostly) implied by @@ -105,7 +108,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { obligations .iter() .filter(|o| o.predicate.has_infer_types()) - .cloned()); + .cloned(), + ); // From the full set of obligations, just filter down to the // region relationships. @@ -125,25 +129,21 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { vec![] } - ty::Predicate::RegionOutlives(ref data) => { - match data.no_late_bound_regions() { - None => vec![], - Some(ty::OutlivesPredicate(r_a, r_b)) => { - vec![ImpliedBound::RegionSubRegion(r_b, r_a)] - } + ty::Predicate::RegionOutlives(ref data) => match data.no_late_bound_regions() { + None => vec![], + Some(ty::OutlivesPredicate(r_a, r_b)) => { + vec![OutlivesBound::RegionSubRegion(r_b, r_a)] } - } + }, - ty::Predicate::TypeOutlives(ref data) => { - match data.no_late_bound_regions() { - None => vec![], - Some(ty::OutlivesPredicate(ty_a, r_b)) => { - let ty_a = self.resolve_type_vars_if_possible(&ty_a); - let components = tcx.outlives_components(ty_a); - Self::implied_bounds_from_components(r_b, components) - } + ty::Predicate::TypeOutlives(ref data) => match data.no_late_bound_regions() { + None => vec![], + Some(ty::OutlivesPredicate(ty_a, r_b)) => { + let ty_a = self.resolve_type_vars_if_possible(&ty_a); + let components = tcx.outlives_components(ty_a); + Self::implied_bounds_from_components(r_b, components) } - } + }, } })); } @@ -165,17 +165,17 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { fn implied_bounds_from_components( sub_region: ty::Region<'tcx>, sup_components: Vec>, - ) -> Vec> { + ) -> Vec> { sup_components .into_iter() .flat_map(|component| { match component { Component::Region(r) => - vec![ImpliedBound::RegionSubRegion(sub_region, r)], + vec![OutlivesBound::RegionSubRegion(sub_region, r)], Component::Param(p) => - vec![ImpliedBound::RegionSubParam(sub_region, p)], + vec![OutlivesBound::RegionSubParam(sub_region, p)], Component::Projection(p) => - vec![ImpliedBound::RegionSubProjection(sub_region, p)], + vec![OutlivesBound::RegionSubProjection(sub_region, p)], Component::EscapingProjection(_) => // If the projection has escaping regions, don't // try to infer any implied bounds even for its @@ -193,3 +193,26 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { .collect() } } + +pub fn explicit_outlives_bounds<'tcx>( + param_env: ty::ParamEnv<'tcx>, +) -> impl Iterator> + 'tcx { + debug!("explicit_outlives_bounds()"); + param_env + .caller_bounds + .into_iter() + .filter_map(move |predicate| match predicate { + ty::Predicate::Projection(..) | + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | + ty::Predicate::WellFormed(..) | + ty::Predicate::ObjectSafe(..) | + ty::Predicate::ClosureKind(..) | + ty::Predicate::TypeOutlives(..) | + ty::Predicate::ConstEvaluatable(..) => None, + ty::Predicate::RegionOutlives(ref data) => data.no_late_bound_regions().map( + |ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a), + ), + }) +}