Skip to main content

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 !infcx.disable_trait_solver_fast_paths()
114            && let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key)
115        {
116            return Ok((result, None, PredicateObligations::new(), Certainty::Proven));
117        }
118
119        let mut canonical_var_values = OriginalQueryValues::default();
120        let old_param_env = query_key.param_env;
121        let canonical_self = infcx.canonicalize_query(query_key, &mut canonical_var_values);
122        let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?;
123
124        let InferOk { value, obligations } = infcx
125            .instantiate_nll_query_response_and_region_obligations(
126                &ObligationCause::dummy_with_span(span),
127                old_param_env,
128                &canonical_var_values,
129                canonical_result,
130                output_query_region_constraints,
131            )?;
132
133        Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty))
134    }
135}
136
137impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q>
138where
139    Q: QueryTypeOp<'tcx>,
140{
141    type Output = Q::QueryResponse;
142    type ErrorInfo = CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Q>>;
143
144    fn fully_perform(
145        self,
146        infcx: &InferCtxt<'tcx>,
147        root_def_id: LocalDefId,
148        span: Span,
149    ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
150        // In the new trait solver, query type ops are performed locally. This
151        // is because query type ops currently use the old canonicalizer, and
152        // that doesn't preserve things like opaques which have been registered
153        // during MIR typeck. Even after the old canonicalizer is gone, it's
154        // probably worthwhile just keeping this run-locally logic, since we
155        // probably don't gain much from caching here given the new solver does
156        // caching internally.
157        if infcx.next_trait_solver() {
158            return Ok(scrape_region_constraints(
159                infcx,
160                root_def_id,
161                "query type op",
162                span,
163                |ocx| {
164                    if !infcx.disable_trait_solver_fast_paths()
165                        && let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &self)
166                    {
167                        return Ok(result);
168                    }
169                    QueryTypeOp::perform_locally_with_next_solver(ocx, self, span)
170                },
171            )?
172            .0);
173        }
174
175        let mut error_info = None;
176        let mut region_constraints = QueryRegionConstraints::default();
177
178        // HACK(type_alias_impl_trait): When moving an opaque type to hidden type mapping from the query to the current inferctxt,
179        // we sometimes end up with `Opaque<'a> = Opaque<'b>` instead of an actual hidden type. In that case we don't register a
180        // hidden type but just equate the lifetimes. Thus we need to scrape the region constraints even though we're also manually
181        // collecting region constraints via `region_constraints`.
182        let (mut output, _) =
183            scrape_region_constraints(infcx, root_def_id, "fully_perform", span, |ocx| {
184                let (output, ei, obligations, _) =
185                    Q::fully_perform_into(self, infcx, &mut region_constraints, span)?;
186                error_info = ei;
187
188                ocx.register_obligations(obligations);
189                Ok(output)
190            })?;
191        output.error_info = error_info;
192        if let Some(QueryRegionConstraints { constraints, assumptions }) = output.constraints {
193            region_constraints.constraints.extend(constraints.iter().cloned());
194            region_constraints.assumptions.extend(assumptions.iter().cloned());
195        }
196        output.constraints = if region_constraints.is_empty() {
197            None
198        } else {
199            Some(infcx.tcx.arena.alloc(region_constraints))
200        };
201        Ok(output)
202    }
203}