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, CanonicalVarKind, Flags, InferCtxtLike, Interner,
8 TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
9};
10
11use crate::delegate::SolverDelegate;
12
13const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits(
15 TypeFlags::HAS_INFER.bits()
16 | TypeFlags::HAS_PLACEHOLDER.bits()
17 | TypeFlags::HAS_PARAM.bits()
18 | TypeFlags::HAS_FREE_REGIONS.bits()
19 | TypeFlags::HAS_RE_ERASED.bits(),
20)
21.unwrap();
22
23#[derive(Debug, Clone, Copy)]
29enum CanonicalizeMode {
30 Input { keep_static: bool },
34 Response {
42 max_input_universe: ty::UniverseIndex,
52 },
53}
54
55pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
56 delegate: &'a D,
57
58 canonicalize_mode: CanonicalizeMode,
60
61 variables: &'a mut Vec<I::GenericArg>,
63 var_kinds: Vec<CanonicalVarKind<I>>,
64 variable_lookup_table: HashMap<I::GenericArg, usize>,
65 binder_index: ty::DebruijnIndex,
66
67 cache: HashMap<(ty::DebruijnIndex, I::Ty), I::Ty>,
71}
72
73impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
74 pub fn canonicalize_response<T: TypeFoldable<I>>(
75 delegate: &'a D,
76 max_input_universe: ty::UniverseIndex,
77 variables: &'a mut Vec<I::GenericArg>,
78 value: T,
79 ) -> ty::Canonical<I, T> {
80 let mut canonicalizer = Canonicalizer {
81 delegate,
82 canonicalize_mode: CanonicalizeMode::Response { max_input_universe },
83
84 variables,
85 variable_lookup_table: Default::default(),
86 var_kinds: Vec::new(),
87 binder_index: ty::INNERMOST,
88
89 cache: Default::default(),
90 };
91
92 let value = if value.has_type_flags(NEEDS_CANONICAL) {
93 value.fold_with(&mut canonicalizer)
94 } else {
95 value
96 };
97 debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
98 debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
99 let (max_universe, variables) = canonicalizer.finalize();
100 Canonical { max_universe, variables, value }
101 }
102
103 pub fn canonicalize_input<P: TypeFoldable<I>>(
112 delegate: &'a D,
113 variables: &'a mut Vec<I::GenericArg>,
114 input: QueryInput<I, P>,
115 ) -> ty::Canonical<I, QueryInput<I, P>> {
116 let mut env_canonicalizer = Canonicalizer {
118 delegate,
119 canonicalize_mode: CanonicalizeMode::Input { keep_static: true },
120
121 variables,
122 variable_lookup_table: Default::default(),
123 var_kinds: Vec::new(),
124 binder_index: ty::INNERMOST,
125
126 cache: Default::default(),
127 };
128
129 let param_env = input.goal.param_env;
130 let param_env = if param_env.has_type_flags(NEEDS_CANONICAL) {
131 param_env.fold_with(&mut env_canonicalizer)
132 } else {
133 param_env
134 };
135
136 debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
137 let mut rest_canonicalizer = Canonicalizer {
140 delegate,
141 canonicalize_mode: CanonicalizeMode::Input { keep_static: false },
142
143 variables: env_canonicalizer.variables,
144 variable_lookup_table: env_canonicalizer.variable_lookup_table,
147 var_kinds: env_canonicalizer.var_kinds,
148 binder_index: ty::INNERMOST,
149
150 cache: Default::default(),
156 };
157
158 let predicate = input.goal.predicate;
159 let predicate = if predicate.has_type_flags(NEEDS_CANONICAL) {
160 predicate.fold_with(&mut rest_canonicalizer)
161 } else {
162 predicate
163 };
164 let goal = Goal { param_env, predicate };
165
166 let predefined_opaques_in_body = input.predefined_opaques_in_body;
167 let predefined_opaques_in_body =
168 if input.predefined_opaques_in_body.has_type_flags(NEEDS_CANONICAL) {
169 predefined_opaques_in_body.fold_with(&mut rest_canonicalizer)
170 } else {
171 predefined_opaques_in_body
172 };
173
174 let value = QueryInput { goal, predefined_opaques_in_body };
175
176 debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
177 debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
178 let (max_universe, variables) = rest_canonicalizer.finalize();
179 Canonical { max_universe, variables, value }
180 }
181
182 fn get_or_insert_bound_var(
183 &mut self,
184 arg: impl Into<I::GenericArg>,
185 kind: CanonicalVarKind<I>,
186 ) -> ty::BoundVar {
187 let arg = arg.into();
190 let idx = if self.variables.len() > 16 {
191 if self.variable_lookup_table.is_empty() {
192 self.variable_lookup_table.extend(self.variables.iter().copied().zip(0..));
193 }
194
195 *self.variable_lookup_table.entry(arg).or_insert_with(|| {
196 let var = self.variables.len();
197 self.variables.push(arg);
198 self.var_kinds.push(kind);
199 var
200 })
201 } else {
202 self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| {
203 let var = self.variables.len();
204 self.variables.push(arg);
205 self.var_kinds.push(kind);
206 var
207 })
208 };
209
210 ty::BoundVar::from(idx)
211 }
212
213 fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) {
214 let mut var_kinds = self.var_kinds;
215 match self.canonicalize_mode {
218 CanonicalizeMode::Input { .. } => {}
223 CanonicalizeMode::Response { max_input_universe } => {
228 for var in var_kinds.iter_mut() {
229 let uv = var.universe();
230 let new_uv = ty::UniverseIndex::from(
231 uv.index().saturating_sub(max_input_universe.index()),
232 );
233 *var = var.with_updated_universe(new_uv);
234 }
235 let max_universe = var_kinds
236 .iter()
237 .map(|kind| kind.universe())
238 .max()
239 .unwrap_or(ty::UniverseIndex::ROOT);
240
241 let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
242 return (max_universe, var_kinds);
243 }
244 }
245
246 let mut curr_compressed_uv = ty::UniverseIndex::ROOT;
265 let mut existential_in_new_uv = None;
266 let mut next_orig_uv = Some(ty::UniverseIndex::ROOT);
267 while let Some(orig_uv) = next_orig_uv.take() {
268 let mut update_uv = |var: &mut CanonicalVarKind<I>, orig_uv, is_existential| {
269 let uv = var.universe();
270 match uv.cmp(&orig_uv) {
271 Ordering::Less => (), Ordering::Equal => {
273 if is_existential {
274 if existential_in_new_uv.is_some_and(|uv| uv < orig_uv) {
275 curr_compressed_uv = curr_compressed_uv.next_universe();
281 }
282
283 existential_in_new_uv = Some(orig_uv);
288 } else if existential_in_new_uv.is_some() {
289 curr_compressed_uv = curr_compressed_uv.next_universe();
296 existential_in_new_uv = None;
297 }
298
299 *var = var.with_updated_universe(curr_compressed_uv);
300 }
301 Ordering::Greater => {
302 if next_orig_uv.is_none_or(|curr_next_uv| uv.cannot_name(curr_next_uv)) {
308 next_orig_uv = Some(uv);
309 }
310 }
311 }
312 };
313
314 for is_existential in [false, true] {
320 for var in var_kinds.iter_mut() {
321 if !var.is_region() {
324 if is_existential == var.is_existential() {
325 update_uv(var, orig_uv, is_existential)
326 }
327 }
328 }
329 }
330 }
331
332 let mut first_region = true;
334 for var in var_kinds.iter_mut() {
335 if var.is_region() {
336 if first_region {
337 first_region = false;
338 curr_compressed_uv = curr_compressed_uv.next_universe();
339 }
340 debug_assert!(var.is_existential());
341 *var = var.with_updated_universe(curr_compressed_uv);
342 }
343 }
344
345 let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
346 (curr_compressed_uv, var_kinds)
347 }
348
349 fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty {
350 let kind = match t.kind() {
351 ty::Infer(i) => match i {
352 ty::TyVar(vid) => {
353 debug_assert_eq!(
354 self.delegate.opportunistic_resolve_ty_var(vid),
355 t,
356 "ty vid should have been resolved fully before canonicalization"
357 );
358
359 CanonicalVarKind::Ty(CanonicalTyVarKind::General(
360 self.delegate
361 .universe_of_ty(vid)
362 .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")),
363 ))
364 }
365 ty::IntVar(vid) => {
366 debug_assert_eq!(
367 self.delegate.opportunistic_resolve_int_var(vid),
368 t,
369 "ty vid should have been resolved fully before canonicalization"
370 );
371 CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
372 }
373 ty::FloatVar(vid) => {
374 debug_assert_eq!(
375 self.delegate.opportunistic_resolve_float_var(vid),
376 t,
377 "ty vid should have been resolved fully before canonicalization"
378 );
379 CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
380 }
381 ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {
382 panic!("fresh vars not expected in canonicalization")
383 }
384 },
385 ty::Placeholder(placeholder) => match self.canonicalize_mode {
386 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
387 PlaceholderLike::new(placeholder.universe(), self.variables.len().into()),
388 ),
389 CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
390 },
391 ty::Param(_) => match self.canonicalize_mode {
392 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
393 PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()),
394 ),
395 CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"),
396 },
397 ty::Bool
398 | ty::Char
399 | ty::Int(_)
400 | ty::Uint(_)
401 | ty::Float(_)
402 | ty::Adt(_, _)
403 | ty::Foreign(_)
404 | ty::Str
405 | ty::Array(_, _)
406 | ty::Slice(_)
407 | ty::RawPtr(_, _)
408 | ty::Ref(_, _, _)
409 | ty::Pat(_, _)
410 | ty::FnDef(_, _)
411 | ty::FnPtr(..)
412 | ty::UnsafeBinder(_)
413 | ty::Dynamic(_, _, _)
414 | ty::Closure(..)
415 | ty::CoroutineClosure(..)
416 | ty::Coroutine(_, _)
417 | ty::CoroutineWitness(..)
418 | ty::Never
419 | ty::Tuple(_)
420 | ty::Alias(_, _)
421 | ty::Bound(_, _)
422 | ty::Error(_) => {
423 return if t.has_type_flags(NEEDS_CANONICAL) {
424 ensure_sufficient_stack(|| t.super_fold_with(self))
425 } else {
426 t
427 };
428 }
429 };
430
431 let var = self.get_or_insert_bound_var(t, kind);
432
433 Ty::new_anon_bound(self.cx(), self.binder_index, var)
434 }
435}
436
437impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicalizer<'_, D, I> {
438 fn cx(&self) -> I {
439 self.delegate.cx()
440 }
441
442 fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
443 where
444 T: TypeFoldable<I>,
445 {
446 self.binder_index.shift_in(1);
447 let t = t.super_fold_with(self);
448 self.binder_index.shift_out(1);
449 t
450 }
451
452 fn fold_region(&mut self, r: I::Region) -> I::Region {
453 let kind = match r.kind() {
454 ty::ReBound(..) => return r,
455
456 ty::ReStatic => match self.canonicalize_mode {
459 CanonicalizeMode::Input { keep_static: false } => {
460 CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
461 }
462 CanonicalizeMode::Input { keep_static: true }
463 | CanonicalizeMode::Response { .. } => return r,
464 },
465
466 ty::ReErased | ty::ReError(_) => match self.canonicalize_mode {
474 CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
475 CanonicalizeMode::Response { .. } => return r,
476 },
477
478 ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode {
479 CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
480 CanonicalizeMode::Response { .. } => {
481 panic!("unexpected region in response: {r:?}")
482 }
483 },
484
485 ty::RePlaceholder(placeholder) => match self.canonicalize_mode {
486 CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
488 CanonicalizeMode::Response { max_input_universe } => {
489 if max_input_universe.can_name(placeholder.universe()) {
492 panic!("new placeholder in universe {max_input_universe:?}: {r:?}");
493 }
494 CanonicalVarKind::PlaceholderRegion(placeholder)
495 }
496 },
497
498 ty::ReVar(vid) => {
499 debug_assert_eq!(
500 self.delegate.opportunistic_resolve_lt_var(vid),
501 r,
502 "region vid should have been resolved fully before canonicalization"
503 );
504 match self.canonicalize_mode {
505 CanonicalizeMode::Input { keep_static: _ } => {
506 CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
507 }
508 CanonicalizeMode::Response { .. } => {
509 CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
510 }
511 }
512 }
513 };
514
515 let var = self.get_or_insert_bound_var(r, kind);
516
517 Region::new_anon_bound(self.cx(), self.binder_index, var)
518 }
519
520 fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
521 if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
522 ty
523 } else {
524 let res = self.cached_fold_ty(t);
525 let old = self.cache.insert((self.binder_index, t), res);
526 assert_eq!(old, None);
527 res
528 }
529 }
530
531 fn fold_const(&mut self, c: I::Const) -> I::Const {
532 let kind = match c.kind() {
533 ty::ConstKind::Infer(i) => match i {
534 ty::InferConst::Var(vid) => {
535 debug_assert_eq!(
536 self.delegate.opportunistic_resolve_ct_var(vid),
537 c,
538 "const vid should have been resolved fully before canonicalization"
539 );
540 CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())
541 }
542 ty::InferConst::Fresh(_) => todo!(),
543 },
544 ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
545 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
546 PlaceholderLike::new(placeholder.universe(), self.variables.len().into()),
547 ),
548 CanonicalizeMode::Response { .. } => {
549 CanonicalVarKind::PlaceholderConst(placeholder)
550 }
551 },
552 ty::ConstKind::Param(_) => match self.canonicalize_mode {
553 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
554 PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()),
555 ),
556 CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"),
557 },
558 ty::ConstKind::Bound(_, _)
560 | ty::ConstKind::Unevaluated(_)
561 | ty::ConstKind::Value(_)
562 | ty::ConstKind::Error(_)
563 | ty::ConstKind::Expr(_) => {
564 return if c.has_type_flags(NEEDS_CANONICAL) { c.super_fold_with(self) } else { c };
565 }
566 };
567
568 let var = self.get_or_insert_bound_var(c, kind);
569
570 Const::new_anon_bound(self.cx(), self.binder_index, var)
571 }
572
573 fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
574 if p.flags().intersects(NEEDS_CANONICAL) { p.super_fold_with(self) } else { p }
575 }
576
577 fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
578 match self.canonicalize_mode {
579 CanonicalizeMode::Input { keep_static: true }
580 | CanonicalizeMode::Response { max_input_universe: _ } => {}
581 CanonicalizeMode::Input { keep_static: false } => {
582 panic!("erasing 'static in env")
583 }
584 }
585 if c.flags().intersects(NEEDS_CANONICAL) { c.super_fold_with(self) } else { c }
586 }
587}