rustc_trait_selection/traits/query/type_op/
mod.rs

1use std::fmt;
2
3use rustc_errors::ErrorGuaranteed;
4use rustc_infer::traits::PredicateObligations;
5use rustc_middle::traits::query::NoSolution;
6use rustc_middle::ty::{ParamEnvAnd, TyCtxt, TypeFoldable};
7use rustc_span::Span;
8
9use crate::infer::canonical::{
10    CanonicalQueryInput, CanonicalQueryResponse, Certainty, OriginalQueryValues,
11    QueryRegionConstraints,
12};
13use crate::infer::{InferCtxt, InferOk};
14use crate::traits::{ObligationCause, ObligationCtxt};
15
16pub mod ascribe_user_type;
17pub mod custom;
18pub mod implied_outlives_bounds;
19pub mod normalize;
20pub mod outlives;
21pub mod prove_predicate;
22
23pub use rustc_middle::traits::query::type_op::*;
24
25use self::custom::scrape_region_constraints;
26
27/// "Type ops" are used in NLL to perform some particular action and
28/// extract out the resulting region constraints (or an error if it
29/// cannot be completed).
30pub trait TypeOp<'tcx>: Sized + fmt::Debug {
31    type Output: fmt::Debug;
32    type ErrorInfo;
33
34    /// Processes the operation and all resulting obligations,
35    /// returning the final result along with any region constraints
36    /// (they will be given over to the NLL region solver).
37    fn fully_perform(
38        self,
39        infcx: &InferCtxt<'tcx>,
40        span: Span,
41    ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>;
42}
43
44/// The output from performing a type op
45pub struct TypeOpOutput<'tcx, Op: TypeOp<'tcx>> {
46    /// The output from the type op.
47    pub output: Op::Output,
48    /// Any region constraints from performing the type op.
49    pub constraints: Option<&'tcx QueryRegionConstraints<'tcx>>,
50    /// Used for error reporting to be able to rerun the query
51    pub error_info: Option<Op::ErrorInfo>,
52}
53
54/// "Query type ops" are type ops that are implemented using a
55/// [canonical query][c]. The `Self` type here contains the kernel of
56/// information needed to do the operation -- `TypeOp` is actually
57/// implemented for `ParamEnvAnd<Self>`, since we always need to bring
58/// along a parameter environment as well. For query type-ops, we will
59/// first canonicalize the key and then invoke the query on the tcx,
60/// which produces the resulting query region constraints.
61///
62/// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
63pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 'tcx {
64    type QueryResponse: TypeFoldable<TyCtxt<'tcx>>;
65
66    /// Give query the option for a simple fast path that never
67    /// actually hits the tcx cache lookup etc. Return `Some(r)` with
68    /// a final result or `None` to do the full path.
69    fn try_fast_path(
70        tcx: TyCtxt<'tcx>,
71        key: &ParamEnvAnd<'tcx, Self>,
72    ) -> Option<Self::QueryResponse>;
73
74    /// Performs the actual query with the canonicalized key -- the
75    /// real work happens here. This method is not given an `infcx`
76    /// because it shouldn't need one -- and if it had access to one,
77    /// it might do things like invoke `sub_regions`, which would be
78    /// bad, because it would create subregion relationships that are
79    /// not captured in the return value.
80    fn perform_query(
81        tcx: TyCtxt<'tcx>,
82        canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>,
83    ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution>;
84
85    /// In the new trait solver, we already do caching in the solver itself,
86    /// so there's no need to canonicalize and cache via the query system.
87    /// Additionally, even if we were to canonicalize, we'd still need to
88    /// make sure to feed it predefined opaque types and the defining anchor
89    /// and that would require duplicating all of the tcx queries. Instead,
90    /// just perform these ops locally.
91    fn perform_locally_with_next_solver(
92        ocx: &ObligationCtxt<'_, 'tcx>,
93        key: ParamEnvAnd<'tcx, Self>,
94        span: Span,
95    ) -> Result<Self::QueryResponse, NoSolution>;
96
97    fn fully_perform_into(
98        query_key: ParamEnvAnd<'tcx, Self>,
99        infcx: &InferCtxt<'tcx>,
100        output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
101        span: Span,
102    ) -> Result<
103        (
104            Self::QueryResponse,
105            Option<CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>>,
106            PredicateObligations<'tcx>,
107            Certainty,
108        ),
109        NoSolution,
110    > {
111        if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) {
112            return Ok((result, None, PredicateObligations::new(), Certainty::Proven));
113        }
114
115        let mut canonical_var_values = OriginalQueryValues::default();
116        let old_param_env = query_key.param_env;
117        let canonical_self = infcx.canonicalize_query(query_key, &mut canonical_var_values);
118        let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?;
119
120        let InferOk { value, obligations } = infcx
121            .instantiate_nll_query_response_and_region_obligations(
122                &ObligationCause::dummy_with_span(span),
123                old_param_env,
124                &canonical_var_values,
125                canonical_result,
126                output_query_region_constraints,
127            )?;
128
129        Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty))
130    }
131}
132
133impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q>
134where
135    Q: QueryTypeOp<'tcx>,
136{
137    type Output = Q::QueryResponse;
138    type ErrorInfo = CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Q>>;
139
140    fn fully_perform(
141        self,
142        infcx: &InferCtxt<'tcx>,
143        span: Span,
144    ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
145        // In the new trait solver, query type ops are performed locally. This
146        // is because query type ops currently use the old canonicalizer, and
147        // that doesn't preserve things like opaques which have been registered
148        // during MIR typeck. Even after the old canonicalizer is gone, it's
149        // probably worthwhile just keeping this run-locally logic, since we
150        // probably don't gain much from caching here given the new solver does
151        // caching internally.
152        if infcx.next_trait_solver() {
153            return Ok(scrape_region_constraints(
154                infcx,
155                |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self, span),
156                "query type op",
157                span,
158            )?
159            .0);
160        }
161
162        let mut error_info = None;
163        let mut region_constraints = QueryRegionConstraints::default();
164
165        // HACK(type_alias_impl_trait): When moving an opaque type to hidden type mapping from the query to the current inferctxt,
166        // we sometimes end up with `Opaque<'a> = Opaque<'b>` instead of an actual hidden type. In that case we don't register a
167        // hidden type but just equate the lifetimes. Thus we need to scrape the region constraints even though we're also manually
168        // collecting region constraints via `region_constraints`.
169        let (mut output, _) = scrape_region_constraints(
170            infcx,
171            |ocx| {
172                let (output, ei, obligations, _) =
173                    Q::fully_perform_into(self, infcx, &mut region_constraints, span)?;
174                error_info = ei;
175
176                ocx.register_obligations(obligations);
177                Ok(output)
178            },
179            "fully_perform",
180            span,
181        )?;
182        output.error_info = error_info;
183        if let Some(QueryRegionConstraints { outlives }) = output.constraints {
184            region_constraints.outlives.extend(outlives.iter().cloned());
185        }
186        output.constraints = if region_constraints.is_empty() {
187            None
188        } else {
189            Some(infcx.tcx.arena.alloc(region_constraints))
190        };
191        Ok(output)
192    }
193}