From a6658d5ff1bee0aa46224bc05985692783fd11b4 Mon Sep 17 00:00:00 2001 From: Tobias Schottdorf Date: Tue, 2 May 2017 14:37:00 -0400 Subject: [PATCH] Store interned predicates in ParameterEnvironment See #41444. As a first step towards untangling `ParameterEnvironment`, change its `caller_bounds` field from a `Vec` into an interned slice of `ty::Predicate`s. This change is intentionally well-contained and doesn't pull on any of the loose ends. In particular, you'll note that `normalize_param_env_or_error` now interns twice. --- src/librustc/traits/mod.rs | 18 ++++++++---- src/librustc/ty/context.rs | 31 ++++++++++++++++++++- src/librustc/ty/mod.rs | 18 ++++++------ src/librustc/ty/util.rs | 3 ++ src/librustc_typeck/check/compare_method.rs | 3 +- src/librustc_typeck/check/method/probe.rs | 2 +- src/librustc_typeck/check/regionck.rs | 2 +- 7 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 4f7cb2b12a7ca..2f525e1b8b45c 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -462,7 +462,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, unnormalized_env); let predicates: Vec<_> = - util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.clone()) + util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.to_vec()) .filter(|p| !p.is_global()) // (*) .collect(); @@ -477,11 +477,19 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); - let elaborated_env = unnormalized_env.with_caller_bounds(predicates); + let elaborated_env = unnormalized_env.with_caller_bounds(tcx.intern_predicates(&predicates)); tcx.infer_ctxt(elaborated_env, Reveal::UserFacing).enter(|infcx| { - let predicates = match fully_normalize(&infcx, cause, - &infcx.parameter_environment.caller_bounds) { + let predicates = match fully_normalize( + &infcx, cause, + // You would really want to pass infcx.parameter_environment.caller_bounds here, + // but that is an interned slice, and fully_normalize takes &T and returns T, so + // without further refactoring, a slice can't be used. Luckily, we still have the + // predicate vector from which we created the ParameterEnvironment in infcx, so we + // can pass that instead. It's roundabout and a bit brittle, but this code path + // ought to be refactored anyway, and until then it saves us from having to copy. + &predicates, + ) { Ok(predicates) => predicates, Err(errors) => { infcx.report_fulfillment_errors(&errors); @@ -520,7 +528,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, debug!("normalize_param_env_or_error: resolved predicates={:?}", predicates); - infcx.parameter_environment.with_caller_bounds(predicates) + infcx.parameter_environment.with_caller_bounds(tcx.intern_predicates(&predicates)) }) } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 6de61013dfdd3..ef0240296ccc5 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -32,7 +32,7 @@ use ty::{self, TraitRef, Ty, TypeAndMut}; use ty::{TyS, TypeVariants, Slice}; use ty::{AdtKind, AdtDef, ClosureSubsts, Region}; use hir::FreevarMap; -use ty::{PolyFnSig, InferTy, ParamTy, ProjectionTy, ExistentialPredicate}; +use ty::{PolyFnSig, InferTy, ParamTy, ProjectionTy, ExistentialPredicate, Predicate}; use ty::RegionKind; use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use ty::TypeVariants::*; @@ -96,6 +96,7 @@ pub struct CtxtInterners<'tcx> { substs: RefCell>>>, region: RefCell>>>, existential_predicates: RefCell>>>>, + predicates: RefCell>>>>, } impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> { @@ -107,6 +108,7 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> { substs: RefCell::new(FxHashSet()), region: RefCell::new(FxHashSet()), existential_predicates: RefCell::new(FxHashSet()), + predicates: RefCell::new(FxHashSet()), } } @@ -1130,6 +1132,13 @@ impl<'tcx: 'lcx, 'lcx> Borrow<[ExistentialPredicate<'lcx>]> } } +impl<'tcx: 'lcx, 'lcx> Borrow<[Predicate<'lcx>]> + for Interned<'tcx, Slice>> { + fn borrow<'a>(&'a self) -> &'a [Predicate<'lcx>] { + &self.0[..] + } +} + macro_rules! intern_method { ($lt_tcx:tt, $name:ident: $method:ident($alloc:ty, $alloc_method:ident, @@ -1224,6 +1233,7 @@ macro_rules! slice_interners { slice_interners!( existential_predicates: _intern_existential_predicates(ExistentialPredicate), + predicates: _intern_predicates(Predicate), type_list: _intern_type_list(Ty), substs: _intern_substs(Kind) ); @@ -1443,6 +1453,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self._intern_existential_predicates(eps) } + pub fn intern_predicates(self, preds: &[Predicate<'tcx>]) + -> &'tcx Slice> { + // FIXME consider asking the input slice to be sorted to avoid + // re-interning permutations, in which case that would be asserted + // here. + if preds.len() == 0 { + // The macro-generated method below asserts we don't intern an empty slice. + Slice::empty() + } else { + self._intern_predicates(preds) + } + } + pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx Slice> { if ts.len() == 0 { Slice::empty() @@ -1481,6 +1504,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { iter.intern_with(|xs| self.intern_existential_predicates(xs)) } + pub fn mk_predicates], + &'tcx Slice>>>(self, iter: I) + -> I::Output { + iter.intern_with(|xs| self.intern_predicates(xs)) + } + pub fn mk_type_list], &'tcx Slice>>>(self, iter: I) -> I::Output { iter.intern_with(|xs| self.intern_type_list(xs)) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index c07f41fb22313..bf0f75cf323ef 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -747,7 +747,7 @@ impl<'a, 'gcx, 'tcx> GenericPredicates<'tcx> { } } -#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum Predicate<'tcx> { /// Corresponds to `where Foo : Bar`. `Foo` here would be /// the `Self` type of the trait reference and `A`, `B`, and `C` @@ -876,7 +876,7 @@ impl<'a, 'gcx, 'tcx> Predicate<'tcx> { } } -#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub struct TraitPredicate<'tcx> { pub trait_ref: TraitRef<'tcx> } @@ -928,18 +928,18 @@ impl<'tcx> PolyTraitPredicate<'tcx> { } } -#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct EquatePredicate<'tcx>(pub Ty<'tcx>, pub Ty<'tcx>); // `0 == 1` pub type PolyEquatePredicate<'tcx> = ty::Binder>; -#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct OutlivesPredicate(pub A, pub B); // `A : B` pub type PolyOutlivesPredicate = ty::Binder>; pub type PolyRegionOutlivesPredicate<'tcx> = PolyOutlivesPredicate, ty::Region<'tcx>>; pub type PolyTypeOutlivesPredicate<'tcx> = PolyOutlivesPredicate, ty::Region<'tcx>>; -#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct SubtypePredicate<'tcx> { pub a_is_expected: bool, pub a: Ty<'tcx>, @@ -1173,7 +1173,7 @@ pub struct ParameterEnvironment<'tcx> { /// Obligations that the caller must satisfy. This is basically /// the set of bounds on the in-scope type parameters, translated /// into Obligations, and elaborated and normalized. - pub caller_bounds: Vec>, + pub caller_bounds: &'tcx [ty::Predicate<'tcx>], /// Scope that is attached to free regions for this scope. This is /// usually the id of the fn body, but for more abstract scopes @@ -1196,7 +1196,7 @@ pub struct ParameterEnvironment<'tcx> { impl<'a, 'tcx> ParameterEnvironment<'tcx> { pub fn with_caller_bounds(&self, - caller_bounds: Vec>) + caller_bounds: &'tcx [ty::Predicate<'tcx>]) -> ParameterEnvironment<'tcx> { ParameterEnvironment { @@ -2441,7 +2441,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn empty_parameter_environment(self) -> ParameterEnvironment<'tcx> { ty::ParameterEnvironment { free_substs: self.intern_substs(&[]), - caller_bounds: Vec::new(), + caller_bounds: Slice::empty(), implicit_region_bound: None, free_id_outlive: None, is_copy_cache: RefCell::new(FxHashMap()), @@ -2516,7 +2516,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { let unnormalized_env = ty::ParameterEnvironment { free_substs: free_substs, implicit_region_bound: free_id_outlive.map(|f| tcx.mk_region(ty::ReScope(f))), - caller_bounds: predicates, + caller_bounds: tcx.intern_predicates(&predicates), free_id_outlive: free_id_outlive, is_copy_cache: RefCell::new(FxHashMap()), is_sized_cache: RefCell::new(FxHashMap()), diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index befc4e6c5fdd1..24dfae3c54065 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -313,6 +313,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// /// Requires that trait definitions have been processed so that we can /// elaborate predicates and walk supertraits. + /// + /// FIXME callers may only have a &[Predicate], not a Vec, so that's + /// what this code should accept. pub fn required_region_bounds(self, erased_self_ty: Ty<'tcx>, predicates: Vec>) diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 80330afaad841..9ed5528e86783 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -218,7 +218,8 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // The key step here is to update the caller_bounds's predicates to be // the new hybrid bounds we computed. let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_node_id); - let trait_param_env = impl_param_env.with_caller_bounds(hybrid_preds.predicates); + let trait_param_env = impl_param_env.with_caller_bounds( + tcx.intern_predicates(&hybrid_preds.predicates)); let trait_param_env = traits::normalize_param_env_or_error(tcx, impl_m.def_id, trait_param_env, diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 3cd53c378dd1b..c1cf5192877c0 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -893,7 +893,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { debug!("assemble_where_clause_candidates(trait_def_id={:?})", trait_def_id); - let caller_predicates = self.parameter_environment.caller_bounds.clone(); + let caller_predicates = self.parameter_environment.caller_bounds.to_vec(); for poly_bound in traits::elaborate_predicates(self.tcx, caller_predicates) .filter_map(|p| p.to_opt_poly_trait_ref()) .filter(|b| b.def_id() == trait_def_id) { diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 8a63d501da8c3..e4936dfc47bc7 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -1687,7 +1687,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // To start, collect bounds from user: let mut param_bounds = self.tcx.required_region_bounds(generic.to_ty(self.tcx), - param_env.caller_bounds.clone()); + 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