rustc_trait_selection/traits/query/type_op/
mod.rs

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