1use std::cmp::Ordering;
2
3use rustc_type_ir::data_structures::HashMap;
4use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
5use rustc_type_ir::inherent::*;
6use rustc_type_ir::solve::{Goal, QueryInput};
7use rustc_type_ir::visit::TypeVisitableExt;
8use rustc_type_ir::{
9 self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike,
10 Interner,
11};
12
13use crate::delegate::SolverDelegate;
14
15#[derive(Debug, Clone, Copy)]
21enum CanonicalizeMode {
22 Input { keep_static: bool },
26 Response {
34 max_input_universe: ty::UniverseIndex,
44 },
45}
46
47pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
48 delegate: &'a D,
49
50 canonicalize_mode: CanonicalizeMode,
52
53 variables: &'a mut Vec<I::GenericArg>,
55 primitive_var_infos: Vec<CanonicalVarInfo<I>>,
56 variable_lookup_table: HashMap<I::GenericArg, usize>,
57 binder_index: ty::DebruijnIndex,
58
59 cache: HashMap<(ty::DebruijnIndex, I::Ty), I::Ty>,
63}
64
65impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
66 pub fn canonicalize_response<T: TypeFoldable<I>>(
67 delegate: &'a D,
68 max_input_universe: ty::UniverseIndex,
69 variables: &'a mut Vec<I::GenericArg>,
70 value: T,
71 ) -> ty::Canonical<I, T> {
72 let mut canonicalizer = Canonicalizer {
73 delegate,
74 canonicalize_mode: CanonicalizeMode::Response { max_input_universe },
75
76 variables,
77 variable_lookup_table: Default::default(),
78 primitive_var_infos: Vec::new(),
79 binder_index: ty::INNERMOST,
80
81 cache: Default::default(),
82 };
83
84 let value = value.fold_with(&mut canonicalizer);
85 assert!(!value.has_infer(), "unexpected infer in {value:?}");
86 assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
87 let (max_universe, variables) = canonicalizer.finalize();
88 Canonical { max_universe, variables, value }
89 }
90
91 pub fn canonicalize_input<P: TypeFoldable<I>>(
100 delegate: &'a D,
101 variables: &'a mut Vec<I::GenericArg>,
102 input: QueryInput<I, P>,
103 ) -> ty::Canonical<I, QueryInput<I, P>> {
104 let mut env_canonicalizer = Canonicalizer {
106 delegate,
107 canonicalize_mode: CanonicalizeMode::Input { keep_static: true },
108
109 variables,
110 variable_lookup_table: Default::default(),
111 primitive_var_infos: Vec::new(),
112 binder_index: ty::INNERMOST,
113
114 cache: Default::default(),
115 };
116 let param_env = input.goal.param_env.fold_with(&mut env_canonicalizer);
117 debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
118 let mut rest_canonicalizer = Canonicalizer {
121 delegate,
122 canonicalize_mode: CanonicalizeMode::Input { keep_static: false },
123
124 variables: env_canonicalizer.variables,
125 variable_lookup_table: env_canonicalizer.variable_lookup_table,
128 primitive_var_infos: env_canonicalizer.primitive_var_infos,
129 binder_index: ty::INNERMOST,
130
131 cache: Default::default(),
137 };
138
139 let predicate = input.goal.predicate.fold_with(&mut rest_canonicalizer);
140 let goal = Goal { param_env, predicate };
141 let predefined_opaques_in_body =
142 input.predefined_opaques_in_body.fold_with(&mut rest_canonicalizer);
143 let value = QueryInput { goal, predefined_opaques_in_body };
144
145 assert!(!value.has_infer(), "unexpected infer in {value:?}");
146 assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
147 let (max_universe, variables) = rest_canonicalizer.finalize();
148 Canonical { max_universe, variables, value }
149 }
150
151 fn get_or_insert_bound_var(
152 &mut self,
153 arg: impl Into<I::GenericArg>,
154 canonical_var_info: CanonicalVarInfo<I>,
155 ) -> ty::BoundVar {
156 let arg = arg.into();
159 let idx = if self.variables.len() > 16 {
160 if self.variable_lookup_table.is_empty() {
161 self.variable_lookup_table.extend(self.variables.iter().copied().zip(0..));
162 }
163
164 *self.variable_lookup_table.entry(arg).or_insert_with(|| {
165 let var = self.variables.len();
166 self.variables.push(arg);
167 self.primitive_var_infos.push(canonical_var_info);
168 var
169 })
170 } else {
171 self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| {
172 let var = self.variables.len();
173 self.variables.push(arg);
174 self.primitive_var_infos.push(canonical_var_info);
175 var
176 })
177 };
178
179 ty::BoundVar::from(idx)
180 }
181
182 fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVars) {
183 let mut var_infos = self.primitive_var_infos;
184 match self.canonicalize_mode {
187 CanonicalizeMode::Input { .. } => {}
192 CanonicalizeMode::Response { max_input_universe } => {
197 for var in var_infos.iter_mut() {
198 let uv = var.universe();
199 let new_uv = ty::UniverseIndex::from(
200 uv.index().saturating_sub(max_input_universe.index()),
201 );
202 *var = var.with_updated_universe(new_uv);
203 }
204 let max_universe = var_infos
205 .iter()
206 .map(|info| info.universe())
207 .max()
208 .unwrap_or(ty::UniverseIndex::ROOT);
209
210 let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos);
211 return (max_universe, var_infos);
212 }
213 }
214
215 let mut curr_compressed_uv = ty::UniverseIndex::ROOT;
234 let mut existential_in_new_uv = None;
235 let mut next_orig_uv = Some(ty::UniverseIndex::ROOT);
236 while let Some(orig_uv) = next_orig_uv.take() {
237 let mut update_uv = |var: &mut CanonicalVarInfo<I>, orig_uv, is_existential| {
238 let uv = var.universe();
239 match uv.cmp(&orig_uv) {
240 Ordering::Less => (), Ordering::Equal => {
242 if is_existential {
243 if existential_in_new_uv.is_some_and(|uv| uv < orig_uv) {
244 curr_compressed_uv = curr_compressed_uv.next_universe();
250 }
251
252 existential_in_new_uv = Some(orig_uv);
257 } else if existential_in_new_uv.is_some() {
258 curr_compressed_uv = curr_compressed_uv.next_universe();
265 existential_in_new_uv = None;
266 }
267
268 *var = var.with_updated_universe(curr_compressed_uv);
269 }
270 Ordering::Greater => {
271 if next_orig_uv.is_none_or(|curr_next_uv| uv.cannot_name(curr_next_uv)) {
277 next_orig_uv = Some(uv);
278 }
279 }
280 }
281 };
282
283 for is_existential in [false, true] {
289 for var in var_infos.iter_mut() {
290 if !var.is_region() {
293 if is_existential == var.is_existential() {
294 update_uv(var, orig_uv, is_existential)
295 }
296 }
297 }
298 }
299 }
300
301 let mut first_region = true;
303 for var in var_infos.iter_mut() {
304 if var.is_region() {
305 if first_region {
306 first_region = false;
307 curr_compressed_uv = curr_compressed_uv.next_universe();
308 }
309 assert!(var.is_existential());
310 *var = var.with_updated_universe(curr_compressed_uv);
311 }
312 }
313
314 let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos);
315 (curr_compressed_uv, var_infos)
316 }
317
318 fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty {
319 let kind = match t.kind() {
320 ty::Infer(i) => match i {
321 ty::TyVar(vid) => {
322 assert_eq!(
323 self.delegate.opportunistic_resolve_ty_var(vid),
324 t,
325 "ty vid should have been resolved fully before canonicalization"
326 );
327
328 CanonicalVarKind::Ty(CanonicalTyVarKind::General(
329 self.delegate
330 .universe_of_ty(vid)
331 .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")),
332 ))
333 }
334 ty::IntVar(vid) => {
335 assert_eq!(
336 self.delegate.opportunistic_resolve_int_var(vid),
337 t,
338 "ty vid should have been resolved fully before canonicalization"
339 );
340 CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
341 }
342 ty::FloatVar(vid) => {
343 assert_eq!(
344 self.delegate.opportunistic_resolve_float_var(vid),
345 t,
346 "ty vid should have been resolved fully before canonicalization"
347 );
348 CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
349 }
350 ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {
351 panic!("fresh vars not expected in canonicalization")
352 }
353 },
354 ty::Placeholder(placeholder) => match self.canonicalize_mode {
355 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
356 PlaceholderLike::new(placeholder.universe(), self.variables.len().into()),
357 ),
358 CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
359 },
360 ty::Param(_) => match self.canonicalize_mode {
361 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
362 PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()),
363 ),
364 CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"),
365 },
366 ty::Bool
367 | ty::Char
368 | ty::Int(_)
369 | ty::Uint(_)
370 | ty::Float(_)
371 | ty::Adt(_, _)
372 | ty::Foreign(_)
373 | ty::Str
374 | ty::Array(_, _)
375 | ty::Slice(_)
376 | ty::RawPtr(_, _)
377 | ty::Ref(_, _, _)
378 | ty::Pat(_, _)
379 | ty::FnDef(_, _)
380 | ty::FnPtr(..)
381 | ty::UnsafeBinder(_)
382 | ty::Dynamic(_, _, _)
383 | ty::Closure(..)
384 | ty::CoroutineClosure(..)
385 | ty::Coroutine(_, _)
386 | ty::CoroutineWitness(..)
387 | ty::Never
388 | ty::Tuple(_)
389 | ty::Alias(_, _)
390 | ty::Bound(_, _)
391 | ty::Error(_) => {
392 return t.super_fold_with(self);
393 }
394 };
395
396 let var = self.get_or_insert_bound_var(t, CanonicalVarInfo { kind });
397
398 Ty::new_anon_bound(self.cx(), self.binder_index, var)
399 }
400}
401
402impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicalizer<'_, D, I> {
403 fn cx(&self) -> I {
404 self.delegate.cx()
405 }
406
407 fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
408 where
409 T: TypeFoldable<I>,
410 {
411 self.binder_index.shift_in(1);
412 let t = t.super_fold_with(self);
413 self.binder_index.shift_out(1);
414 t
415 }
416
417 fn fold_region(&mut self, r: I::Region) -> I::Region {
418 let kind = match r.kind() {
419 ty::ReBound(..) => return r,
420
421 ty::ReStatic => match self.canonicalize_mode {
424 CanonicalizeMode::Input { keep_static: false } => {
425 CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
426 }
427 CanonicalizeMode::Input { keep_static: true }
428 | CanonicalizeMode::Response { .. } => return r,
429 },
430
431 ty::ReErased | ty::ReError(_) => match self.canonicalize_mode {
439 CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
440 CanonicalizeMode::Response { .. } => return r,
441 },
442
443 ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode {
444 CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
445 CanonicalizeMode::Response { .. } => {
446 panic!("unexpected region in response: {r:?}")
447 }
448 },
449
450 ty::RePlaceholder(placeholder) => match self.canonicalize_mode {
451 CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
453 CanonicalizeMode::Response { max_input_universe } => {
454 if max_input_universe.can_name(placeholder.universe()) {
457 panic!("new placeholder in universe {max_input_universe:?}: {r:?}");
458 }
459 CanonicalVarKind::PlaceholderRegion(placeholder)
460 }
461 },
462
463 ty::ReVar(vid) => {
464 assert_eq!(
465 self.delegate.opportunistic_resolve_lt_var(vid),
466 r,
467 "region vid should have been resolved fully before canonicalization"
468 );
469 match self.canonicalize_mode {
470 CanonicalizeMode::Input { keep_static: _ } => {
471 CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
472 }
473 CanonicalizeMode::Response { .. } => {
474 CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
475 }
476 }
477 }
478 };
479
480 let var = self.get_or_insert_bound_var(r, CanonicalVarInfo { kind });
481
482 Region::new_anon_bound(self.cx(), self.binder_index, var)
483 }
484
485 fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
486 if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
487 ty
488 } else {
489 let res = self.cached_fold_ty(t);
490 assert!(self.cache.insert((self.binder_index, t), res).is_none());
491 res
492 }
493 }
494
495 fn fold_const(&mut self, c: I::Const) -> I::Const {
496 let kind = match c.kind() {
497 ty::ConstKind::Infer(i) => match i {
498 ty::InferConst::Var(vid) => {
499 assert_eq!(
500 self.delegate.opportunistic_resolve_ct_var(vid),
501 c,
502 "const vid should have been resolved fully before canonicalization"
503 );
504 CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())
505 }
506 ty::InferConst::Fresh(_) => todo!(),
507 },
508 ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
509 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
510 PlaceholderLike::new(placeholder.universe(), self.variables.len().into()),
511 ),
512 CanonicalizeMode::Response { .. } => {
513 CanonicalVarKind::PlaceholderConst(placeholder)
514 }
515 },
516 ty::ConstKind::Param(_) => match self.canonicalize_mode {
517 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
518 PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()),
519 ),
520 CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"),
521 },
522 ty::ConstKind::Bound(_, _)
524 | ty::ConstKind::Unevaluated(_)
525 | ty::ConstKind::Value(_)
526 | ty::ConstKind::Error(_)
527 | ty::ConstKind::Expr(_) => return c.super_fold_with(self),
528 };
529
530 let var = self.get_or_insert_bound_var(c, CanonicalVarInfo { kind });
531
532 Const::new_anon_bound(self.cx(), self.binder_index, var)
533 }
534}