]>
Commit | Line | Data |
---|---|---|
9fa01778 | 1 | use crate::infer::canonical::{ |
dfeec247 | 2 | Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, QueryRegionConstraints, |
0bf4aa26 | 3 | }; |
9fa01778 | 4 | use crate::infer::{InferCtxt, InferOk}; |
9fa01778 XL |
5 | use crate::traits::query::Fallible; |
6 | use crate::traits::ObligationCause; | |
5099ac24 FG |
7 | use rustc_infer::infer::canonical::{Canonical, Certainty}; |
8 | use rustc_infer::traits::query::NoSolution; | |
9 | use rustc_infer::traits::PredicateObligations; | |
ba9703b0 XL |
10 | use rustc_middle::ty::fold::TypeFoldable; |
11 | use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; | |
dfeec247 XL |
12 | use std::fmt; |
13 | use std::rc::Rc; | |
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 | |
8faf50e0 XL |
26 | /// "Type ops" are used in NLL to perform some particular action and |
27 | /// extract out the resulting region constraints (or an error if it | |
28 | /// cannot be completed). | |
dc9dc135 | 29 | pub trait TypeOp<'tcx>: Sized + fmt::Debug { |
8faf50e0 XL |
30 | type Output; |
31 | ||
32 | /// Processes the operation and all resulting obligations, | |
33 | /// returning the final result along with any region constraints | |
34 | /// (they will be given over to the NLL region solver). | |
94222f64 XL |
35 | fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>>; |
36 | } | |
37 | ||
38 | /// The output from performing a type op | |
39 | pub struct TypeOpOutput<'tcx, Op: TypeOp<'tcx>> { | |
40 | /// The output from the type op. | |
41 | pub output: Op::Output, | |
42 | /// Any region constraints from performing the type op. | |
43 | pub constraints: Option<Rc<QueryRegionConstraints<'tcx>>>, | |
44 | /// The canonicalized form of the query. | |
45 | /// This for error reporting to be able to rerun the query. | |
46 | pub canonicalized_query: Option<Canonical<'tcx, Op>>, | |
8faf50e0 XL |
47 | } |
48 | ||
49 | /// "Query type ops" are type ops that are implemented using a | |
50 | /// [canonical query][c]. The `Self` type here contains the kernel of | |
51 | /// information needed to do the operation -- `TypeOp` is actually | |
52 | /// implemented for `ParamEnvAnd<Self>`, since we always need to bring | |
53 | /// along a parameter environment as well. For query type-ops, we will | |
54 | /// first canonicalize the key and then invoke the query on the tcx, | |
55 | /// which produces the resulting query region constraints. | |
56 | /// | |
f9f354fc | 57 | /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html |
94222f64 | 58 | pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<'tcx> + 'tcx { |
dc9dc135 | 59 | type QueryResponse: TypeFoldable<'tcx>; |
8faf50e0 XL |
60 | |
61 | /// Give query the option for a simple fast path that never | |
62 | /// actually hits the tcx cache lookup etc. Return `Some(r)` with | |
63 | /// a final result or `None` to do the full path. | |
64 | fn try_fast_path( | |
dc9dc135 | 65 | tcx: TyCtxt<'tcx>, |
8faf50e0 | 66 | key: &ParamEnvAnd<'tcx, Self>, |
0bf4aa26 | 67 | ) -> Option<Self::QueryResponse>; |
8faf50e0 XL |
68 | |
69 | /// Performs the actual query with the canonicalized key -- the | |
70 | /// real work happens here. This method is not given an `infcx` | |
71 | /// because it shouldn't need one -- and if it had access to one, | |
72 | /// it might do things like invoke `sub_regions`, which would be | |
73 | /// bad, because it would create subregion relationships that are | |
74 | /// not captured in the return value. | |
75 | fn perform_query( | |
dc9dc135 XL |
76 | tcx: TyCtxt<'tcx>, |
77 | canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, | |
78 | ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>>; | |
8faf50e0 | 79 | |
8faf50e0 XL |
80 | fn fully_perform_into( |
81 | query_key: ParamEnvAnd<'tcx, Self>, | |
dc9dc135 XL |
82 | infcx: &InferCtxt<'_, 'tcx>, |
83 | output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, | |
5099ac24 FG |
84 | ) -> Fallible<( |
85 | Self::QueryResponse, | |
86 | Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>, | |
87 | PredicateObligations<'tcx>, | |
88 | Certainty, | |
89 | )> { | |
8faf50e0 | 90 | if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { |
5099ac24 | 91 | return Ok((result, None, vec![], Certainty::Proven)); |
8faf50e0 XL |
92 | } |
93 | ||
94 | // FIXME(#33684) -- We need to use | |
136023e0 | 95 | // `canonicalize_query_keep_static` here because of things |
8faf50e0 XL |
96 | // like the subtype query, which go awry around |
97 | // `'static` otherwise. | |
0bf4aa26 | 98 | let mut canonical_var_values = OriginalQueryValues::default(); |
fc512014 | 99 | let old_param_env = query_key.param_env; |
136023e0 XL |
100 | let canonical_self = |
101 | infcx.canonicalize_query_keep_static(query_key, &mut canonical_var_values); | |
8faf50e0 | 102 | let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; |
8faf50e0 | 103 | |
8faf50e0 | 104 | let InferOk { value, obligations } = infcx |
0bf4aa26 | 105 | .instantiate_nll_query_response_and_region_obligations( |
8faf50e0 | 106 | &ObligationCause::dummy(), |
fc512014 | 107 | old_param_env, |
8faf50e0 XL |
108 | &canonical_var_values, |
109 | canonical_result, | |
110 | output_query_region_constraints, | |
111 | )?; | |
112 | ||
5099ac24 | 113 | Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty)) |
8faf50e0 XL |
114 | } |
115 | } | |
116 | ||
dc9dc135 | 117 | impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q> |
8faf50e0 | 118 | where |
dc9dc135 | 119 | Q: QueryTypeOp<'tcx>, |
8faf50e0 | 120 | { |
0bf4aa26 | 121 | type Output = Q::QueryResponse; |
8faf50e0 | 122 | |
94222f64 | 123 | fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> { |
dc9dc135 | 124 | let mut region_constraints = QueryRegionConstraints::default(); |
5099ac24 | 125 | let (output, canonicalized_query, mut obligations, _) = |
94222f64 | 126 | Q::fully_perform_into(self, infcx, &mut region_constraints)?; |
8faf50e0 | 127 | |
5099ac24 FG |
128 | // Typically, instantiating NLL query results does not |
129 | // create obligations. However, in some cases there | |
130 | // are unresolved type variables, and unify them *can* | |
131 | // create obligations. In that case, we have to go | |
132 | // fulfill them. We do this via a (recursive) query. | |
133 | while !obligations.is_empty() { | |
134 | trace!("{:#?}", obligations); | |
135 | let mut progress = false; | |
136 | for obligation in std::mem::take(&mut obligations) { | |
137 | let obligation = infcx.resolve_vars_if_possible(obligation); | |
138 | match ProvePredicate::fully_perform_into( | |
139 | obligation.param_env.and(ProvePredicate::new(obligation.predicate)), | |
140 | infcx, | |
141 | &mut region_constraints, | |
142 | ) { | |
143 | Ok(((), _, new, certainty)) => { | |
144 | obligations.extend(new); | |
145 | progress = true; | |
146 | if let Certainty::Ambiguous = certainty { | |
147 | obligations.push(obligation); | |
148 | } | |
149 | } | |
150 | Err(_) => obligations.push(obligation), | |
151 | } | |
152 | } | |
153 | if !progress { | |
154 | return Err(NoSolution); | |
155 | } | |
156 | } | |
157 | ||
8faf50e0 XL |
158 | // Promote the final query-region-constraints into a |
159 | // (optional) ref-counted vector: | |
94222f64 | 160 | let region_constraints = |
dfeec247 | 161 | if region_constraints.is_empty() { None } else { Some(Rc::new(region_constraints)) }; |
8faf50e0 | 162 | |
94222f64 | 163 | Ok(TypeOpOutput { output, constraints: region_constraints, canonicalized_query }) |
8faf50e0 XL |
164 | } |
165 | } |