Skip to main content

rustc_infer/infer/canonical/
instantiate.rs

1//! This module contains code to instantiate new values into a
2//! `Canonical<'tcx, T>`.
3//!
4//! For an overview of what canonicalization is and how it fits into
5//! rustc, check out the [chapter in the rustc dev guide][c].
6//!
7//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
8
9use rustc_macros::extension;
10use rustc_middle::ty::{
11    self, DelayedMap, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable,
12    TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
13};
14
15use crate::infer::canonical::{Canonical, CanonicalVarValues};
16
17/// FIXME(-Znext-solver): This or public because it is shared with the
18/// new trait solver implementation. We should deduplicate canonicalization.
19impl<'tcx, V> CanonicalExt<'tcx, V> for Canonical<'tcx, V> {
    #[doc = " Instantiate the wrapped value, replacing each canonical value"]
    #[doc = " with the value given in `var_values`."]
    fn instantiate(&self, tcx: TyCtxt<'tcx>,
        var_values: &CanonicalVarValues<'tcx>) -> V where
        V: TypeFoldable<TyCtxt<'tcx>> {
        self.instantiate_projected(tcx, var_values, |value| value.clone())
    }
    #[doc = " Allows one to apply a instantiation to some subset of"]
    #[doc = " `self.value`. Invoke `projection_fn` with `self.value` to get"]
    #[doc = " a value V that is expressed in terms of the same canonical"]
    #[doc = " variables bound in `self` (usually this extracts from subset"]
    #[doc = " of `self`). Apply the instantiation `var_values` to this value"]
    #[doc = " V, replacing each of the canonical variables."]
    fn instantiate_projected<T>(&self, tcx: TyCtxt<'tcx>,
        var_values: &CanonicalVarValues<'tcx>,
        projection_fn: impl FnOnce(&V) -> T) -> T where
        T: TypeFoldable<TyCtxt<'tcx>> {
        match (&self.var_kinds.len(), &var_values.len()) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        };
        let value = projection_fn(&self.value);
        instantiate_value(tcx, var_values, value)
    }
}#[extension(pub trait CanonicalExt<'tcx, V>)]
20impl<'tcx, V> Canonical<'tcx, V> {
21    /// Instantiate the wrapped value, replacing each canonical value
22    /// with the value given in `var_values`.
23    fn instantiate(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
24    where
25        V: TypeFoldable<TyCtxt<'tcx>>,
26    {
27        self.instantiate_projected(tcx, var_values, |value| value.clone())
28    }
29
30    /// Allows one to apply a instantiation to some subset of
31    /// `self.value`. Invoke `projection_fn` with `self.value` to get
32    /// a value V that is expressed in terms of the same canonical
33    /// variables bound in `self` (usually this extracts from subset
34    /// of `self`). Apply the instantiation `var_values` to this value
35    /// V, replacing each of the canonical variables.
36    fn instantiate_projected<T>(
37        &self,
38        tcx: TyCtxt<'tcx>,
39        var_values: &CanonicalVarValues<'tcx>,
40        projection_fn: impl FnOnce(&V) -> T,
41    ) -> T
42    where
43        T: TypeFoldable<TyCtxt<'tcx>>,
44    {
45        assert_eq!(self.var_kinds.len(), var_values.len());
46        let value = projection_fn(&self.value);
47        instantiate_value(tcx, var_values, value)
48    }
49}
50
51/// Instantiate the values from `var_values` into `value`. `var_values`
52/// must be values for the set of canonical variables that appear in
53/// `value`.
54pub(super) fn instantiate_value<'tcx, T>(
55    tcx: TyCtxt<'tcx>,
56    var_values: &CanonicalVarValues<'tcx>,
57    value: T,
58) -> T
59where
60    T: TypeFoldable<TyCtxt<'tcx>>,
61{
62    if var_values.var_values.is_empty() {
63        return value;
64    }
65
66    value.fold_with(&mut CanonicalInstantiator {
67        tcx,
68        var_values: var_values.var_values,
69        cache: Default::default(),
70    })
71}
72
73/// Replaces the bound vars in a canonical binder with var values.
74struct CanonicalInstantiator<'tcx> {
75    tcx: TyCtxt<'tcx>,
76
77    // The values that the bound vars are are being instantiated with.
78    var_values: ty::GenericArgsRef<'tcx>,
79
80    // Because we use `ty::BoundVarIndexKind::Canonical`, we can cache
81    // based only on the entire ty, not worrying about a `DebruijnIndex`
82    cache: DelayedMap<Ty<'tcx>, Ty<'tcx>>,
83}
84
85impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
86    fn cx(&self) -> TyCtxt<'tcx> {
87        self.tcx
88    }
89
90    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
91        match *t.kind() {
92            ty::Bound(ty::BoundVarIndexKind::Canonical, bound_ty) => {
93                self.var_values[bound_ty.var.as_usize()].expect_ty()
94            }
95            _ => {
96                if !t.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
97                    t
98                } else if let Some(&t) = self.cache.get(&t) {
99                    t
100                } else {
101                    let res = t.super_fold_with(self);
102                    if !self.cache.insert(t, res) {
    ::core::panicking::panic("assertion failed: self.cache.insert(t, res)")
};assert!(self.cache.insert(t, res));
103                    res
104                }
105            }
106        }
107    }
108
109    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
110        match r.kind() {
111            ty::ReBound(ty::BoundVarIndexKind::Canonical, br) => {
112                self.var_values[br.var.as_usize()].expect_region()
113            }
114            _ => r,
115        }
116    }
117
118    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
119        match ct.kind() {
120            ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bound_const) => {
121                self.var_values[bound_const.var.as_usize()].expect_const()
122            }
123            _ => ct.super_fold_with(self),
124        }
125    }
126
127    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
128        if p.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { p.super_fold_with(self) } else { p }
129    }
130
131    fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
132        if !c.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
133            return c;
134        }
135
136        // Our cache key is `(clauses, var_values)`, but we also don't care about
137        // var values that aren't named in the clauses, since they can change without
138        // affecting the output. Since `ParamEnv`s are cached first, we compute the
139        // last var value that is mentioned in the clauses, and cut off the list so
140        // that we have more hits in the cache.
141
142        // We also cache the computation of "highest var named by clauses" since that
143        // is both expensive (depending on the size of the clauses) and a pure function.
144        let index = *self
145            .tcx
146            .highest_var_in_clauses_cache
147            .lock()
148            .entry(c)
149            .or_insert_with(|| highest_var_in_clauses(c));
150        let c_args = &self.var_values[..=index];
151
152        if let Some(c) = self.tcx.clauses_cache.lock().get(&(c, c_args)) {
153            c
154        } else {
155            let folded = c.super_fold_with(self);
156            self.tcx.clauses_cache.lock().insert((c, c_args), folded);
157            folded
158        }
159    }
160}
161
162fn highest_var_in_clauses<'tcx>(c: ty::Clauses<'tcx>) -> usize {
163    struct HighestVarInClauses {
164        max_var: usize,
165    }
166    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HighestVarInClauses {
167        fn visit_ty(&mut self, t: Ty<'tcx>) {
168            if let ty::Bound(ty::BoundVarIndexKind::Canonical, bound_ty) = *t.kind() {
169                self.max_var = self.max_var.max(bound_ty.var.as_usize());
170            } else if t.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
171                t.super_visit_with(self);
172            }
173        }
174        fn visit_region(&mut self, r: ty::Region<'tcx>) {
175            if let ty::ReBound(ty::BoundVarIndexKind::Canonical, bound_region) = r.kind() {
176                self.max_var = self.max_var.max(bound_region.var.as_usize());
177            }
178        }
179        fn visit_const(&mut self, ct: ty::Const<'tcx>) {
180            if let ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bound_const) = ct.kind() {
181                self.max_var = self.max_var.max(bound_const.var.as_usize());
182            } else if ct.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
183                ct.super_visit_with(self);
184            }
185        }
186    }
187    let mut visitor = HighestVarInClauses { max_var: 0 };
188    c.visit_with(&mut visitor);
189    visitor.max_var
190}