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 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 pub quantified: Canonical<T>,
62
63 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 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}