]>
Commit | Line | Data |
---|---|---|
8faf50e0 XL |
1 | // Copyright 2016 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
0bf4aa26 XL |
11 | use infer::canonical::{ |
12 | Canonical, Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, | |
13 | QueryRegionConstraint, QueryResponse, | |
14 | }; | |
8faf50e0 | 15 | use infer::{InferCtxt, InferOk}; |
8faf50e0 XL |
16 | use std::fmt; |
17 | use std::rc::Rc; | |
18 | use traits::query::Fallible; | |
19 | use traits::ObligationCause; | |
20 | use ty::fold::TypeFoldable; | |
21 | use ty::{Lift, ParamEnvAnd, TyCtxt}; | |
22 | ||
0bf4aa26 | 23 | pub mod ascribe_user_type; |
8faf50e0 XL |
24 | pub mod custom; |
25 | pub mod eq; | |
b7449926 | 26 | pub mod implied_outlives_bounds; |
8faf50e0 XL |
27 | pub mod normalize; |
28 | pub mod outlives; | |
29 | pub mod prove_predicate; | |
30 | use self::prove_predicate::ProvePredicate; | |
31 | pub mod subtype; | |
32 | ||
33 | /// "Type ops" are used in NLL to perform some particular action and | |
34 | /// extract out the resulting region constraints (or an error if it | |
35 | /// cannot be completed). | |
36 | pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug { | |
37 | type Output; | |
38 | ||
39 | /// Processes the operation and all resulting obligations, | |
40 | /// returning the final result along with any region constraints | |
41 | /// (they will be given over to the NLL region solver). | |
42 | fn fully_perform( | |
43 | self, | |
44 | infcx: &InferCtxt<'_, 'gcx, 'tcx>, | |
45 | ) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)>; | |
46 | } | |
47 | ||
48 | /// "Query type ops" are type ops that are implemented using a | |
49 | /// [canonical query][c]. The `Self` type here contains the kernel of | |
50 | /// information needed to do the operation -- `TypeOp` is actually | |
51 | /// implemented for `ParamEnvAnd<Self>`, since we always need to bring | |
52 | /// along a parameter environment as well. For query type-ops, we will | |
53 | /// first canonicalize the key and then invoke the query on the tcx, | |
54 | /// which produces the resulting query region constraints. | |
55 | /// | |
56 | /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html | |
57 | pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: | |
58 | fmt::Debug + Sized + TypeFoldable<'tcx> + Lift<'gcx> | |
59 | { | |
0bf4aa26 | 60 | type QueryResponse: TypeFoldable<'tcx> + Lift<'gcx>; |
8faf50e0 XL |
61 | |
62 | /// Give query the option for a simple fast path that never | |
63 | /// actually hits the tcx cache lookup etc. Return `Some(r)` with | |
64 | /// a final result or `None` to do the full path. | |
65 | fn try_fast_path( | |
66 | tcx: TyCtxt<'_, 'gcx, 'tcx>, | |
67 | key: &ParamEnvAnd<'tcx, Self>, | |
0bf4aa26 | 68 | ) -> Option<Self::QueryResponse>; |
8faf50e0 XL |
69 | |
70 | /// Performs the actual query with the canonicalized key -- the | |
71 | /// real work happens here. This method is not given an `infcx` | |
72 | /// because it shouldn't need one -- and if it had access to one, | |
73 | /// it might do things like invoke `sub_regions`, which would be | |
74 | /// bad, because it would create subregion relationships that are | |
75 | /// not captured in the return value. | |
76 | fn perform_query( | |
77 | tcx: TyCtxt<'_, 'gcx, 'tcx>, | |
78 | canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, | |
0bf4aa26 | 79 | ) -> Fallible<CanonicalizedQueryResponse<'gcx, Self::QueryResponse>>; |
8faf50e0 XL |
80 | |
81 | /// Casts a lifted query result (which is in the gcx lifetime) | |
82 | /// into the tcx lifetime. This is always just an identity cast, | |
83 | /// but the generic code doesn't realize it -- put another way, in | |
0bf4aa26 XL |
84 | /// the generic code, we have a `Lifted<'gcx, Self::QueryResponse>` |
85 | /// and we want to convert that to a `Self::QueryResponse`. This is | |
8faf50e0 XL |
86 | /// not a priori valid, so we can't do it -- but in practice, it |
87 | /// is always a no-op (e.g., the lifted form of a type, | |
88 | /// `Ty<'gcx>`, is a subtype of `Ty<'tcx>`). So we have to push | |
89 | /// the operation into the impls that know more specifically what | |
0bf4aa26 | 90 | /// `QueryResponse` is. This operation would (maybe) be nicer with |
8faf50e0 | 91 | /// something like HKTs or GATs, since then we could make |
0bf4aa26 | 92 | /// `QueryResponse` parametric and `'gcx` and `'tcx` etc. |
8faf50e0 | 93 | fn shrink_to_tcx_lifetime( |
0bf4aa26 XL |
94 | lifted_query_result: &'a CanonicalizedQueryResponse<'gcx, Self::QueryResponse>, |
95 | ) -> &'a Canonical<'tcx, QueryResponse<'tcx, Self::QueryResponse>>; | |
8faf50e0 XL |
96 | |
97 | fn fully_perform_into( | |
98 | query_key: ParamEnvAnd<'tcx, Self>, | |
99 | infcx: &InferCtxt<'_, 'gcx, 'tcx>, | |
100 | output_query_region_constraints: &mut Vec<QueryRegionConstraint<'tcx>>, | |
0bf4aa26 | 101 | ) -> Fallible<Self::QueryResponse> { |
8faf50e0 XL |
102 | if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { |
103 | return Ok(result); | |
104 | } | |
105 | ||
106 | // FIXME(#33684) -- We need to use | |
107 | // `canonicalize_hr_query_hack` here because of things | |
108 | // like the subtype query, which go awry around | |
109 | // `'static` otherwise. | |
0bf4aa26 | 110 | let mut canonical_var_values = OriginalQueryValues::default(); |
8faf50e0 XL |
111 | let canonical_self = |
112 | infcx.canonicalize_hr_query_hack(&query_key, &mut canonical_var_values); | |
113 | let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; | |
114 | let canonical_result = Self::shrink_to_tcx_lifetime(&canonical_result); | |
115 | ||
116 | let param_env = query_key.param_env; | |
117 | ||
118 | let InferOk { value, obligations } = infcx | |
0bf4aa26 | 119 | .instantiate_nll_query_response_and_region_obligations( |
8faf50e0 XL |
120 | &ObligationCause::dummy(), |
121 | param_env, | |
122 | &canonical_var_values, | |
123 | canonical_result, | |
124 | output_query_region_constraints, | |
125 | )?; | |
126 | ||
127 | // Typically, instantiating NLL query results does not | |
128 | // create obligations. However, in some cases there | |
129 | // are unresolved type variables, and unify them *can* | |
130 | // create obligations. In that case, we have to go | |
131 | // fulfill them. We do this via a (recursive) query. | |
132 | for obligation in obligations { | |
133 | let () = ProvePredicate::fully_perform_into( | |
134 | obligation | |
135 | .param_env | |
136 | .and(ProvePredicate::new(obligation.predicate)), | |
137 | infcx, | |
138 | output_query_region_constraints, | |
139 | )?; | |
140 | } | |
141 | ||
142 | Ok(value) | |
143 | } | |
144 | } | |
145 | ||
146 | impl<'gcx: 'tcx, 'tcx, Q> TypeOp<'gcx, 'tcx> for ParamEnvAnd<'tcx, Q> | |
147 | where | |
148 | Q: QueryTypeOp<'gcx, 'tcx>, | |
149 | { | |
0bf4aa26 | 150 | type Output = Q::QueryResponse; |
8faf50e0 XL |
151 | |
152 | fn fully_perform( | |
153 | self, | |
154 | infcx: &InferCtxt<'_, 'gcx, 'tcx>, | |
155 | ) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)> { | |
156 | let mut qrc = vec![]; | |
157 | let r = Q::fully_perform_into(self, infcx, &mut qrc)?; | |
158 | ||
159 | // Promote the final query-region-constraints into a | |
160 | // (optional) ref-counted vector: | |
161 | let opt_qrc = if qrc.is_empty() { | |
162 | None | |
163 | } else { | |
164 | Some(Rc::new(qrc)) | |
165 | }; | |
166 | ||
167 | Ok((r, opt_qrc)) | |
168 | } | |
169 | } |