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