chalk_solve/infer/
ucanonicalize.rs

1use crate::debug_span;
2use chalk_derive::FallibleTypeFolder;
3use chalk_ir::fold::{TypeFoldable, TypeFolder};
4use chalk_ir::interner::{HasInterner, Interner};
5use chalk_ir::visit::{TypeVisitable, TypeVisitor};
6use chalk_ir::*;
7use std::ops::ControlFlow;
8
9use super::InferenceTable;
10
11impl<I: Interner> InferenceTable<I> {
12    pub fn u_canonicalize<T>(interner: I, value0: &Canonical<T>) -> UCanonicalized<T>
13    where
14        T: Clone + HasInterner<Interner = I> + TypeFoldable<I> + TypeVisitable<I>,
15        T: HasInterner<Interner = I>,
16    {
17        debug_span!("u_canonicalize", "{:#?}", value0);
18
19        // First, find all the universes that appear in `value`.
20        let mut universes = UniverseMap::new();
21
22        for universe in value0.binders.iter(interner) {
23            universes.add(*universe.skip_kind());
24        }
25
26        value0.value.visit_with(
27            &mut UCollector {
28                universes: &mut universes,
29                interner,
30            },
31            DebruijnIndex::INNERMOST,
32        );
33
34        // Now re-map the universes found in value. We have to do this
35        // in a second pass because it is only then that we know the
36        // full set of universes found in the original value.
37        let value1 = value0
38            .value
39            .clone()
40            .try_fold_with(
41                &mut UMapToCanonical {
42                    universes: &universes,
43                    interner,
44                },
45                DebruijnIndex::INNERMOST,
46            )
47            .unwrap();
48        let binders = CanonicalVarKinds::from_iter(
49            interner,
50            value0
51                .binders
52                .iter(interner)
53                .map(|pk| pk.map_ref(|&ui| universes.map_universe_to_canonical(ui).unwrap())),
54        );
55
56        UCanonicalized {
57            quantified: UCanonical {
58                universes: universes.num_canonical_universes(),
59                canonical: Canonical {
60                    value: value1,
61                    binders,
62                },
63            },
64            universes,
65        }
66    }
67}
68
69#[derive(Debug)]
70pub struct UCanonicalized<T: HasInterner> {
71    /// The canonicalized result.
72    pub quantified: UCanonical<T>,
73
74    /// A map between the universes in `quantified` and the original universes
75    pub universes: UniverseMap,
76}
77
78pub trait UniverseMapExt {
79    fn add(&mut self, universe: UniverseIndex);
80    fn map_universe_to_canonical(&self, universe: UniverseIndex) -> Option<UniverseIndex>;
81    fn map_universe_from_canonical(&self, universe: UniverseIndex) -> UniverseIndex;
82    fn map_from_canonical<T, I>(&self, interner: I, canonical_value: &Canonical<T>) -> Canonical<T>
83    where
84        T: Clone + TypeFoldable<I> + HasInterner<Interner = I>,
85        T: HasInterner<Interner = I>,
86        I: Interner;
87}
88impl UniverseMapExt for UniverseMap {
89    fn add(&mut self, universe: UniverseIndex) {
90        if let Err(i) = self.universes.binary_search(&universe) {
91            self.universes.insert(i, universe);
92        }
93    }
94
95    /// Given a universe U that appeared in our original value, return
96    /// the universe to use in the u-canonical value. This is done by
97    /// looking for the index I of U in `self.universes`. We will
98    /// return the universe with "counter" I. This effectively
99    /// "compresses" the range of universes to things from
100    /// `0..self.universes.len()`. If the universe is not present in the map,
101    /// we return `None`.
102    fn map_universe_to_canonical(&self, universe: UniverseIndex) -> Option<UniverseIndex> {
103        self.universes
104            .binary_search(&universe)
105            .ok()
106            .map(|index| UniverseIndex { counter: index })
107    }
108
109    /// Given a "canonical universe" -- one found in the
110    /// `u_canonicalize` result -- returns the original universe that
111    /// it corresponded to.
112    fn map_universe_from_canonical(&self, universe: UniverseIndex) -> UniverseIndex {
113        if universe.counter < self.universes.len() {
114            self.universes[universe.counter]
115        } else {
116            // If this universe is out of bounds, we assume an
117            // implicit `forall` binder, effectively, and map to a
118            // "big enough" universe in the original space. See
119            // comments on `map_from_canonical` for a detailed
120            // explanation.
121            let difference = universe.counter - self.universes.len();
122            let max_counter = self.universes.last().unwrap().counter;
123            let new_counter = max_counter + difference + 1;
124            UniverseIndex {
125                counter: new_counter,
126            }
127        }
128    }
129
130    /// Returns a mapped version of `value` where the universes have
131    /// been translated from canonical universes into the original
132    /// universes.
133    ///
134    /// In some cases, `value` may contain fresh universes that are
135    /// not described in the original map. This occurs when we return
136    /// region constraints -- for example, if we were to process a
137    /// constraint like `for<'a> 'a == 'b`, where `'b` is an inference
138    /// variable, that would generate a region constraint that `!2 ==
139    /// ?0`. (This constraint is typically not, as it happens,
140    /// satisfiable, but it may be, depending on the bounds on `!2`.)
141    /// In effect, there is a "for all" binder around the constraint,
142    /// but it is not represented explicitly -- only implicitly, by
143    /// the presence of a U2 variable.
144    ///
145    /// If we encounter universes like this, which are "out of bounds"
146    /// from our original set of universes, we map them to a distinct
147    /// universe in the original space that is greater than all the
148    /// other universes in the map. That is, if we encounter a
149    /// canonical universe `Ux` where our canonical vector is (say)
150    /// `[U0, U3]`, we would compute the difference `d = x - 2` and
151    /// then return the universe `3 + d + 1`.
152    ///
153    /// The important thing is that we preserve (a) the relative order
154    /// of universes, since that determines visibility, and (b) that
155    /// the universe we produce does not correspond to any of the
156    /// other original universes.
157    fn map_from_canonical<T, I>(&self, interner: I, canonical_value: &Canonical<T>) -> Canonical<T>
158    where
159        T: Clone + TypeFoldable<I> + HasInterner<Interner = I>,
160        T: HasInterner<Interner = I>,
161        I: Interner,
162    {
163        debug_span!("map_from_canonical", ?canonical_value, universes = ?self.universes);
164
165        let binders = canonical_value
166            .binders
167            .iter(interner)
168            .map(|cvk| cvk.map_ref(|&universe| self.map_universe_from_canonical(universe)));
169
170        let value = canonical_value
171            .value
172            .clone()
173            .try_fold_with(
174                &mut UMapFromCanonical {
175                    interner,
176                    universes: self,
177                },
178                DebruijnIndex::INNERMOST,
179            )
180            .unwrap();
181
182        Canonical {
183            binders: CanonicalVarKinds::from_iter(interner, binders),
184            value,
185        }
186    }
187}
188
189/// The `UCollector` is a "no-op" in terms of the value, but along the
190/// way it collects all universes that were found into a vector.
191struct UCollector<'q, I> {
192    universes: &'q mut UniverseMap,
193    interner: I,
194}
195
196impl<I: Interner> TypeVisitor<I> for UCollector<'_, I> {
197    type BreakTy = ();
198
199    fn as_dyn(&mut self) -> &mut dyn TypeVisitor<I, BreakTy = Self::BreakTy> {
200        self
201    }
202
203    fn visit_free_placeholder(
204        &mut self,
205        universe: PlaceholderIndex,
206        _outer_binder: DebruijnIndex,
207    ) -> ControlFlow<()> {
208        self.universes.add(universe.ui);
209        ControlFlow::Continue(())
210    }
211
212    fn forbid_inference_vars(&self) -> bool {
213        true
214    }
215
216    fn interner(&self) -> I {
217        self.interner
218    }
219}
220
221#[derive(FallibleTypeFolder)]
222struct UMapToCanonical<'q, I: Interner> {
223    interner: I,
224    universes: &'q UniverseMap,
225}
226
227impl<'i, I: Interner> TypeFolder<I> for UMapToCanonical<'i, I> {
228    fn as_dyn(&mut self) -> &mut dyn TypeFolder<I> {
229        self
230    }
231
232    fn forbid_inference_vars(&self) -> bool {
233        true
234    }
235
236    fn fold_free_placeholder_ty(
237        &mut self,
238        universe0: PlaceholderIndex,
239        _outer_binder: DebruijnIndex,
240    ) -> Ty<I> {
241        let ui = self
242            .universes
243            .map_universe_to_canonical(universe0.ui)
244            .expect("Expected UCollector to encounter this universe");
245        PlaceholderIndex {
246            ui,
247            idx: universe0.idx,
248        }
249        .to_ty(TypeFolder::interner(self))
250    }
251
252    fn fold_free_placeholder_lifetime(
253        &mut self,
254        universe0: PlaceholderIndex,
255        _outer_binder: DebruijnIndex,
256    ) -> Lifetime<I> {
257        let universe = self
258            .universes
259            .map_universe_to_canonical(universe0.ui)
260            .expect("Expected UCollector to encounter this universe");
261
262        PlaceholderIndex {
263            ui: universe,
264            idx: universe0.idx,
265        }
266        .to_lifetime(TypeFolder::interner(self))
267    }
268
269    fn fold_free_placeholder_const(
270        &mut self,
271        ty: Ty<I>,
272        universe0: PlaceholderIndex,
273        _outer_binder: DebruijnIndex,
274    ) -> Const<I> {
275        let universe = self
276            .universes
277            .map_universe_to_canonical(universe0.ui)
278            .expect("Expected UCollector to encounter this universe");
279
280        PlaceholderIndex {
281            ui: universe,
282            idx: universe0.idx,
283        }
284        .to_const(TypeFolder::interner(self), ty)
285    }
286
287    fn interner(&self) -> I {
288        self.interner
289    }
290}
291
292#[derive(FallibleTypeFolder)]
293struct UMapFromCanonical<'q, I: Interner> {
294    interner: I,
295    universes: &'q UniverseMap,
296}
297
298impl<'i, I: Interner> TypeFolder<I> for UMapFromCanonical<'i, I> {
299    fn as_dyn(&mut self) -> &mut dyn TypeFolder<I> {
300        self
301    }
302
303    fn fold_free_placeholder_ty(
304        &mut self,
305        universe0: PlaceholderIndex,
306        _outer_binder: DebruijnIndex,
307    ) -> Ty<I> {
308        let ui = self.universes.map_universe_from_canonical(universe0.ui);
309        PlaceholderIndex {
310            ui,
311            idx: universe0.idx,
312        }
313        .to_ty(TypeFolder::interner(self))
314    }
315
316    fn fold_free_placeholder_lifetime(
317        &mut self,
318        universe0: PlaceholderIndex,
319        _outer_binder: DebruijnIndex,
320    ) -> Lifetime<I> {
321        let universe = self.universes.map_universe_from_canonical(universe0.ui);
322        PlaceholderIndex {
323            ui: universe,
324            idx: universe0.idx,
325        }
326        .to_lifetime(TypeFolder::interner(self))
327    }
328
329    fn forbid_inference_vars(&self) -> bool {
330        true
331    }
332
333    fn interner(&self) -> I {
334        self.interner
335    }
336}