diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2018-06-19 18:32:43 -0400 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2018-06-26 13:18:27 -0400 |
| commit | 82169b6134b34f17b0e7c247dbd92bb6949951c0 (patch) | |
| tree | b88687335417912da041d4ceb8cbd61c7f6b794b | |
| parent | a583269af57e6acce6d9c2ffe4c0ce2b4f2e1ab9 (diff) | |
| download | rust-82169b6134b34f17b0e7c247dbd92bb6949951c0.tar.gz rust-82169b6134b34f17b0e7c247dbd92bb6949951c0.zip | |
convert query-type-op to create query-region-constraint directly
| -rw-r--r-- | src/librustc/infer/canonical/query_result.rs | 85 | ||||
| -rw-r--r-- | src/librustc/traits/query/type_op/custom.rs | 57 | ||||
| -rw-r--r-- | src/librustc/traits/query/type_op/eq.rs | 12 | ||||
| -rw-r--r-- | src/librustc/traits/query/type_op/mod.rs | 170 | ||||
| -rw-r--r-- | src/librustc/traits/query/type_op/normalize.rs | 10 | ||||
| -rw-r--r-- | src/librustc/traits/query/type_op/outlives.rs | 12 | ||||
| -rw-r--r-- | src/librustc/traits/query/type_op/prove_predicate.rs | 10 | ||||
| -rw-r--r-- | src/librustc/traits/query/type_op/subtype.rs | 10 |
8 files changed, 179 insertions, 187 deletions
diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index d512bb5268d..8a3784fe088 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -17,7 +17,6 @@ //! //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html -use either::Either; use infer::canonical::substitute::substitute_value; use infer::canonical::{ Canonical, CanonicalVarKind, CanonicalVarValues, CanonicalizedQueryResult, Certainty, @@ -29,7 +28,6 @@ use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::sync::Lrc; use std::fmt::Debug; -use std::iter::once; use syntax::ast; use traits::query::NoSolution; use traits::{FulfillmentContext, TraitEngine}; @@ -191,9 +189,11 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { pub fn instantiate_nll_query_result_and_region_obligations<R>( &self, cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, original_values: &CanonicalVarValues<'tcx>, query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, - ) -> Vec<QueryRegionConstraint<'tcx>> + output_query_region_constraints: &mut Vec<QueryRegionConstraint<'tcx>>, + ) -> InferResult<'tcx, R> where R: Debug + TypeFoldable<'tcx>, { @@ -210,52 +210,59 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { // Compute `QueryRegionConstraint` values that unify each of // the original values `v_o` that was canonicalized into a // variable... - let qrc_from_unify = original_values.var_values.iter_enumerated().flat_map( - |(index, original_value)| { - // ...with the value `v_r` of that variable from the query. - let result_value = - query_result - .substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]); - match (original_value.unpack(), result_value.unpack()) { - ( - UnpackedKind::Lifetime(ty::ReErased), - UnpackedKind::Lifetime(ty::ReErased), - ) => { - // no action needed - Either::Left(None.into_iter()) - } + let mut obligations = vec![]; + + for (index, original_value) in original_values.var_values.iter_enumerated() { + // ...with the value `v_r` of that variable from the query. + let result_value = query_result + .substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]); + match (original_value.unpack(), result_value.unpack()) { + (UnpackedKind::Lifetime(ty::ReErased), UnpackedKind::Lifetime(ty::ReErased)) => { + // no action needed + } - (UnpackedKind::Lifetime(v_o), UnpackedKind::Lifetime(v_r)) => { - // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. - Either::Right( - once(ty::OutlivesPredicate(v_o.into(), v_r)) - .chain(once(ty::OutlivesPredicate(v_r.into(), v_o))) - .map(ty::Binder::dummy), - ) + (UnpackedKind::Lifetime(v_o), UnpackedKind::Lifetime(v_r)) => { + // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. + if v_o != v_r { + output_query_region_constraints + .push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r))); + output_query_region_constraints + .push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o))); } + } - (UnpackedKind::Type(_), _) | (_, UnpackedKind::Type(_)) => { - // in NLL queries, we do not expect `type` results. - bug!( - "unexpected type in NLL query: cannot unify {:?} and {:?}", - original_value, - result_value, - ); - } + (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => { + let ok = self.at(cause, param_env).eq(v1, v2)?; + obligations.extend(ok.into_obligations()); } - }, - ); + + _ => { + bug!( + "kind mismatch, cannot unify {:?} and {:?}", + original_value, + result_value + ); + } + } + } // ...also include the other query region constraints from the query. - let qrc_from_result = query_result.value.region_constraints.iter().map(|r_c| { - r_c.map_bound(|ty::OutlivesPredicate(k1, r2)| { + output_query_region_constraints.reserve(query_result.value.region_constraints.len()); + for r_c in query_result.value.region_constraints.iter() { + output_query_region_constraints.push(r_c.map_bound(|ty::OutlivesPredicate(k1, r2)| { let k1 = substitute_value(self.tcx, &result_subst, &k1); let r2 = substitute_value(self.tcx, &result_subst, &r2); ty::OutlivesPredicate(k1, r2) - }) - }); + })); + } - qrc_from_unify.chain(qrc_from_result).collect() + let user_result: R = + query_result.substitute_projected(self.tcx, &result_subst, |q_r| &q_r.value); + + Ok(InferOk { + value: user_result, + obligations, + }) } /// Given the original values and the (canonicalized) result from diff --git a/src/librustc/traits/query/type_op/custom.rs b/src/librustc/traits/query/type_op/custom.rs index d3bc81a09a9..90ea276a094 100644 --- a/src/librustc/traits/query/type_op/custom.rs +++ b/src/librustc/traits/query/type_op/custom.rs @@ -9,9 +9,14 @@ // except according to those terms. use infer::{InferCtxt, InferOk}; -use traits::query::Fallible; -use ty::TyCtxt; use std::fmt; +use traits::query::Fallible; + +use infer::canonical::query_result; +use infer::canonical::QueryRegionConstraint; +use std::rc::Rc; +use syntax::codemap::DUMMY_SP; +use traits::{ObligationCause, TraitEngine}; pub struct CustomTypeOp<F, G> { closure: F, @@ -38,12 +43,18 @@ where { type Output = R; - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::Output, Self> { - Err(self) - } + /// Processes the operation and all resulting obligations, + /// returning the final result along with any region constraints + /// (they will be given over to the NLL region solver). + fn fully_perform( + self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + ) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)> { + if cfg!(debug_assertions) { + info!("fully_perform({:?})", self); + } - fn perform(self, infcx: &InferCtxt<'_, 'gcx, 'tcx>) -> Fallible<InferOk<'tcx, R>> { - Ok((self.closure)(infcx)?) + scrape_region_constraints(infcx, || Ok((self.closure)(infcx)?)) } } @@ -55,3 +66,35 @@ where write!(f, "{}", (self.description)()) } } + +/// Executes `op` and then scrapes out all the "old style" region +/// constraints that result, creating query-region-constraints. +fn scrape_region_constraints<'gcx, 'tcx, R>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>, +) -> Fallible<(R, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)> { + let mut fulfill_cx = TraitEngine::new(infcx.tcx); + let dummy_body_id = ObligationCause::dummy().body_id; + let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?; + debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); + fulfill_cx.register_predicate_obligations(infcx, obligations); + if let Err(e) = fulfill_cx.select_all_or_error(infcx) { + infcx.tcx.sess.diagnostic().delay_span_bug( + DUMMY_SP, + &format!("errors selecting obligation during MIR typeck: {:?}", e), + ); + } + + let region_obligations = infcx.take_registered_region_obligations(); + + let region_constraint_data = infcx.take_and_reset_region_constraints(); + + let outlives = + query_result::make_query_outlives(infcx.tcx, region_obligations, ®ion_constraint_data); + + if outlives.is_empty() { + Ok((value, None)) + } else { + Ok((value, Some(Rc::new(outlives)))) + } +} diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index b70a5307097..04d256a93e1 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs @@ -10,7 +10,7 @@ use infer::canonical::{Canonical, CanonicalizedQueryResult, QueryResult}; use traits::query::Fallible; -use ty::{self, ParamEnv, Ty, TyCtxt}; +use ty::{ParamEnv, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct Eq<'tcx> { @@ -29,7 +29,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { type QueryKey = Self; type QueryResult = (); - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::QueryResult, Self> { + fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::QueryResult, Self> { if self.a == self.b { Ok(()) } else { @@ -37,12 +37,8 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Eq<'tcx> { } } - fn into_query_key(self) -> Self { - self - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.param_env + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { + key.param_env } fn perform_query( diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 41d33338db4..16280885c12 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -8,16 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::canonical::query_result; use infer::canonical::{ Canonical, Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint, QueryResult, }; use infer::{InferCtxt, InferOk}; use std::fmt; use std::rc::Rc; -use syntax::codemap::DUMMY_SP; use traits::query::Fallible; -use traits::{ObligationCause, TraitEngine}; +use traits::ObligationCause; use ty::fold::TypeFoldable; use ty::{Lift, ParamEnv, TyCtxt}; @@ -31,89 +29,25 @@ pub mod subtype; pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { type Output; - /// Micro-optimization: returns `Ok(x)` if we can trivially - /// produce the output, else returns `Err(self)` back. - fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::Output, Self>; - - /// Given an infcx, performs **the kernel** of the operation: this does the - /// key action and then, optionally, returns a set of obligations which must be proven. - /// - /// This method is not meant to be invoked directly: instead, one - /// should use `fully_perform`, which will take those resulting - /// obligations and prove them, and then process the combined - /// results into region obligations which are returned. - fn perform( - self, - infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Fallible<InferOk<'tcx, Self::Output>>; - /// Processes the operation and all resulting obligations, /// returning the final result along with any region constraints /// (they will be given over to the NLL region solver). fn fully_perform( self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)> { - match self.trivial_noop(infcx.tcx) { - Ok(r) => Ok((r, None)), - Err(op) => op.fully_perform_nontrivial(infcx), - } - } - - /// Helper for `fully_perform` that handles the nontrivial cases. - #[inline(never)] // just to help with profiling - fn fully_perform_nontrivial( - self, - infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)> { - if cfg!(debug_assertions) { - info!( - "fully_perform_op_and_get_region_constraint_data({:?})", - self - ); - } - - let mut fulfill_cx = TraitEngine::new(infcx.tcx); - let dummy_body_id = ObligationCause::dummy().body_id; - let InferOk { value, obligations } = infcx.commit_if_ok(|_| self.perform(infcx))?; - debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); - fulfill_cx.register_predicate_obligations(infcx, obligations); - if let Err(e) = fulfill_cx.select_all_or_error(infcx) { - infcx.tcx.sess.diagnostic().delay_span_bug( - DUMMY_SP, - &format!("errors selecting obligation during MIR typeck: {:?}", e), - ); - } - - let region_obligations = infcx.take_registered_region_obligations(); - - let region_constraint_data = infcx.take_and_reset_region_constraints(); - - let outlives = query_result::make_query_outlives( - infcx.tcx, - region_obligations, - ®ion_constraint_data, - ); - - if outlives.is_empty() { - Ok((value, None)) - } else { - Ok((value, Some(Rc::new(outlives)))) - } - } + ) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)>; } pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { type QueryKey: TypeFoldable<'tcx> + Lift<'gcx>; type QueryResult: TypeFoldable<'tcx> + Lift<'gcx>; - /// Micro-optimization: returns `Ok(x)` if we can trivially - /// produce the output, else returns `Err(self)` back. - fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::QueryResult, Self>; - - fn into_query_key(self) -> Self::QueryKey; + /// Either converts `self` directly into a `QueryResult` (for + /// simple cases) or into a `QueryKey` (for more complex cases + /// where we actually have work to do). + fn prequery(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::QueryResult, Self::QueryKey>; - fn param_env(&self) -> ParamEnv<'tcx>; + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx>; fn perform_query( tcx: TyCtxt<'_, 'gcx, 'tcx>, @@ -130,6 +64,51 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: fmt::Debug + Sized { fn upcast_result( lifted_query_result: &'a CanonicalizedQueryResult<'gcx, Self::QueryResult>, ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>>; + + fn fully_perform_into( + self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + output_query_region_constraints: &mut Vec<QueryRegionConstraint<'tcx>>, + ) -> Fallible<Self::QueryResult> { + match QueryTypeOp::prequery(self, infcx.tcx) { + Ok(result) => Ok(result), + Err(query_key) => { + // FIXME(#33684) -- We need to use + // `canonicalize_hr_query_hack` here because of things + // like the subtype query, which go awry around + // `'static` otherwise. + let (canonical_self, canonical_var_values) = + infcx.canonicalize_hr_query_hack(&query_key); + let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; + let canonical_result = Self::upcast_result(&canonical_result); + + let param_env = Self::param_env(&query_key); + + let InferOk { value, obligations } = infcx + .instantiate_nll_query_result_and_region_obligations( + &ObligationCause::dummy(), + param_env, + &canonical_var_values, + canonical_result, + output_query_region_constraints, + )?; + + // Typically, instantiating NLL query results does not + // create obligations. However, in some cases there + // are unresolved type variables, and unify them *can* + // create obligations. In that case, we have to go + // fulfill them. We do this via a (recursive) query. + for obligation in obligations { + let () = prove_predicate::ProvePredicate::new( + obligation.param_env, + obligation.predicate, + ).fully_perform_into(infcx, output_query_region_constraints)?; + } + + Ok(value) + } + } + } } impl<'gcx: 'tcx, 'tcx, Q> TypeOp<'gcx, 'tcx> for Q @@ -138,38 +117,21 @@ where { type Output = Q::QueryResult; - fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::Output, Self> { - QueryTypeOp::trivial_noop(self, tcx) - } - - fn perform( + fn fully_perform( self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, - ) -> Fallible<InferOk<'tcx, Self::Output>> { - let param_env = self.param_env(); - - // FIXME(#33684) -- We need to use - // `canonicalize_hr_query_hack` here because of things like - // the subtype query, which go awry around `'static` - // otherwise. - let query_key = self.into_query_key(); - let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&query_key); - let canonical_result = Q::perform_query(infcx.tcx, canonical_self)?; - - // FIXME: This is not the most efficient setup. The - // `instantiate_query_result_and_region_obligations` basically - // takes the `QueryRegionConstraint` values that we ultimately - // want to use and converts them into obligations. We return - // those to our caller, which will convert them into AST - // region constraints; we then convert *those* back into - // `QueryRegionConstraint` and ultimately into NLL - // constraints. We should cut out the middleman but that will - // take a bit of refactoring. - Ok(infcx.instantiate_query_result_and_region_obligations( - &ObligationCause::dummy(), - param_env, - &canonical_var_values, - Q::upcast_result(&canonical_result), - )?) + ) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)> { + let mut qrc = vec![]; + let r = Q::fully_perform_into(self, infcx, &mut qrc)?; + + // Promote the final query-region-constraints into a + // (optional) ref-counted vector: + let opt_qrc = if qrc.is_empty() { + None + } else { + Some(Rc::new(qrc)) + }; + + Ok((r, opt_qrc)) } } diff --git a/src/librustc/traits/query/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs index 8c5bbe05616..b72c887ba50 100644 --- a/src/librustc/traits/query/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs @@ -36,7 +36,7 @@ where type QueryKey = Self; type QueryResult = T; - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<T, Self> { + fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<T, Self> { if !self.value.has_projections() { Ok(self.value) } else { @@ -44,12 +44,8 @@ where } } - fn into_query_key(self) -> Self { - self - } - - fn param_env(&self) -> ParamEnv<'tcx> { - self.param_env + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { + key.param_env } fn perform_query( diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index a6fbb165098..0254b901a8c 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -36,20 +36,16 @@ where type QueryKey = ParamEnvAnd<'tcx, Ty<'tcx>>; type QueryResult = DropckOutlivesResult<'tcx>; - fn trivial_noop(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::QueryResult, Self> { + fn prequery(self, tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::QueryResult, Self::QueryKey> { if trivial_dropck_outlives(tcx, self.dropped_ty) { Ok(DropckOutlivesResult::default()) } else { - Err(self) + Err(self.param_env.and(self.dropped_ty)) } } - fn param_env(&self) -> ParamEnv<'tcx> { - self.param_env - } - - fn into_query_key(self) -> Self::QueryKey { - self.param_env.and(self.dropped_ty) + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { + key.param_env } fn perform_query( diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index 1d02fe9a113..b06ad32ff22 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs @@ -31,16 +31,12 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ProvePredicate<'tcx> { type QueryKey = Self; type QueryResult = (); - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::QueryResult, Self> { + fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<Self::QueryResult, Self::QueryKey> { Err(self) } - fn into_query_key(self) -> Self { - self - } - - fn param_env(&self) -> ParamEnv<'tcx> { - self.param_env + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { + key.param_env } fn perform_query( diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index 0842b78ce8b..bdc7dd9589b 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs @@ -33,7 +33,7 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { type QueryKey = Self; type QueryResult = (); - fn trivial_noop(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<(), Self> { + fn prequery(self, _tcx: TyCtxt<'_, 'gcx, 'tcx>) -> Result<(), Self::QueryKey> { if self.sub == self.sup { Ok(()) } else { @@ -41,12 +41,8 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for Subtype<'tcx> { } } - fn into_query_key(self) -> Self { - self - } - - fn param_env(&self) -> ParamEnv<'tcx> { - self.param_env + fn param_env(key: &Self::QueryKey) -> ParamEnv<'tcx> { + key.param_env } fn perform_query( |
