rustc_middle/infer/
canonical.rs

1//! **Canonicalization** is the key to constructing a query in the
2//! middle of type inference. Ordinarily, it is not possible to store
3//! types from type inference in query keys, because they contain
4//! references to inference variables whose lifetimes are too short
5//! and so forth. Canonicalizing a value T1 using `canonicalize_query`
6//! produces two things:
7//!
8//! - a value T2 where each unbound inference variable has been
9//!   replaced with a **canonical variable**;
10//! - a map M (of type `CanonicalVarValues`) from those canonical
11//!   variables back to the original.
12//!
13//! We can then do queries using T2. These will give back constraints
14//! on the canonical variables which can be translated, using the map
15//! M, into constraints in our source context. This process of
16//! translating the results back is done by the
17//! `instantiate_query_result` method.
18//!
19//! For a more detailed look at what is happening here, check
20//! out the [chapter in the rustc dev guide][c].
21//!
22//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
23
24use std::collections::hash_map::Entry;
25
26use rustc_data_structures::fx::FxHashMap;
27use rustc_data_structures::sync::Lock;
28use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
29pub use rustc_type_ir as ir;
30pub use rustc_type_ir::{CanonicalTyVarKind, CanonicalVarKind};
31use smallvec::SmallVec;
32
33use crate::mir::ConstraintCategory;
34use crate::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt};
35
36pub type CanonicalQueryInput<'tcx, V> = ir::CanonicalQueryInput<TyCtxt<'tcx>, V>;
37pub type Canonical<'tcx, V> = ir::Canonical<TyCtxt<'tcx>, V>;
38pub type CanonicalVarInfo<'tcx> = ir::CanonicalVarInfo<TyCtxt<'tcx>>;
39pub type CanonicalVarValues<'tcx> = ir::CanonicalVarValues<TyCtxt<'tcx>>;
40pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo<'tcx>>;
41
42impl<'tcx> ty::TypeFoldable<TyCtxt<'tcx>> for CanonicalVarInfos<'tcx> {
43    fn try_fold_with<F: ty::FallibleTypeFolder<TyCtxt<'tcx>>>(
44        self,
45        folder: &mut F,
46    ) -> Result<Self, F::Error> {
47        ty::util::fold_list(self, folder, |tcx, v| tcx.mk_canonical_var_infos(v))
48    }
49}
50
51/// When we canonicalize a value to form a query, we wind up replacing
52/// various parts of it with canonical variables. This struct stores
53/// those replaced bits to remember for when we process the query
54/// result.
55#[derive(Clone, Debug)]
56pub struct OriginalQueryValues<'tcx> {
57    /// Map from the universes that appear in the query to the universes in the
58    /// caller context. For all queries except `evaluate_goal` (used by Chalk),
59    /// we only ever put ROOT values into the query, so this map is very
60    /// simple.
61    pub universe_map: SmallVec<[ty::UniverseIndex; 4]>,
62
63    /// This is equivalent to `CanonicalVarValues`, but using a
64    /// `SmallVec` yields a significant performance win.
65    pub var_values: SmallVec<[GenericArg<'tcx>; 8]>,
66}
67
68impl<'tcx> Default for OriginalQueryValues<'tcx> {
69    fn default() -> Self {
70        let mut universe_map = SmallVec::default();
71        universe_map.push(ty::UniverseIndex::ROOT);
72
73        Self { universe_map, var_values: SmallVec::default() }
74    }
75}
76
77/// After we execute a query with a canonicalized key, we get back a
78/// `Canonical<QueryResponse<..>>`. You can use
79/// `instantiate_query_result` to access the data in this result.
80#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
81pub struct QueryResponse<'tcx, R> {
82    pub var_values: CanonicalVarValues<'tcx>,
83    pub region_constraints: QueryRegionConstraints<'tcx>,
84    pub certainty: Certainty,
85    pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>,
86    pub value: R,
87}
88
89#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
90#[derive(HashStable, TypeFoldable, TypeVisitable)]
91pub struct QueryRegionConstraints<'tcx> {
92    pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
93}
94
95impl QueryRegionConstraints<'_> {
96    /// Represents an empty (trivially true) set of region
97    /// constraints.
98    pub fn is_empty(&self) -> bool {
99        self.outlives.is_empty()
100    }
101}
102
103pub type CanonicalQueryResponse<'tcx, T> = &'tcx Canonical<'tcx, QueryResponse<'tcx, T>>;
104
105/// Indicates whether or not we were able to prove the query to be
106/// true.
107#[derive(Copy, Clone, Debug, HashStable)]
108pub enum Certainty {
109    /// The query is known to be true, presuming that you apply the
110    /// given `var_values` and the region-constraints are satisfied.
111    Proven,
112
113    /// The query is not known to be true, but also not known to be
114    /// false. The `var_values` represent *either* values that must
115    /// hold in order for the query to be true, or helpful tips that
116    /// *might* make it true. Currently rustc's trait solver cannot
117    /// distinguish the two (e.g., due to our preference for where
118    /// clauses over impls).
119    ///
120    /// After some unification and things have been done, it makes
121    /// sense to try and prove again -- of course, at that point, the
122    /// canonical form will be different, making this a distinct
123    /// query.
124    Ambiguous,
125}
126
127impl Certainty {
128    pub fn is_proven(&self) -> bool {
129        match self {
130            Certainty::Proven => true,
131            Certainty::Ambiguous => false,
132        }
133    }
134}
135
136impl<'tcx, R> QueryResponse<'tcx, R> {
137    pub fn is_proven(&self) -> bool {
138        self.certainty.is_proven()
139    }
140}
141
142pub type QueryOutlivesConstraint<'tcx> =
143    (ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>);
144
145#[derive(Default)]
146pub struct CanonicalParamEnvCache<'tcx> {
147    map: Lock<
148        FxHashMap<
149            ty::ParamEnv<'tcx>,
150            (Canonical<'tcx, ty::ParamEnv<'tcx>>, &'tcx [GenericArg<'tcx>]),
151        >,
152    >,
153}
154
155impl<'tcx> CanonicalParamEnvCache<'tcx> {
156    /// Gets the cached canonical form of `key` or executes
157    /// `canonicalize_op` and caches the result if not present.
158    ///
159    /// `canonicalize_op` is intentionally not allowed to be a closure to
160    /// statically prevent it from capturing `InferCtxt` and resolving
161    /// inference variables, which invalidates the cache.
162    pub fn get_or_insert(
163        &self,
164        tcx: TyCtxt<'tcx>,
165        key: ty::ParamEnv<'tcx>,
166        state: &mut OriginalQueryValues<'tcx>,
167        canonicalize_op: fn(
168            TyCtxt<'tcx>,
169            ty::ParamEnv<'tcx>,
170            &mut OriginalQueryValues<'tcx>,
171        ) -> Canonical<'tcx, ty::ParamEnv<'tcx>>,
172    ) -> Canonical<'tcx, ty::ParamEnv<'tcx>> {
173        if !key.has_type_flags(
174            TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS,
175        ) {
176            return Canonical {
177                max_universe: ty::UniverseIndex::ROOT,
178                variables: List::empty(),
179                value: key,
180            };
181        }
182
183        assert_eq!(state.var_values.len(), 0);
184        assert_eq!(state.universe_map.len(), 1);
185        debug_assert_eq!(&*state.universe_map, &[ty::UniverseIndex::ROOT]);
186
187        match self.map.borrow().entry(key) {
188            Entry::Occupied(e) => {
189                let (canonical, var_values) = e.get();
190                if cfg!(debug_assertions) {
191                    let mut state = state.clone();
192                    let rerun_canonical = canonicalize_op(tcx, key, &mut state);
193                    assert_eq!(rerun_canonical, *canonical);
194                    let OriginalQueryValues { var_values: rerun_var_values, universe_map } = state;
195                    assert_eq!(universe_map.len(), 1);
196                    assert_eq!(**var_values, *rerun_var_values);
197                }
198                state.var_values.extend_from_slice(var_values);
199                *canonical
200            }
201            Entry::Vacant(e) => {
202                let canonical = canonicalize_op(tcx, key, state);
203                let OriginalQueryValues { var_values, universe_map } = state;
204                assert_eq!(universe_map.len(), 1);
205                e.insert((canonical, tcx.arena.alloc_slice(var_values)));
206                canonical
207            }
208        }
209    }
210}