]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / compiler / rustc_trait_selection / src / traits / query / type_op / mod.rs
CommitLineData
9fa01778 1use crate::infer::canonical::{
dfeec247 2 Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, QueryRegionConstraints,
0bf4aa26 3};
9fa01778 4use crate::infer::{InferCtxt, InferOk};
9fa01778
XL
5use crate::traits::query::Fallible;
6use crate::traits::ObligationCause;
5099ac24
FG
7use rustc_infer::infer::canonical::{Canonical, Certainty};
8use rustc_infer::traits::query::NoSolution;
9use rustc_infer::traits::PredicateObligations;
ba9703b0
XL
10use rustc_middle::ty::fold::TypeFoldable;
11use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
dfeec247 12use std::fmt;
8faf50e0 13
0bf4aa26 14pub mod ascribe_user_type;
8faf50e0
XL
15pub mod custom;
16pub mod eq;
b7449926 17pub mod implied_outlives_bounds;
8faf50e0
XL
18pub mod normalize;
19pub mod outlives;
20pub mod prove_predicate;
8faf50e0
XL
21pub mod subtype;
22
ba9703b0 23pub use rustc_middle::traits::query::type_op::*;
74b04a01 24
8faf50e0
XL
25/// "Type ops" are used in NLL to perform some particular action and
26/// extract out the resulting region constraints (or an error if it
27/// cannot be completed).
dc9dc135 28pub trait TypeOp<'tcx>: Sized + fmt::Debug {
f2b60f7d 29 type Output: fmt::Debug;
5e7ed085 30 type ErrorInfo;
8faf50e0
XL
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
39pub 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.
064997fb 43 pub constraints: Option<&'tcx QueryRegionConstraints<'tcx>>,
5e7ed085
FG
44 /// Used for error reporting to be able to rerun the query
45 pub error_info: Option<Op::ErrorInfo>,
8faf50e0
XL
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///
f9f354fc 56/// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
94222f64 57pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<'tcx> + 'tcx {
dc9dc135 58 type QueryResponse: TypeFoldable<'tcx>;
8faf50e0
XL
59
60 /// Give query the option for a simple fast path that never
61 /// actually hits the tcx cache lookup etc. Return `Some(r)` with
62 /// a final result or `None` to do the full path.
63 fn try_fast_path(
dc9dc135 64 tcx: TyCtxt<'tcx>,
8faf50e0 65 key: &ParamEnvAnd<'tcx, Self>,
0bf4aa26 66 ) -> Option<Self::QueryResponse>;
8faf50e0
XL
67
68 /// Performs the actual query with the canonicalized key -- the
69 /// real work happens here. This method is not given an `infcx`
70 /// because it shouldn't need one -- and if it had access to one,
71 /// it might do things like invoke `sub_regions`, which would be
72 /// bad, because it would create subregion relationships that are
73 /// not captured in the return value.
74 fn perform_query(
dc9dc135
XL
75 tcx: TyCtxt<'tcx>,
76 canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>,
77 ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>>;
8faf50e0 78
8faf50e0
XL
79 fn fully_perform_into(
80 query_key: ParamEnvAnd<'tcx, Self>,
dc9dc135
XL
81 infcx: &InferCtxt<'_, 'tcx>,
82 output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
5099ac24
FG
83 ) -> Fallible<(
84 Self::QueryResponse,
85 Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>,
86 PredicateObligations<'tcx>,
87 Certainty,
88 )> {
8faf50e0 89 if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) {
5099ac24 90 return Ok((result, None, vec![], Certainty::Proven));
8faf50e0
XL
91 }
92
93 // FIXME(#33684) -- We need to use
136023e0 94 // `canonicalize_query_keep_static` here because of things
8faf50e0
XL
95 // like the subtype query, which go awry around
96 // `'static` otherwise.
0bf4aa26 97 let mut canonical_var_values = OriginalQueryValues::default();
fc512014 98 let old_param_env = query_key.param_env;
136023e0
XL
99 let canonical_self =
100 infcx.canonicalize_query_keep_static(query_key, &mut canonical_var_values);
8faf50e0 101 let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?;
8faf50e0 102
8faf50e0 103 let InferOk { value, obligations } = infcx
0bf4aa26 104 .instantiate_nll_query_response_and_region_obligations(
8faf50e0 105 &ObligationCause::dummy(),
fc512014 106 old_param_env,
8faf50e0
XL
107 &canonical_var_values,
108 canonical_result,
109 output_query_region_constraints,
110 )?;
111
5099ac24 112 Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty))
8faf50e0
XL
113 }
114}
115
dc9dc135 116impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q>
8faf50e0 117where
dc9dc135 118 Q: QueryTypeOp<'tcx>,
8faf50e0 119{
0bf4aa26 120 type Output = Q::QueryResponse;
5e7ed085 121 type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>;
8faf50e0 122
94222f64 123 fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> {
dc9dc135 124 let mut region_constraints = QueryRegionConstraints::default();
5e7ed085 125 let (output, error_info, 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
064997fb
FG
158 Ok(TypeOpOutput {
159 output,
160 constraints: if region_constraints.is_empty() {
161 None
162 } else {
163 Some(infcx.tcx.arena.alloc(region_constraints))
164 },
165 error_info,
166 })
8faf50e0
XL
167 }
168}