chalk_solve/infer/
canonicalize.rs

1use crate::debug_span;
2use chalk_derive::FallibleTypeFolder;
3use chalk_ir::fold::shift::Shift;
4use chalk_ir::fold::{TypeFoldable, TypeFolder};
5use chalk_ir::interner::{HasInterner, Interner};
6use chalk_ir::*;
7use std::cmp::max;
8use tracing::{debug, instrument};
9
10use super::{InferenceTable, ParameterEnaVariable};
11
12impl<I: Interner> InferenceTable<I> {
13    /// Given a value `value` with variables in it, replaces those variables
14    /// with their instantiated values; any variables not yet instantiated are
15    /// replaced with a small integer index 0..N in order of appearance. The
16    /// result is a canonicalized representation of `value`.
17    ///
18    /// Example:
19    ///
20    ///    ?22: Foo<?23>
21    ///
22    /// would be quantified to
23    ///
24    ///    Canonical { value: `?0: Foo<?1>`, binders: [ui(?22), ui(?23)] }
25    ///
26    /// where `ui(?22)` and `ui(?23)` are the universe indices of `?22` and
27    /// `?23` respectively.
28    ///
29    /// A substitution mapping from the free variables to their re-bound form is
30    /// also returned.
31    pub fn canonicalize<T>(&mut self, interner: I, value: T) -> Canonicalized<T>
32    where
33        T: TypeFoldable<I>,
34        T: HasInterner<Interner = I>,
35    {
36        debug_span!("canonicalize", "{:#?}", value);
37        let mut q = Canonicalizer {
38            table: self,
39            free_vars: Vec::new(),
40            max_universe: UniverseIndex::root(),
41            interner,
42        };
43        let value = value
44            .try_fold_with(&mut q, DebruijnIndex::INNERMOST)
45            .unwrap();
46        let free_vars = q.free_vars.clone();
47
48        Canonicalized {
49            quantified: Canonical {
50                value,
51                binders: q.into_binders(),
52            },
53            free_vars,
54        }
55    }
56}
57
58#[derive(Debug)]
59pub struct Canonicalized<T: HasInterner> {
60    /// The canonicalized result.
61    pub quantified: Canonical<T>,
62
63    /// The free existential variables, along with the universes they inhabit.
64    pub free_vars: Vec<ParameterEnaVariable<T::Interner>>,
65}
66
67#[derive(FallibleTypeFolder)]
68struct Canonicalizer<'q, I: Interner> {
69    table: &'q mut InferenceTable<I>,
70    free_vars: Vec<ParameterEnaVariable<I>>,
71    max_universe: UniverseIndex,
72    interner: I,
73}
74
75impl<'q, I: Interner> Canonicalizer<'q, I> {
76    fn into_binders(self) -> CanonicalVarKinds<I> {
77        let Canonicalizer {
78            table,
79            free_vars,
80            interner,
81            ..
82        } = self;
83        CanonicalVarKinds::from_iter(
84            interner,
85            free_vars
86                .into_iter()
87                .map(|p_v| p_v.map(|v| table.universe_of_unbound_var(v))),
88        )
89    }
90
91    fn add(&mut self, free_var: ParameterEnaVariable<I>) -> usize {
92        self.max_universe = max(
93            self.max_universe,
94            self.table.universe_of_unbound_var(*free_var.skip_kind()),
95        );
96
97        self.free_vars
98            .iter()
99            .position(|v| v.skip_kind() == free_var.skip_kind())
100            .unwrap_or_else(|| {
101                let next_index = self.free_vars.len();
102                self.free_vars.push(free_var);
103                next_index
104            })
105    }
106}
107
108impl<'i, I: Interner> TypeFolder<I> for Canonicalizer<'i, I> {
109    fn as_dyn(&mut self) -> &mut dyn TypeFolder<I> {
110        self
111    }
112
113    fn fold_free_placeholder_ty(
114        &mut self,
115        universe: PlaceholderIndex,
116        _outer_binder: DebruijnIndex,
117    ) -> Ty<I> {
118        let interner = self.interner;
119        self.max_universe = max(self.max_universe, universe.ui);
120        universe.to_ty(interner)
121    }
122
123    fn fold_free_placeholder_lifetime(
124        &mut self,
125        universe: PlaceholderIndex,
126        _outer_binder: DebruijnIndex,
127    ) -> Lifetime<I> {
128        let interner = self.interner;
129        self.max_universe = max(self.max_universe, universe.ui);
130        universe.to_lifetime(interner)
131    }
132
133    fn fold_free_placeholder_const(
134        &mut self,
135        ty: Ty<I>,
136        universe: PlaceholderIndex,
137        _outer_binder: DebruijnIndex,
138    ) -> Const<I> {
139        let interner = self.interner;
140        self.max_universe = max(self.max_universe, universe.ui);
141        universe.to_const(interner, ty)
142    }
143
144    fn forbid_free_vars(&self) -> bool {
145        true
146    }
147
148    #[instrument(level = "debug", skip(self))]
149    fn fold_inference_ty(
150        &mut self,
151        var: InferenceVar,
152        kind: TyVariableKind,
153        outer_binder: DebruijnIndex,
154    ) -> Ty<I> {
155        let interner = self.interner;
156        match self.table.probe_var(var) {
157            Some(ty) => {
158                let ty = ty.assert_ty_ref(interner);
159                debug!("bound to {:?}", ty);
160                ty.clone()
161                    .fold_with(self, DebruijnIndex::INNERMOST)
162                    .shifted_in_from(interner, outer_binder)
163            }
164            None => {
165                // If this variable is not yet bound, find its
166                // canonical index `root_var` in the union-find table,
167                // and then map `root_var` to a fresh index that is
168                // unique to this quantification.
169                let free_var =
170                    ParameterEnaVariable::new(VariableKind::Ty(kind), self.table.unify.find(var));
171
172                let bound_var = BoundVar::new(DebruijnIndex::INNERMOST, self.add(free_var));
173                debug!(position=?bound_var, "not yet unified");
174                TyKind::BoundVar(bound_var.shifted_in_from(outer_binder)).intern(interner)
175            }
176        }
177    }
178
179    #[instrument(level = "debug", skip(self))]
180    fn fold_inference_lifetime(
181        &mut self,
182        var: InferenceVar,
183        outer_binder: DebruijnIndex,
184    ) -> Lifetime<I> {
185        let interner = self.interner;
186        match self.table.probe_var(var) {
187            Some(l) => {
188                let l = l.assert_lifetime_ref(interner);
189                debug!("bound to {:?}", l);
190                l.clone()
191                    .fold_with(self, DebruijnIndex::INNERMOST)
192                    .shifted_in_from(interner, outer_binder)
193            }
194            None => {
195                let free_var =
196                    ParameterEnaVariable::new(VariableKind::Lifetime, self.table.unify.find(var));
197                let bound_var = BoundVar::new(DebruijnIndex::INNERMOST, self.add(free_var));
198                debug!(position=?bound_var, "not yet unified");
199                LifetimeData::BoundVar(bound_var.shifted_in_from(outer_binder)).intern(interner)
200            }
201        }
202    }
203
204    #[instrument(level = "debug", skip(self, ty))]
205    fn fold_inference_const(
206        &mut self,
207        ty: Ty<I>,
208        var: InferenceVar,
209        outer_binder: DebruijnIndex,
210    ) -> Const<I> {
211        let interner = self.interner;
212        match self.table.probe_var(var) {
213            Some(c) => {
214                let c = c.assert_const_ref(interner);
215                debug!("bound to {:?}", c);
216                c.clone()
217                    .fold_with(self, DebruijnIndex::INNERMOST)
218                    .shifted_in_from(interner, outer_binder)
219            }
220            None => {
221                let free_var = ParameterEnaVariable::new(
222                    VariableKind::Const(ty.clone()),
223                    self.table.unify.find(var),
224                );
225                let bound_var = BoundVar::new(DebruijnIndex::INNERMOST, self.add(free_var));
226                debug!(position = ?bound_var, "not yet unified");
227                bound_var
228                    .shifted_in_from(outer_binder)
229                    .to_const(interner, ty)
230            }
231        }
232    }
233
234    fn interner(&self) -> I {
235        self.interner
236    }
237}