rustc_infer/infer/canonical/
canonicalizer.rs1use rustc_data_structures::fx::FxHashMap;
9use rustc_data_structures::sso::SsoHashMap;
10use rustc_index::Idx;
11use rustc_middle::bug;
12use rustc_middle::ty::{
13 self, BoundVar, GenericArg, InferConst, List, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeFolder,
14 TypeSuperFoldable, TypeVisitableExt,
15};
16use smallvec::SmallVec;
17use tracing::debug;
18
19use crate::infer::InferCtxt;
20use crate::infer::canonical::{
21 Canonical, CanonicalQueryInput, CanonicalVarKind, OriginalQueryValues,
22};
23
24impl<'tcx> InferCtxt<'tcx> {
25 pub fn canonicalize_query<V>(
41 &self,
42 value: ty::ParamEnvAnd<'tcx, V>,
43 query_state: &mut OriginalQueryValues<'tcx>,
44 ) -> CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, V>>
45 where
46 V: TypeFoldable<TyCtxt<'tcx>>,
47 {
48 let ty::ParamEnvAnd { param_env, value } = value;
49 let canonical_param_env = self.tcx.canonical_param_env_cache.get_or_insert(
50 self.tcx,
51 param_env,
52 query_state,
53 |tcx, param_env, query_state| {
54 Canonicalizer::canonicalize(
57 param_env,
58 None,
59 tcx,
60 &CanonicalizeFreeRegionsOtherThanStatic,
61 query_state,
62 )
63 },
64 );
65
66 let canonical = Canonicalizer::canonicalize_with_base(
67 canonical_param_env,
68 value,
69 Some(self),
70 self.tcx,
71 &CanonicalizeAllFreeRegions,
72 query_state,
73 )
74 .unchecked_map(|(param_env, value)| param_env.and(value));
75 CanonicalQueryInput { canonical, typing_mode: self.typing_mode() }
76 }
77
78 pub fn canonicalize_response<V>(&self, value: V) -> Canonical<'tcx, V>
104 where
105 V: TypeFoldable<TyCtxt<'tcx>>,
106 {
107 let mut query_state = OriginalQueryValues::default();
108 Canonicalizer::canonicalize(
109 value,
110 Some(self),
111 self.tcx,
112 &CanonicalizeQueryResponse,
113 &mut query_state,
114 )
115 }
116
117 pub fn canonicalize_user_type_annotation<V>(&self, value: V) -> Canonical<'tcx, V>
118 where
119 V: TypeFoldable<TyCtxt<'tcx>>,
120 {
121 let mut query_state = OriginalQueryValues::default();
122 Canonicalizer::canonicalize(
123 value,
124 Some(self),
125 self.tcx,
126 &CanonicalizeUserTypeAnnotation,
127 &mut query_state,
128 )
129 }
130}
131
132trait CanonicalizeMode {
140 fn canonicalize_free_region<'tcx>(
141 &self,
142 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
143 r: ty::Region<'tcx>,
144 ) -> ty::Region<'tcx>;
145
146 fn any(&self) -> bool;
147
148 fn preserve_universes(&self) -> bool;
150}
151
152struct CanonicalizeQueryResponse;
153
154impl CanonicalizeMode for CanonicalizeQueryResponse {
155 fn canonicalize_free_region<'tcx>(
156 &self,
157 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
158 mut r: ty::Region<'tcx>,
159 ) -> ty::Region<'tcx> {
160 let infcx = canonicalizer.infcx.unwrap();
161
162 if let ty::ReVar(vid) = r.kind() {
163 r = infcx
164 .inner
165 .borrow_mut()
166 .unwrap_region_constraints()
167 .opportunistic_resolve_var(canonicalizer.tcx, vid);
168 debug!(
169 "canonical: region var found with vid {vid:?}, \
170 opportunistically resolved to {r:?}",
171 );
172 };
173
174 match r.kind() {
175 ty::ReLateParam(_) | ty::ReErased | ty::ReStatic | ty::ReEarlyParam(..) => r,
176
177 ty::RePlaceholder(placeholder) => canonicalizer
178 .canonical_var_for_region(CanonicalVarKind::PlaceholderRegion(placeholder), r),
179
180 ty::ReVar(vid) => {
181 let universe = infcx
182 .inner
183 .borrow_mut()
184 .unwrap_region_constraints()
185 .probe_value(vid)
186 .unwrap_err();
187 canonicalizer.canonical_var_for_region(CanonicalVarKind::Region(universe), r)
188 }
189
190 _ => {
191 canonicalizer
200 .tcx
201 .dcx()
202 .delayed_bug(format!("unexpected region in query response: `{r:?}`"));
203 r
204 }
205 }
206 }
207
208 fn any(&self) -> bool {
209 false
210 }
211
212 fn preserve_universes(&self) -> bool {
213 true
214 }
215}
216
217struct CanonicalizeUserTypeAnnotation;
218
219impl CanonicalizeMode for CanonicalizeUserTypeAnnotation {
220 fn canonicalize_free_region<'tcx>(
221 &self,
222 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
223 r: ty::Region<'tcx>,
224 ) -> ty::Region<'tcx> {
225 match r.kind() {
226 ty::ReEarlyParam(_)
227 | ty::ReLateParam(_)
228 | ty::ReErased
229 | ty::ReStatic
230 | ty::ReError(_) => r,
231 ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
232 ty::RePlaceholder(..) | ty::ReBound(..) => {
233 bug!("unexpected region in query response: `{:?}`", r)
235 }
236 }
237 }
238
239 fn any(&self) -> bool {
240 false
241 }
242
243 fn preserve_universes(&self) -> bool {
244 false
245 }
246}
247
248struct CanonicalizeAllFreeRegions;
249
250impl CanonicalizeMode for CanonicalizeAllFreeRegions {
251 fn canonicalize_free_region<'tcx>(
252 &self,
253 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
254 r: ty::Region<'tcx>,
255 ) -> ty::Region<'tcx> {
256 canonicalizer.canonical_var_for_region_in_root_universe(r)
257 }
258
259 fn any(&self) -> bool {
260 true
261 }
262
263 fn preserve_universes(&self) -> bool {
264 false
265 }
266}
267
268struct CanonicalizeFreeRegionsOtherThanStatic;
269
270impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic {
271 fn canonicalize_free_region<'tcx>(
272 &self,
273 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
274 r: ty::Region<'tcx>,
275 ) -> ty::Region<'tcx> {
276 if r.is_static() { r } else { canonicalizer.canonical_var_for_region_in_root_universe(r) }
277 }
278
279 fn any(&self) -> bool {
280 true
281 }
282
283 fn preserve_universes(&self) -> bool {
284 false
285 }
286}
287
288struct Canonicalizer<'cx, 'tcx> {
289 infcx: Option<&'cx InferCtxt<'tcx>>,
291 tcx: TyCtxt<'tcx>,
292 variables: SmallVec<[CanonicalVarKind<'tcx>; 8]>,
293 query_state: &'cx mut OriginalQueryValues<'tcx>,
294 indices: FxHashMap<GenericArg<'tcx>, BoundVar>,
297 sub_root_lookup_table: SsoHashMap<ty::TyVid, usize>,
304 canonicalize_mode: &'cx dyn CanonicalizeMode,
305 needs_canonical_flags: TypeFlags,
306
307 binder_index: ty::DebruijnIndex,
308}
309
310impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
311 fn cx(&self) -> TyCtxt<'tcx> {
312 self.tcx
313 }
314
315 fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T>
316 where
317 T: TypeFoldable<TyCtxt<'tcx>>,
318 {
319 self.binder_index.shift_in(1);
320 let t = t.super_fold_with(self);
321 self.binder_index.shift_out(1);
322 t
323 }
324
325 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
326 match r.kind() {
327 ty::ReBound(index, ..) => {
328 if index >= self.binder_index {
329 bug!("escaping late-bound region during canonicalization");
330 } else {
331 r
332 }
333 }
334
335 ty::ReStatic
336 | ty::ReEarlyParam(..)
337 | ty::ReError(_)
338 | ty::ReLateParam(_)
339 | ty::RePlaceholder(..)
340 | ty::ReVar(_)
341 | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r),
342 }
343 }
344
345 fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
346 match *t.kind() {
347 ty::Infer(ty::TyVar(mut vid)) => {
348 let root_vid = self.infcx.unwrap().root_var(vid);
352 if root_vid != vid {
353 t = Ty::new_var(self.tcx, root_vid);
354 vid = root_vid;
355 }
356
357 debug!("canonical: type var found with vid {:?}", vid);
358 match self.infcx.unwrap().probe_ty_var(vid) {
359 Ok(t) => {
361 debug!("(resolved to {:?})", t);
362 self.fold_ty(t)
363 }
364
365 Err(mut ui) => {
368 if !self.canonicalize_mode.preserve_universes() {
369 ui = ty::UniverseIndex::ROOT;
371 }
372 let sub_root = self.get_or_insert_sub_root(vid);
373 self.canonicalize_ty_var(CanonicalVarKind::Ty { ui, sub_root }, t)
374 }
375 }
376 }
377
378 ty::Infer(ty::IntVar(vid)) => {
379 let nt = self.infcx.unwrap().opportunistic_resolve_int_var(vid);
380 if nt != t {
381 return self.fold_ty(nt);
382 } else {
383 self.canonicalize_ty_var(CanonicalVarKind::Int, t)
384 }
385 }
386 ty::Infer(ty::FloatVar(vid)) => {
387 let nt = self.infcx.unwrap().opportunistic_resolve_float_var(vid);
388 if nt != t {
389 return self.fold_ty(nt);
390 } else {
391 self.canonicalize_ty_var(CanonicalVarKind::Float, t)
392 }
393 }
394
395 ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
396 bug!("encountered a fresh type during canonicalization")
397 }
398
399 ty::Placeholder(mut placeholder) => {
400 if !self.canonicalize_mode.preserve_universes() {
401 placeholder.universe = ty::UniverseIndex::ROOT;
402 }
403 self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t)
404 }
405
406 ty::Bound(debruijn, _) => {
407 if debruijn >= self.binder_index {
408 bug!("escaping bound type during canonicalization")
409 } else {
410 t
411 }
412 }
413
414 ty::Closure(..)
415 | ty::CoroutineClosure(..)
416 | ty::Coroutine(..)
417 | ty::CoroutineWitness(..)
418 | ty::Bool
419 | ty::Char
420 | ty::Int(..)
421 | ty::Uint(..)
422 | ty::Float(..)
423 | ty::Adt(..)
424 | ty::Str
425 | ty::Error(_)
426 | ty::Array(..)
427 | ty::Slice(..)
428 | ty::RawPtr(..)
429 | ty::Ref(..)
430 | ty::FnDef(..)
431 | ty::FnPtr(..)
432 | ty::Dynamic(..)
433 | ty::UnsafeBinder(_)
434 | ty::Never
435 | ty::Tuple(..)
436 | ty::Alias(..)
437 | ty::Foreign(..)
438 | ty::Pat(..)
439 | ty::Param(..) => {
440 if t.flags().intersects(self.needs_canonical_flags) {
441 t.super_fold_with(self)
442 } else {
443 t
444 }
445 }
446 }
447 }
448
449 fn fold_const(&mut self, mut ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
450 match ct.kind() {
451 ty::ConstKind::Infer(InferConst::Var(mut vid)) => {
452 let root_vid = self.infcx.unwrap().root_const_var(vid);
456 if root_vid != vid {
457 ct = ty::Const::new_var(self.tcx, root_vid);
458 vid = root_vid;
459 }
460
461 debug!("canonical: const var found with vid {:?}", vid);
462 match self.infcx.unwrap().probe_const_var(vid) {
463 Ok(c) => {
464 debug!("(resolved to {:?})", c);
465 return self.fold_const(c);
466 }
467
468 Err(mut ui) => {
471 if !self.canonicalize_mode.preserve_universes() {
472 ui = ty::UniverseIndex::ROOT;
474 }
475 return self.canonicalize_const_var(CanonicalVarKind::Const(ui), ct);
476 }
477 }
478 }
479 ty::ConstKind::Infer(InferConst::Fresh(_)) => {
480 bug!("encountered a fresh const during canonicalization")
481 }
482 ty::ConstKind::Bound(debruijn, _) => {
483 if debruijn >= self.binder_index {
484 bug!("escaping bound const during canonicalization")
485 } else {
486 return ct;
487 }
488 }
489 ty::ConstKind::Placeholder(placeholder) => {
490 return self
491 .canonicalize_const_var(CanonicalVarKind::PlaceholderConst(placeholder), ct);
492 }
493 _ => {}
494 }
495
496 if ct.flags().intersects(self.needs_canonical_flags) {
497 ct.super_fold_with(self)
498 } else {
499 ct
500 }
501 }
502
503 fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
504 if p.flags().intersects(self.needs_canonical_flags) { p.super_fold_with(self) } else { p }
505 }
506
507 fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
508 if c.flags().intersects(self.needs_canonical_flags) { c.super_fold_with(self) } else { c }
509 }
510}
511
512impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
513 fn canonicalize<V>(
516 value: V,
517 infcx: Option<&InferCtxt<'tcx>>,
518 tcx: TyCtxt<'tcx>,
519 canonicalize_region_mode: &dyn CanonicalizeMode,
520 query_state: &mut OriginalQueryValues<'tcx>,
521 ) -> Canonical<'tcx, V>
522 where
523 V: TypeFoldable<TyCtxt<'tcx>>,
524 {
525 let base = Canonical {
526 max_universe: ty::UniverseIndex::ROOT,
527 variables: List::empty(),
528 value: (),
529 };
530 Canonicalizer::canonicalize_with_base(
531 base,
532 value,
533 infcx,
534 tcx,
535 canonicalize_region_mode,
536 query_state,
537 )
538 .unchecked_map(|((), val)| val)
539 }
540
541 fn canonicalize_with_base<U, V>(
542 base: Canonical<'tcx, U>,
543 value: V,
544 infcx: Option<&InferCtxt<'tcx>>,
545 tcx: TyCtxt<'tcx>,
546 canonicalize_region_mode: &dyn CanonicalizeMode,
547 query_state: &mut OriginalQueryValues<'tcx>,
548 ) -> Canonical<'tcx, (U, V)>
549 where
550 V: TypeFoldable<TyCtxt<'tcx>>,
551 {
552 let needs_canonical_flags = if canonicalize_region_mode.any() {
553 TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS
554 } else {
555 TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER
556 };
557
558 if !value.has_type_flags(needs_canonical_flags) {
560 return base.unchecked_map(|b| (b, value));
561 }
562
563 let mut canonicalizer = Canonicalizer {
564 infcx,
565 tcx,
566 canonicalize_mode: canonicalize_region_mode,
567 needs_canonical_flags,
568 variables: SmallVec::from_slice(base.variables),
569 query_state,
570 indices: FxHashMap::default(),
571 sub_root_lookup_table: Default::default(),
572 binder_index: ty::INNERMOST,
573 };
574 if canonicalizer.query_state.var_values.spilled() {
575 canonicalizer.indices = canonicalizer
576 .query_state
577 .var_values
578 .iter()
579 .enumerate()
580 .map(|(i, &kind)| (kind, BoundVar::new(i)))
581 .collect();
582 }
583 let out_value = value.fold_with(&mut canonicalizer);
584
585 debug_assert!(!out_value.has_infer() && !out_value.has_placeholders());
589
590 let canonical_variables =
591 tcx.mk_canonical_var_kinds(&canonicalizer.universe_canonicalized_variables());
592
593 let max_universe = canonical_variables
594 .iter()
595 .map(|cvar| cvar.universe())
596 .max()
597 .unwrap_or(ty::UniverseIndex::ROOT);
598
599 Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) }
600 }
601
602 fn canonical_var(
607 &mut self,
608 var_kind: CanonicalVarKind<'tcx>,
609 value: GenericArg<'tcx>,
610 ) -> BoundVar {
611 let Canonicalizer { variables, query_state, indices, .. } = self;
612
613 let var_values = &mut query_state.var_values;
614
615 let universe = var_kind.universe();
616 if universe != ty::UniverseIndex::ROOT {
617 assert!(self.canonicalize_mode.preserve_universes());
618
619 match query_state.universe_map.binary_search(&universe) {
623 Err(idx) => query_state.universe_map.insert(idx, universe),
624 Ok(_) => {}
625 }
626 }
627
628 if !var_values.spilled() {
634 if let Some(idx) = var_values.iter().position(|&v| v == value) {
637 BoundVar::new(idx)
639 } else {
640 variables.push(var_kind);
643 var_values.push(value);
644 assert_eq!(variables.len(), var_values.len());
645
646 if var_values.spilled() {
649 assert!(indices.is_empty());
650 *indices = var_values
651 .iter()
652 .enumerate()
653 .map(|(i, &value)| (value, BoundVar::new(i)))
654 .collect();
655 }
656 BoundVar::new(var_values.len() - 1)
658 }
659 } else {
660 *indices.entry(value).or_insert_with(|| {
662 variables.push(var_kind);
663 var_values.push(value);
664 assert_eq!(variables.len(), var_values.len());
665 BoundVar::new(variables.len() - 1)
666 })
667 }
668 }
669
670 fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar {
671 let root_vid = self.infcx.unwrap().sub_unification_table_root_var(vid);
672 let idx =
673 *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len());
674 ty::BoundVar::from(idx)
675 }
676
677 fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarKind<'tcx>; 8]> {
681 if self.query_state.universe_map.len() == 1 {
682 return self.variables;
683 }
684
685 let reverse_universe_map: FxHashMap<ty::UniverseIndex, ty::UniverseIndex> = self
686 .query_state
687 .universe_map
688 .iter()
689 .enumerate()
690 .map(|(idx, universe)| (*universe, ty::UniverseIndex::from_usize(idx)))
691 .collect();
692
693 self.variables
694 .iter()
695 .map(|&kind| match kind {
696 CanonicalVarKind::Int | CanonicalVarKind::Float => {
697 return kind;
698 }
699 CanonicalVarKind::Ty { ui, sub_root } => {
700 CanonicalVarKind::Ty { ui: reverse_universe_map[&ui], sub_root }
701 }
702 CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]),
703 CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
704 CanonicalVarKind::PlaceholderTy(placeholder) => {
705 CanonicalVarKind::PlaceholderTy(ty::Placeholder {
706 universe: reverse_universe_map[&placeholder.universe],
707 ..placeholder
708 })
709 }
710 CanonicalVarKind::PlaceholderRegion(placeholder) => {
711 CanonicalVarKind::PlaceholderRegion(ty::Placeholder {
712 universe: reverse_universe_map[&placeholder.universe],
713 ..placeholder
714 })
715 }
716 CanonicalVarKind::PlaceholderConst(placeholder) => {
717 CanonicalVarKind::PlaceholderConst(ty::Placeholder {
718 universe: reverse_universe_map[&placeholder.universe],
719 ..placeholder
720 })
721 }
722 })
723 .collect()
724 }
725
726 fn canonical_var_for_region_in_root_universe(
740 &mut self,
741 r: ty::Region<'tcx>,
742 ) -> ty::Region<'tcx> {
743 self.canonical_var_for_region(CanonicalVarKind::Region(ty::UniverseIndex::ROOT), r)
744 }
745
746 fn canonical_var_for_region(
749 &mut self,
750 var_kind: CanonicalVarKind<'tcx>,
751 r: ty::Region<'tcx>,
752 ) -> ty::Region<'tcx> {
753 let var = self.canonical_var(var_kind, r.into());
754 let br = ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon };
755 ty::Region::new_bound(self.cx(), self.binder_index, br)
756 }
757
758 fn canonicalize_ty_var(
763 &mut self,
764 var_kind: CanonicalVarKind<'tcx>,
765 ty_var: Ty<'tcx>,
766 ) -> Ty<'tcx> {
767 debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var)));
768 let var = self.canonical_var(var_kind, ty_var.into());
769 let bt = ty::BoundTy { var, kind: ty::BoundTyKind::Anon };
770 Ty::new_bound(self.tcx, self.binder_index, bt)
771 }
772
773 fn canonicalize_const_var(
778 &mut self,
779 var_kind: CanonicalVarKind<'tcx>,
780 ct_var: ty::Const<'tcx>,
781 ) -> ty::Const<'tcx> {
782 debug_assert!(
783 !self.infcx.is_some_and(|infcx| ct_var != infcx.shallow_resolve_const(ct_var))
784 );
785 let var = self.canonical_var(var_kind, ct_var.into());
786 let bc = ty::BoundConst { var };
787 ty::Const::new_bound(self.tcx, self.binder_index, bc)
788 }
789}