|
17 | 17 | //!
|
18 | 18 | //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html
|
19 | 19 |
|
| 20 | +use either::Either; |
20 | 21 | use infer::canonical::substitute::substitute_value;
|
21 | 22 | use infer::canonical::{
|
22 |
| - Canonical, CanonicalVarValues, CanonicalizedQueryResult, Certainty, QueryRegionConstraint, |
23 |
| - QueryResult, |
| 23 | + Canonical, CanonicalVarKind, CanonicalVarValues, CanonicalizedQueryResult, Certainty, |
| 24 | + QueryRegionConstraint, QueryResult, |
24 | 25 | };
|
25 | 26 | use infer::region_constraints::{Constraint, RegionConstraintData};
|
26 | 27 | use infer::{InferCtxt, InferOk, InferResult, RegionObligation};
|
27 | 28 | use rustc_data_structures::indexed_vec::Idx;
|
28 | 29 | use rustc_data_structures::indexed_vec::IndexVec;
|
29 | 30 | use rustc_data_structures::sync::Lrc;
|
30 | 31 | use std::fmt::Debug;
|
| 32 | +use std::iter::once; |
31 | 33 | use syntax::ast;
|
32 | 34 | use traits::query::NoSolution;
|
33 | 35 | use traits::{FulfillmentContext, TraitEngine};
|
@@ -176,6 +178,86 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
|
176 | 178 | })
|
177 | 179 | }
|
178 | 180 |
|
| 181 | + /// NLL does a lot of queries that have a particular form that we |
| 182 | + /// can take advantage of to be more efficient. These queries do |
| 183 | + /// not have any *type* inference variables, only region inference |
| 184 | + /// variables. Therefore, when we instantiate the query result, we |
| 185 | + /// only ever produce new *region constraints* and never other |
| 186 | + /// forms of obligations (moreover, since we only determine |
| 187 | + /// satisfiability modulo region constraints, instantiation is |
| 188 | + /// infallible). Therefore, the return value need only be a larger |
| 189 | + /// set of query region constraints. These constraints can then be |
| 190 | + /// added directly to the NLL inference context. |
| 191 | + pub fn instantiate_nll_query_result_and_region_obligations<R>( |
| 192 | + &self, |
| 193 | + cause: &ObligationCause<'tcx>, |
| 194 | + original_values: &CanonicalVarValues<'tcx>, |
| 195 | + query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, |
| 196 | + ) -> Vec<QueryRegionConstraint<'tcx>> |
| 197 | + where |
| 198 | + R: Debug + TypeFoldable<'tcx>, |
| 199 | + { |
| 200 | + // In an NLL query, there should be no type variables in the |
| 201 | + // query, only region variables. |
| 202 | + debug_assert!(query_result.variables.iter().all(|v| match v.kind { |
| 203 | + CanonicalVarKind::Ty(_) => false, |
| 204 | + CanonicalVarKind::Region => true, |
| 205 | + })); |
| 206 | + |
| 207 | + let result_subst = |
| 208 | + self.query_result_substitution_guess(cause, original_values, query_result); |
| 209 | + |
| 210 | + // Compute `QueryRegionConstraint` values that unify each of |
| 211 | + // the original values `v_o` that was canonicalized into a |
| 212 | + // variable... |
| 213 | + let qrc_from_unify = original_values.var_values.iter_enumerated().flat_map( |
| 214 | + |(index, original_value)| { |
| 215 | + // ...with the value `v_r` of that variable from the query. |
| 216 | + let result_value = |
| 217 | + query_result |
| 218 | + .substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]); |
| 219 | + match (original_value.unpack(), result_value.unpack()) { |
| 220 | + ( |
| 221 | + UnpackedKind::Lifetime(ty::ReErased), |
| 222 | + UnpackedKind::Lifetime(ty::ReErased), |
| 223 | + ) => { |
| 224 | + // no action needed |
| 225 | + Either::Left(None.into_iter()) |
| 226 | + } |
| 227 | + |
| 228 | + (UnpackedKind::Lifetime(v_o), UnpackedKind::Lifetime(v_r)) => { |
| 229 | + // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. |
| 230 | + Either::Right( |
| 231 | + once(ty::OutlivesPredicate(v_o.into(), v_r)) |
| 232 | + .chain(once(ty::OutlivesPredicate(v_r.into(), v_o))) |
| 233 | + .map(ty::Binder::dummy), |
| 234 | + ) |
| 235 | + } |
| 236 | + |
| 237 | + (UnpackedKind::Type(_), _) | (_, UnpackedKind::Type(_)) => { |
| 238 | + // in NLL queries, we do not expect `type` results. |
| 239 | + bug!( |
| 240 | + "unexpected type in NLL query: cannot unify {:?} and {:?}", |
| 241 | + original_value, |
| 242 | + result_value, |
| 243 | + ); |
| 244 | + } |
| 245 | + } |
| 246 | + }, |
| 247 | + ); |
| 248 | + |
| 249 | + // ...also include the other query region constraints from the query. |
| 250 | + let qrc_from_result = query_result.value.region_constraints.iter().map(|r_c| { |
| 251 | + r_c.map_bound(|ty::OutlivesPredicate(k1, r2)| { |
| 252 | + let k1 = substitute_value(self.tcx, &result_subst, &k1); |
| 253 | + let r2 = substitute_value(self.tcx, &result_subst, &r2); |
| 254 | + ty::OutlivesPredicate(k1, r2) |
| 255 | + }) |
| 256 | + }); |
| 257 | + |
| 258 | + qrc_from_unify.chain(qrc_from_result).collect() |
| 259 | + } |
| 260 | + |
179 | 261 | /// Given the original values and the (canonicalized) result from
|
180 | 262 | /// computing a query, returns a substitution that can be applied
|
181 | 263 | /// to the query result to convert the result back into the
|
|
0 commit comments