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