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