]>
Commit | Line | Data |
---|---|---|
9fa01778 | 1 | use crate::infer::canonical::{ |
9c376795 | 2 | Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints, |
0bf4aa26 | 3 | }; |
9fa01778 | 4 | use crate::infer::{InferCtxt, InferOk}; |
49aad941 FG |
5 | use crate::traits::{ObligationCause, ObligationCtxt}; |
6 | use rustc_errors::ErrorGuaranteed; | |
9c376795 | 7 | use rustc_infer::infer::canonical::Certainty; |
5099ac24 | 8 | use rustc_infer::traits::PredicateObligations; |
49aad941 | 9 | use rustc_middle::traits::query::NoSolution; |
ba9703b0 XL |
10 | use rustc_middle::ty::fold::TypeFoldable; |
11 | use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; | |
49aad941 | 12 | use rustc_span::Span; |
dfeec247 | 13 | use std::fmt; |
8faf50e0 | 14 | |
0bf4aa26 | 15 | pub mod ascribe_user_type; |
8faf50e0 XL |
16 | pub mod custom; |
17 | pub mod eq; | |
b7449926 | 18 | pub mod implied_outlives_bounds; |
8faf50e0 XL |
19 | pub mod normalize; |
20 | pub mod outlives; | |
21 | pub mod prove_predicate; | |
8faf50e0 XL |
22 | pub mod subtype; |
23 | ||
ba9703b0 | 24 | pub use rustc_middle::traits::query::type_op::*; |
74b04a01 | 25 | |
49aad941 FG |
26 | use self::custom::scrape_region_constraints; |
27 | ||
8faf50e0 XL |
28 | /// "Type ops" are used in NLL to perform some particular action and |
29 | /// extract out the resulting region constraints (or an error if it | |
30 | /// cannot be completed). | |
dc9dc135 | 31 | pub trait TypeOp<'tcx>: Sized + fmt::Debug { |
f2b60f7d | 32 | type Output: fmt::Debug; |
5e7ed085 | 33 | type ErrorInfo; |
8faf50e0 XL |
34 | |
35 | /// Processes the operation and all resulting obligations, | |
36 | /// returning the final result along with any region constraints | |
37 | /// (they will be given over to the NLL region solver). | |
49aad941 FG |
38 | fn fully_perform( |
39 | self, | |
40 | infcx: &InferCtxt<'tcx>, | |
41 | span: Span, | |
42 | ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>; | |
94222f64 XL |
43 | } |
44 | ||
45 | /// The output from performing a type op | |
46 | pub struct TypeOpOutput<'tcx, Op: TypeOp<'tcx>> { | |
47 | /// The output from the type op. | |
48 | pub output: Op::Output, | |
49 | /// Any region constraints from performing the type op. | |
064997fb | 50 | pub constraints: Option<&'tcx QueryRegionConstraints<'tcx>>, |
5e7ed085 FG |
51 | /// Used for error reporting to be able to rerun the query |
52 | pub error_info: Option<Op::ErrorInfo>, | |
8faf50e0 XL |
53 | } |
54 | ||
55 | /// "Query type ops" are type ops that are implemented using a | |
56 | /// [canonical query][c]. The `Self` type here contains the kernel of | |
57 | /// information needed to do the operation -- `TypeOp` is actually | |
58 | /// implemented for `ParamEnvAnd<Self>`, since we always need to bring | |
59 | /// along a parameter environment as well. For query type-ops, we will | |
60 | /// first canonicalize the key and then invoke the query on the tcx, | |
61 | /// which produces the resulting query region constraints. | |
62 | /// | |
f9f354fc | 63 | /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html |
9ffffee4 FG |
64 | pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 'tcx { |
65 | type QueryResponse: TypeFoldable<TyCtxt<'tcx>>; | |
8faf50e0 XL |
66 | |
67 | /// Give query the option for a simple fast path that never | |
68 | /// actually hits the tcx cache lookup etc. Return `Some(r)` with | |
69 | /// a final result or `None` to do the full path. | |
70 | fn try_fast_path( | |
dc9dc135 | 71 | tcx: TyCtxt<'tcx>, |
8faf50e0 | 72 | key: &ParamEnvAnd<'tcx, Self>, |
0bf4aa26 | 73 | ) -> Option<Self::QueryResponse>; |
8faf50e0 XL |
74 | |
75 | /// Performs the actual query with the canonicalized key -- the | |
76 | /// real work happens here. This method is not given an `infcx` | |
77 | /// because it shouldn't need one -- and if it had access to one, | |
78 | /// it might do things like invoke `sub_regions`, which would be | |
79 | /// bad, because it would create subregion relationships that are | |
80 | /// not captured in the return value. | |
81 | fn perform_query( | |
dc9dc135 | 82 | tcx: TyCtxt<'tcx>, |
9c376795 | 83 | canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, |
49aad941 FG |
84 | ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution>; |
85 | ||
86 | /// In the new trait solver, we already do caching in the solver itself, | |
87 | /// so there's no need to canonicalize and cache via the query system. | |
88 | /// Additionally, even if we were to canonicalize, we'd still need to | |
89 | /// make sure to feed it predefined opaque types and the defining anchor | |
90 | /// and that would require duplicating all of the tcx queries. Instead, | |
91 | /// just perform these ops locally. | |
92 | fn perform_locally_in_new_solver( | |
93 | ocx: &ObligationCtxt<'_, 'tcx>, | |
94 | key: ParamEnvAnd<'tcx, Self>, | |
95 | ) -> Result<Self::QueryResponse, NoSolution>; | |
8faf50e0 | 96 | |
8faf50e0 XL |
97 | fn fully_perform_into( |
98 | query_key: ParamEnvAnd<'tcx, Self>, | |
2b03887a | 99 | infcx: &InferCtxt<'tcx>, |
dc9dc135 | 100 | output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, |
49aad941 FG |
101 | ) -> Result< |
102 | ( | |
103 | Self::QueryResponse, | |
104 | Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>, | |
105 | PredicateObligations<'tcx>, | |
106 | Certainty, | |
107 | ), | |
108 | NoSolution, | |
109 | > { | |
8faf50e0 | 110 | if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { |
5099ac24 | 111 | return Ok((result, None, vec![], Certainty::Proven)); |
8faf50e0 XL |
112 | } |
113 | ||
114 | // FIXME(#33684) -- We need to use | |
136023e0 | 115 | // `canonicalize_query_keep_static` here because of things |
8faf50e0 XL |
116 | // like the subtype query, which go awry around |
117 | // `'static` otherwise. | |
0bf4aa26 | 118 | let mut canonical_var_values = OriginalQueryValues::default(); |
fc512014 | 119 | let old_param_env = query_key.param_env; |
136023e0 XL |
120 | let canonical_self = |
121 | infcx.canonicalize_query_keep_static(query_key, &mut canonical_var_values); | |
8faf50e0 | 122 | let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; |
8faf50e0 | 123 | |
8faf50e0 | 124 | let InferOk { value, obligations } = infcx |
0bf4aa26 | 125 | .instantiate_nll_query_response_and_region_obligations( |
8faf50e0 | 126 | &ObligationCause::dummy(), |
fc512014 | 127 | old_param_env, |
8faf50e0 XL |
128 | &canonical_var_values, |
129 | canonical_result, | |
130 | output_query_region_constraints, | |
131 | )?; | |
132 | ||
5099ac24 | 133 | Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty)) |
8faf50e0 XL |
134 | } |
135 | } | |
136 | ||
dc9dc135 | 137 | impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q> |
8faf50e0 | 138 | where |
dc9dc135 | 139 | Q: QueryTypeOp<'tcx>, |
8faf50e0 | 140 | { |
0bf4aa26 | 141 | type Output = Q::QueryResponse; |
5e7ed085 | 142 | type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>; |
8faf50e0 | 143 | |
49aad941 FG |
144 | fn fully_perform( |
145 | self, | |
146 | infcx: &InferCtxt<'tcx>, | |
147 | span: Span, | |
148 | ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> { | |
149 | if infcx.tcx.trait_solver_next() { | |
150 | return Ok(scrape_region_constraints( | |
151 | infcx, | |
152 | |ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self), | |
153 | "query type op", | |
154 | span, | |
155 | )? | |
156 | .0); | |
157 | } | |
158 | ||
dc9dc135 | 159 | let mut region_constraints = QueryRegionConstraints::default(); |
5e7ed085 | 160 | let (output, error_info, mut obligations, _) = |
49aad941 FG |
161 | Q::fully_perform_into(self, infcx, &mut region_constraints).map_err(|_| { |
162 | infcx.tcx.sess.delay_span_bug(span, format!("error performing {self:?}")) | |
163 | })?; | |
8faf50e0 | 164 | |
5099ac24 FG |
165 | // Typically, instantiating NLL query results does not |
166 | // create obligations. However, in some cases there | |
167 | // are unresolved type variables, and unify them *can* | |
168 | // create obligations. In that case, we have to go | |
169 | // fulfill them. We do this via a (recursive) query. | |
170 | while !obligations.is_empty() { | |
171 | trace!("{:#?}", obligations); | |
172 | let mut progress = false; | |
173 | for obligation in std::mem::take(&mut obligations) { | |
174 | let obligation = infcx.resolve_vars_if_possible(obligation); | |
175 | match ProvePredicate::fully_perform_into( | |
176 | obligation.param_env.and(ProvePredicate::new(obligation.predicate)), | |
177 | infcx, | |
178 | &mut region_constraints, | |
179 | ) { | |
180 | Ok(((), _, new, certainty)) => { | |
181 | obligations.extend(new); | |
182 | progress = true; | |
183 | if let Certainty::Ambiguous = certainty { | |
184 | obligations.push(obligation); | |
185 | } | |
186 | } | |
187 | Err(_) => obligations.push(obligation), | |
188 | } | |
189 | } | |
190 | if !progress { | |
49aad941 FG |
191 | return Err(infcx.tcx.sess.delay_span_bug( |
192 | span, | |
193 | format!("ambiguity processing {obligations:?} from {self:?}"), | |
194 | )); | |
5099ac24 FG |
195 | } |
196 | } | |
197 | ||
064997fb FG |
198 | Ok(TypeOpOutput { |
199 | output, | |
200 | constraints: if region_constraints.is_empty() { | |
201 | None | |
202 | } else { | |
203 | Some(infcx.tcx.arena.alloc(region_constraints)) | |
204 | }, | |
205 | error_info, | |
206 | }) | |
8faf50e0 XL |
207 | } |
208 | } |