1use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack};
2use rustc_type_ir::inherent::*;
3use rustc_type_ir::solve::{Goal, QueryInput};
4use rustc_type_ir::{
5 self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalVarKind, Flags, InferCtxtLike,
6 Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
7};
8
9use crate::delegate::SolverDelegate;
10
11const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits(
13 TypeFlags::HAS_INFER.bits()
14 | TypeFlags::HAS_PLACEHOLDER.bits()
15 | TypeFlags::HAS_PARAM.bits()
16 | TypeFlags::HAS_FREE_REGIONS.bits()
17 | TypeFlags::HAS_RE_ERASED.bits(),
18)
19.unwrap();
20
21#[derive(Debug, Clone, Copy)]
22enum CanonicalizeInputKind {
23 ParamEnv,
27 Predicate,
29}
30
31#[derive(Debug, Clone, Copy)]
37enum CanonicalizeMode {
38 Input(CanonicalizeInputKind),
39 Response {
47 max_input_universe: ty::UniverseIndex,
57 },
58}
59
60pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
61 delegate: &'a D,
62
63 canonicalize_mode: CanonicalizeMode,
65
66 variables: &'a mut Vec<I::GenericArg>,
68 var_kinds: Vec<CanonicalVarKind<I>>,
69 variable_lookup_table: HashMap<I::GenericArg, usize>,
70 sub_root_lookup_table: HashMap<ty::TyVid, usize>,
77 binder_index: ty::DebruijnIndex,
78
79 cache: HashMap<(ty::DebruijnIndex, I::Ty), I::Ty>,
83}
84
85impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
86 pub fn canonicalize_response<T: TypeFoldable<I>>(
87 delegate: &'a D,
88 max_input_universe: ty::UniverseIndex,
89 variables: &'a mut Vec<I::GenericArg>,
90 value: T,
91 ) -> ty::Canonical<I, T> {
92 let mut canonicalizer = Canonicalizer {
93 delegate,
94 canonicalize_mode: CanonicalizeMode::Response { max_input_universe },
95
96 variables,
97 variable_lookup_table: Default::default(),
98 sub_root_lookup_table: Default::default(),
99 var_kinds: Vec::new(),
100 binder_index: ty::INNERMOST,
101
102 cache: Default::default(),
103 };
104
105 let value = if value.has_type_flags(NEEDS_CANONICAL) {
106 value.fold_with(&mut canonicalizer)
107 } else {
108 value
109 };
110 debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
111 debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
112 let (max_universe, variables) = canonicalizer.finalize();
113 Canonical { max_universe, variables, value }
114 }
115
116 fn canonicalize_param_env(
117 delegate: &'a D,
118 variables: &'a mut Vec<I::GenericArg>,
119 param_env: I::ParamEnv,
120 ) -> (I::ParamEnv, HashMap<I::GenericArg, usize>, Vec<CanonicalVarKind<I>>) {
121 if !param_env.has_type_flags(NEEDS_CANONICAL) {
122 return (param_env, Default::default(), Vec::new());
123 }
124
125 if !param_env.has_non_region_infer() {
133 delegate.cx().canonical_param_env_cache_get_or_insert(
134 param_env,
135 || {
136 let mut variables = Vec::new();
137 let mut env_canonicalizer = Canonicalizer {
138 delegate,
139 canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
140
141 variables: &mut variables,
142 variable_lookup_table: Default::default(),
143 sub_root_lookup_table: Default::default(),
144 var_kinds: Vec::new(),
145 binder_index: ty::INNERMOST,
146
147 cache: Default::default(),
148 };
149 let param_env = param_env.fold_with(&mut env_canonicalizer);
150 debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
151 debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
152 CanonicalParamEnvCacheEntry {
153 param_env,
154 variable_lookup_table: env_canonicalizer.variable_lookup_table,
155 var_kinds: env_canonicalizer.var_kinds,
156 variables,
157 }
158 },
159 |&CanonicalParamEnvCacheEntry {
160 param_env,
161 variables: ref cache_variables,
162 ref variable_lookup_table,
163 ref var_kinds,
164 }| {
165 debug_assert!(variables.is_empty());
166 variables.extend(cache_variables.iter().copied());
167 (param_env, variable_lookup_table.clone(), var_kinds.clone())
168 },
169 )
170 } else {
171 let mut env_canonicalizer = Canonicalizer {
172 delegate,
173 canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
174
175 variables,
176 variable_lookup_table: Default::default(),
177 sub_root_lookup_table: Default::default(),
178 var_kinds: Vec::new(),
179 binder_index: ty::INNERMOST,
180
181 cache: Default::default(),
182 };
183 let param_env = param_env.fold_with(&mut env_canonicalizer);
184 debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
185 debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
186 (param_env, env_canonicalizer.variable_lookup_table, env_canonicalizer.var_kinds)
187 }
188 }
189
190 pub fn canonicalize_input<P: TypeFoldable<I>>(
199 delegate: &'a D,
200 variables: &'a mut Vec<I::GenericArg>,
201 input: QueryInput<I, P>,
202 ) -> ty::Canonical<I, QueryInput<I, P>> {
203 let (param_env, variable_lookup_table, var_kinds) =
205 Canonicalizer::canonicalize_param_env(delegate, variables, input.goal.param_env);
206 let mut rest_canonicalizer = Canonicalizer {
209 delegate,
210 canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate),
211
212 variables,
213 variable_lookup_table,
214 sub_root_lookup_table: Default::default(),
215 var_kinds,
216 binder_index: ty::INNERMOST,
217
218 cache: Default::default(),
224 };
225
226 let predicate = input.goal.predicate;
227 let predicate = if predicate.has_type_flags(NEEDS_CANONICAL) {
228 predicate.fold_with(&mut rest_canonicalizer)
229 } else {
230 predicate
231 };
232 let goal = Goal { param_env, predicate };
233
234 let predefined_opaques_in_body = input.predefined_opaques_in_body;
235 let predefined_opaques_in_body =
236 if input.predefined_opaques_in_body.has_type_flags(NEEDS_CANONICAL) {
237 predefined_opaques_in_body.fold_with(&mut rest_canonicalizer)
238 } else {
239 predefined_opaques_in_body
240 };
241
242 let value = QueryInput { goal, predefined_opaques_in_body };
243
244 debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
245 debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
246 let (max_universe, variables) = rest_canonicalizer.finalize();
247 Canonical { max_universe, variables, value }
248 }
249
250 fn get_or_insert_bound_var(
251 &mut self,
252 arg: impl Into<I::GenericArg>,
253 kind: CanonicalVarKind<I>,
254 ) -> ty::BoundVar {
255 let arg = arg.into();
258 let idx = if self.variables.len() > 16 {
259 if self.variable_lookup_table.is_empty() {
260 self.variable_lookup_table.extend(self.variables.iter().copied().zip(0..));
261 }
262
263 *self.variable_lookup_table.entry(arg).or_insert_with(|| {
264 let var = self.variables.len();
265 self.variables.push(arg);
266 self.var_kinds.push(kind);
267 var
268 })
269 } else {
270 self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| {
271 let var = self.variables.len();
272 self.variables.push(arg);
273 self.var_kinds.push(kind);
274 var
275 })
276 };
277
278 ty::BoundVar::from(idx)
279 }
280
281 fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar {
282 let root_vid = self.delegate.sub_unification_table_root_var(vid);
283 let idx =
284 *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len());
285 ty::BoundVar::from(idx)
286 }
287
288 fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) {
289 let mut var_kinds = self.var_kinds;
290 match self.canonicalize_mode {
293 CanonicalizeMode::Input { .. } => {
295 debug_assert!(
296 var_kinds.iter().all(|var| var.universe() == ty::UniverseIndex::ROOT),
297 "expected all vars to be canonicalized in root universe: {var_kinds:#?}"
298 );
299 let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
300 (ty::UniverseIndex::ROOT, var_kinds)
301 }
302 CanonicalizeMode::Response { max_input_universe } => {
307 for var in var_kinds.iter_mut() {
308 let uv = var.universe();
309 let new_uv = ty::UniverseIndex::from(
310 uv.index().saturating_sub(max_input_universe.index()),
311 );
312 *var = var.with_updated_universe(new_uv);
313 }
314 let max_universe = var_kinds
315 .iter()
316 .map(|kind| kind.universe())
317 .max()
318 .unwrap_or(ty::UniverseIndex::ROOT);
319 let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
320 (max_universe, var_kinds)
321 }
322 }
323 }
324
325 fn inner_fold_ty(&mut self, t: I::Ty) -> I::Ty {
326 let kind = match t.kind() {
327 ty::Infer(i) => match i {
328 ty::TyVar(vid) => {
329 debug_assert_eq!(
330 self.delegate.opportunistic_resolve_ty_var(vid),
331 t,
332 "ty vid should have been resolved fully before canonicalization"
333 );
334
335 let sub_root = self.get_or_insert_sub_root(vid);
336 let ui = match self.canonicalize_mode {
337 CanonicalizeMode::Input { .. } => ty::UniverseIndex::ROOT,
338 CanonicalizeMode::Response { .. } => self
339 .delegate
340 .universe_of_ty(vid)
341 .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")),
342 };
343 CanonicalVarKind::Ty { ui, sub_root }
344 }
345 ty::IntVar(vid) => {
346 debug_assert_eq!(
347 self.delegate.opportunistic_resolve_int_var(vid),
348 t,
349 "ty vid should have been resolved fully before canonicalization"
350 );
351 CanonicalVarKind::Int
352 }
353 ty::FloatVar(vid) => {
354 debug_assert_eq!(
355 self.delegate.opportunistic_resolve_float_var(vid),
356 t,
357 "ty vid should have been resolved fully before canonicalization"
358 );
359 CanonicalVarKind::Float
360 }
361 ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {
362 panic!("fresh vars not expected in canonicalization")
363 }
364 },
365 ty::Placeholder(placeholder) => match self.canonicalize_mode {
366 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
367 PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
368 ),
369 CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
370 },
371 ty::Param(_) => match self.canonicalize_mode {
372 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
373 PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
374 ),
375 CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"),
376 },
377 ty::Bool
378 | ty::Char
379 | ty::Int(_)
380 | ty::Uint(_)
381 | ty::Float(_)
382 | ty::Adt(_, _)
383 | ty::Foreign(_)
384 | ty::Str
385 | ty::Array(_, _)
386 | ty::Slice(_)
387 | ty::RawPtr(_, _)
388 | ty::Ref(_, _, _)
389 | ty::Pat(_, _)
390 | ty::FnDef(_, _)
391 | ty::FnPtr(..)
392 | ty::UnsafeBinder(_)
393 | ty::Dynamic(_, _, _)
394 | ty::Closure(..)
395 | ty::CoroutineClosure(..)
396 | ty::Coroutine(_, _)
397 | ty::CoroutineWitness(..)
398 | ty::Never
399 | ty::Tuple(_)
400 | ty::Alias(_, _)
401 | ty::Bound(_, _)
402 | ty::Error(_) => {
403 return if t.has_type_flags(NEEDS_CANONICAL) {
404 ensure_sufficient_stack(|| t.super_fold_with(self))
405 } else {
406 t
407 };
408 }
409 };
410
411 let var = self.get_or_insert_bound_var(t, kind);
412
413 Ty::new_anon_bound(self.cx(), self.binder_index, var)
414 }
415}
416
417impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicalizer<'_, D, I> {
418 fn cx(&self) -> I {
419 self.delegate.cx()
420 }
421
422 fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
423 where
424 T: TypeFoldable<I>,
425 {
426 self.binder_index.shift_in(1);
427 let t = t.super_fold_with(self);
428 self.binder_index.shift_out(1);
429 t
430 }
431
432 fn fold_region(&mut self, r: I::Region) -> I::Region {
433 let kind = match r.kind() {
434 ty::ReBound(..) => return r,
435
436 ty::ReStatic => match self.canonicalize_mode {
439 CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => {
440 CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
441 }
442 CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
443 | CanonicalizeMode::Response { .. } => return r,
444 },
445
446 ty::ReErased | ty::ReError(_) => match self.canonicalize_mode {
454 CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
455 CanonicalizeMode::Response { .. } => return r,
456 },
457
458 ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode {
459 CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
460 CanonicalizeMode::Response { .. } => {
461 panic!("unexpected region in response: {r:?}")
462 }
463 },
464
465 ty::RePlaceholder(placeholder) => match self.canonicalize_mode {
466 CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
468 CanonicalizeMode::Response { max_input_universe } => {
469 if max_input_universe.can_name(placeholder.universe()) {
472 panic!("new placeholder in universe {max_input_universe:?}: {r:?}");
473 }
474 CanonicalVarKind::PlaceholderRegion(placeholder)
475 }
476 },
477
478 ty::ReVar(vid) => {
479 debug_assert_eq!(
480 self.delegate.opportunistic_resolve_lt_var(vid),
481 r,
482 "region vid should have been resolved fully before canonicalization"
483 );
484 match self.canonicalize_mode {
485 CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
486 CanonicalizeMode::Response { .. } => {
487 CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
488 }
489 }
490 }
491 };
492
493 let var = self.get_or_insert_bound_var(r, kind);
494
495 Region::new_anon_bound(self.cx(), self.binder_index, var)
496 }
497
498 fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
499 if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
500 ty
501 } else {
502 let res = self.inner_fold_ty(t);
503 let old = self.cache.insert((self.binder_index, t), res);
504 assert_eq!(old, None);
505 res
506 }
507 }
508
509 fn fold_const(&mut self, c: I::Const) -> I::Const {
510 let kind = match c.kind() {
511 ty::ConstKind::Infer(i) => match i {
512 ty::InferConst::Var(vid) => {
513 debug_assert_eq!(
514 self.delegate.opportunistic_resolve_ct_var(vid),
515 c,
516 "const vid should have been resolved fully before canonicalization"
517 );
518
519 match self.canonicalize_mode {
520 CanonicalizeMode::Input { .. } => {
521 CanonicalVarKind::Const(ty::UniverseIndex::ROOT)
522 }
523 CanonicalizeMode::Response { .. } => {
524 CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())
525 }
526 }
527 }
528 ty::InferConst::Fresh(_) => todo!(),
529 },
530 ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
531 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
532 PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
533 ),
534 CanonicalizeMode::Response { .. } => {
535 CanonicalVarKind::PlaceholderConst(placeholder)
536 }
537 },
538 ty::ConstKind::Param(_) => match self.canonicalize_mode {
539 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
540 PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
541 ),
542 CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"),
543 },
544 ty::ConstKind::Bound(_, _)
546 | ty::ConstKind::Unevaluated(_)
547 | ty::ConstKind::Value(_)
548 | ty::ConstKind::Error(_)
549 | ty::ConstKind::Expr(_) => {
550 return if c.has_type_flags(NEEDS_CANONICAL) { c.super_fold_with(self) } else { c };
551 }
552 };
553
554 let var = self.get_or_insert_bound_var(c, kind);
555
556 Const::new_anon_bound(self.cx(), self.binder_index, var)
557 }
558
559 fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
560 if p.flags().intersects(NEEDS_CANONICAL) { p.super_fold_with(self) } else { p }
561 }
562
563 fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
564 match self.canonicalize_mode {
565 CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
566 | CanonicalizeMode::Response { max_input_universe: _ } => {}
567 CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => {
568 panic!("erasing 'static in env")
569 }
570 }
571 if c.flags().intersects(NEEDS_CANONICAL) { c.super_fold_with(self) } else { c }
572 }
573}