]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
New upstream version 1.60.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
XL
12use std::fmt;
13use std::rc::Rc;
8faf50e0 14
0bf4aa26 15pub mod ascribe_user_type;
8faf50e0
XL
16pub mod custom;
17pub mod eq;
b7449926 18pub mod implied_outlives_bounds;
8faf50e0
XL
19pub mod normalize;
20pub mod outlives;
21pub mod prove_predicate;
8faf50e0
XL
22pub mod subtype;
23
ba9703b0 24pub 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 29pub 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
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.
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 58pub 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 117impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q>
8faf50e0 118where
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}