1 use crate::infer
::canonical
::{
2 Canonical
, CanonicalQueryResponse
, OriginalQueryValues
, QueryRegionConstraints
,
4 use crate::infer
::{InferCtxt, InferOk}
;
5 use crate::traits
::{ObligationCause, ObligationCtxt}
;
6 use rustc_errors
::ErrorGuaranteed
;
7 use rustc_infer
::infer
::canonical
::Certainty
;
8 use rustc_infer
::traits
::PredicateObligations
;
9 use rustc_middle
::traits
::query
::NoSolution
;
10 use rustc_middle
::ty
::fold
::TypeFoldable
;
11 use rustc_middle
::ty
::{ParamEnvAnd, TyCtxt}
;
15 pub mod ascribe_user_type
;
18 pub mod implied_outlives_bounds
;
21 pub mod prove_predicate
;
24 pub use rustc_middle
::traits
::query
::type_op
::*;
26 use self::custom
::scrape_region_constraints
;
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).
31 pub trait TypeOp
<'tcx
>: Sized
+ fmt
::Debug
{
32 type Output
: fmt
::Debug
;
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).
40 infcx
: &InferCtxt
<'tcx
>,
42 ) -> Result
<TypeOpOutput
<'tcx
, Self>, ErrorGuaranteed
>;
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.
50 pub constraints
: Option
<&'tcx QueryRegionConstraints
<'tcx
>>,
51 /// Used for error reporting to be able to rerun the query
52 pub error_info
: Option
<Op
::ErrorInfo
>,
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.
63 /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
64 pub trait QueryTypeOp
<'tcx
>: fmt
::Debug
+ Copy
+ TypeFoldable
<TyCtxt
<'tcx
>> + 'tcx
{
65 type QueryResponse
: TypeFoldable
<TyCtxt
<'tcx
>>;
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.
72 key
: &ParamEnvAnd
<'tcx
, Self>,
73 ) -> Option
<Self::QueryResponse
>;
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.
83 canonicalized
: Canonical
<'tcx
, ParamEnvAnd
<'tcx
, Self>>,
84 ) -> Result
<CanonicalQueryResponse
<'tcx
, Self::QueryResponse
>, NoSolution
>;
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
>;
97 fn fully_perform_into(
98 query_key
: ParamEnvAnd
<'tcx
, Self>,
99 infcx
: &InferCtxt
<'tcx
>,
100 output_query_region_constraints
: &mut QueryRegionConstraints
<'tcx
>,
104 Option
<Canonical
<'tcx
, ParamEnvAnd
<'tcx
, Self>>>,
105 PredicateObligations
<'tcx
>,
110 if let Some(result
) = QueryTypeOp
::try_fast_path(infcx
.tcx
, &query_key
) {
111 return Ok((result
, None
, vec
![], Certainty
::Proven
));
114 // FIXME(#33684) -- We need to use
115 // `canonicalize_query_keep_static` here because of things
116 // like the subtype query, which go awry around
117 // `'static` otherwise.
118 let mut canonical_var_values
= OriginalQueryValues
::default();
119 let old_param_env
= query_key
.param_env
;
121 infcx
.canonicalize_query_keep_static(query_key
, &mut canonical_var_values
);
122 let canonical_result
= Self::perform_query(infcx
.tcx
, canonical_self
)?
;
124 let InferOk { value, obligations }
= infcx
125 .instantiate_nll_query_response_and_region_obligations(
126 &ObligationCause
::dummy(),
128 &canonical_var_values
,
130 output_query_region_constraints
,
133 Ok((value
, Some(canonical_self
), obligations
, canonical_result
.value
.certainty
))
137 impl<'tcx
, Q
> TypeOp
<'tcx
> for ParamEnvAnd
<'tcx
, Q
>
139 Q
: QueryTypeOp
<'tcx
>,
141 type Output
= Q
::QueryResponse
;
142 type ErrorInfo
= Canonical
<'tcx
, ParamEnvAnd
<'tcx
, Q
>>;
146 infcx
: &InferCtxt
<'tcx
>,
148 ) -> Result
<TypeOpOutput
<'tcx
, Self>, ErrorGuaranteed
> {
149 if infcx
.tcx
.trait_solver_next() {
150 return Ok(scrape_region_constraints(
152 |ocx
| QueryTypeOp
::perform_locally_in_new_solver(ocx
, self),
159 let mut region_constraints
= QueryRegionConstraints
::default();
160 let (output
, error_info
, mut obligations
, _
) =
161 Q
::fully_perform_into(self, infcx
, &mut region_constraints
).map_err(|_
| {
162 infcx
.tcx
.sess
.delay_span_bug(span
, format
!("error performing {self:?}"))
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
)),
178 &mut region_constraints
,
180 Ok(((), _
, new
, certainty
)) => {
181 obligations
.extend(new
);
183 if let Certainty
::Ambiguous
= certainty
{
184 obligations
.push(obligation
);
187 Err(_
) => obligations
.push(obligation
),
191 return Err(infcx
.tcx
.sess
.delay_span_bug(
193 format
!("ambiguity processing {obligations:?} from {self:?}"),
200 constraints
: if region_constraints
.is_empty() {
203 Some(infcx
.tcx
.arena
.alloc(region_constraints
))