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
308impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
309 fn cx(&self) -> TyCtxt<'tcx> {
310 self.tcx
311 }
312
313 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
314 match r.kind() {
315 ty::ReBound(ty::BoundVarIndexKind::Bound(_), ..) => r,
316
317 ty::ReBound(ty::BoundVarIndexKind::Canonical, _) => {
318 bug!("canonicalized bound var found during canonicalization");
319 }
320
321 ty::ReStatic
322 | ty::ReEarlyParam(..)
323 | ty::ReError(_)
324 | ty::ReLateParam(_)
325 | ty::RePlaceholder(..)
326 | ty::ReVar(_)
327 | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r),
328 }
329 }
330
331 fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
332 match *t.kind() {
333 ty::Infer(ty::TyVar(mut vid)) => {
334 let root_vid = self.infcx.unwrap().root_var(vid);
338 if root_vid != vid {
339 t = Ty::new_var(self.tcx, root_vid);
340 vid = root_vid;
341 }
342
343 debug!("canonical: type var found with vid {:?}", vid);
344 match self.infcx.unwrap().probe_ty_var(vid) {
345 Ok(t) => {
347 debug!("(resolved to {:?})", t);
348 self.fold_ty(t)
349 }
350
351 Err(mut ui) => {
354 if !self.canonicalize_mode.preserve_universes() {
355 ui = ty::UniverseIndex::ROOT;
357 }
358 let sub_root = self.get_or_insert_sub_root(vid);
359 self.canonicalize_ty_var(CanonicalVarKind::Ty { ui, sub_root }, t)
360 }
361 }
362 }
363
364 ty::Infer(ty::IntVar(vid)) => {
365 let nt = self.infcx.unwrap().opportunistic_resolve_int_var(vid);
366 if nt != t {
367 return self.fold_ty(nt);
368 } else {
369 self.canonicalize_ty_var(CanonicalVarKind::Int, t)
370 }
371 }
372 ty::Infer(ty::FloatVar(vid)) => {
373 let nt = self.infcx.unwrap().opportunistic_resolve_float_var(vid);
374 if nt != t {
375 return self.fold_ty(nt);
376 } else {
377 self.canonicalize_ty_var(CanonicalVarKind::Float, t)
378 }
379 }
380
381 ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
382 bug!("encountered a fresh type during canonicalization")
383 }
384
385 ty::Placeholder(mut placeholder) => {
386 if !self.canonicalize_mode.preserve_universes() {
387 placeholder.universe = ty::UniverseIndex::ROOT;
388 }
389 self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t)
390 }
391
392 ty::Bound(ty::BoundVarIndexKind::Bound(_), _) => t,
393
394 ty::Bound(ty::BoundVarIndexKind::Canonical, _) => {
395 bug!("canonicalized bound var found during canonicalization");
396 }
397
398 ty::Closure(..)
399 | ty::CoroutineClosure(..)
400 | ty::Coroutine(..)
401 | ty::CoroutineWitness(..)
402 | ty::Bool
403 | ty::Char
404 | ty::Int(..)
405 | ty::Uint(..)
406 | ty::Float(..)
407 | ty::Adt(..)
408 | ty::Str
409 | ty::Error(_)
410 | ty::Array(..)
411 | ty::Slice(..)
412 | ty::RawPtr(..)
413 | ty::Ref(..)
414 | ty::FnDef(..)
415 | ty::FnPtr(..)
416 | ty::Dynamic(..)
417 | ty::UnsafeBinder(_)
418 | ty::Never
419 | ty::Tuple(..)
420 | ty::Alias(..)
421 | ty::Foreign(..)
422 | ty::Pat(..)
423 | ty::Param(..) => {
424 if t.flags().intersects(self.needs_canonical_flags) {
425 t.super_fold_with(self)
426 } else {
427 t
428 }
429 }
430 }
431 }
432
433 fn fold_const(&mut self, mut ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
434 match ct.kind() {
435 ty::ConstKind::Infer(InferConst::Var(mut vid)) => {
436 let root_vid = self.infcx.unwrap().root_const_var(vid);
440 if root_vid != vid {
441 ct = ty::Const::new_var(self.tcx, root_vid);
442 vid = root_vid;
443 }
444
445 debug!("canonical: const var found with vid {:?}", vid);
446 match self.infcx.unwrap().probe_const_var(vid) {
447 Ok(c) => {
448 debug!("(resolved to {:?})", c);
449 return self.fold_const(c);
450 }
451
452 Err(mut ui) => {
455 if !self.canonicalize_mode.preserve_universes() {
456 ui = ty::UniverseIndex::ROOT;
458 }
459 return self.canonicalize_const_var(CanonicalVarKind::Const(ui), ct);
460 }
461 }
462 }
463 ty::ConstKind::Infer(InferConst::Fresh(_)) => {
464 bug!("encountered a fresh const during canonicalization")
465 }
466 ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(_), _) => {
467 return ct;
468 }
469 ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, _) => {
470 bug!("canonicalized bound var found during canonicalization");
471 }
472 ty::ConstKind::Placeholder(placeholder) => {
473 return self
474 .canonicalize_const_var(CanonicalVarKind::PlaceholderConst(placeholder), ct);
475 }
476 _ => {}
477 }
478
479 if ct.flags().intersects(self.needs_canonical_flags) {
480 ct.super_fold_with(self)
481 } else {
482 ct
483 }
484 }
485
486 fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
487 if p.flags().intersects(self.needs_canonical_flags) { p.super_fold_with(self) } else { p }
488 }
489
490 fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
491 if c.flags().intersects(self.needs_canonical_flags) { c.super_fold_with(self) } else { c }
492 }
493}
494
495impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
496 fn canonicalize<V>(
499 value: V,
500 infcx: Option<&InferCtxt<'tcx>>,
501 tcx: TyCtxt<'tcx>,
502 canonicalize_region_mode: &dyn CanonicalizeMode,
503 query_state: &mut OriginalQueryValues<'tcx>,
504 ) -> Canonical<'tcx, V>
505 where
506 V: TypeFoldable<TyCtxt<'tcx>>,
507 {
508 let base = Canonical {
509 max_universe: ty::UniverseIndex::ROOT,
510 variables: List::empty(),
511 value: (),
512 };
513 Canonicalizer::canonicalize_with_base(
514 base,
515 value,
516 infcx,
517 tcx,
518 canonicalize_region_mode,
519 query_state,
520 )
521 .unchecked_map(|((), val)| val)
522 }
523
524 fn canonicalize_with_base<U, V>(
525 base: Canonical<'tcx, U>,
526 value: V,
527 infcx: Option<&InferCtxt<'tcx>>,
528 tcx: TyCtxt<'tcx>,
529 canonicalize_region_mode: &dyn CanonicalizeMode,
530 query_state: &mut OriginalQueryValues<'tcx>,
531 ) -> Canonical<'tcx, (U, V)>
532 where
533 V: TypeFoldable<TyCtxt<'tcx>>,
534 {
535 let needs_canonical_flags = if canonicalize_region_mode.any() {
536 TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS
537 } else {
538 TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER
539 };
540
541 if !value.has_type_flags(needs_canonical_flags) {
543 return base.unchecked_map(|b| (b, value));
544 }
545
546 let mut canonicalizer = Canonicalizer {
547 infcx,
548 tcx,
549 canonicalize_mode: canonicalize_region_mode,
550 needs_canonical_flags,
551 variables: SmallVec::from_slice(base.variables),
552 query_state,
553 indices: FxHashMap::default(),
554 sub_root_lookup_table: Default::default(),
555 };
556 if canonicalizer.query_state.var_values.spilled() {
557 canonicalizer.indices = canonicalizer
558 .query_state
559 .var_values
560 .iter()
561 .enumerate()
562 .map(|(i, &kind)| (kind, BoundVar::new(i)))
563 .collect();
564 }
565 let out_value = value.fold_with(&mut canonicalizer);
566
567 debug_assert!(!out_value.has_infer() && !out_value.has_placeholders());
571
572 let canonical_variables =
573 tcx.mk_canonical_var_kinds(&canonicalizer.universe_canonicalized_variables());
574
575 let max_universe = canonical_variables
576 .iter()
577 .map(|cvar| cvar.universe())
578 .max()
579 .unwrap_or(ty::UniverseIndex::ROOT);
580
581 Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) }
582 }
583
584 fn canonical_var(
589 &mut self,
590 var_kind: CanonicalVarKind<'tcx>,
591 value: GenericArg<'tcx>,
592 ) -> BoundVar {
593 let Canonicalizer { variables, query_state, indices, .. } = self;
594
595 let var_values = &mut query_state.var_values;
596
597 let universe = var_kind.universe();
598 if universe != ty::UniverseIndex::ROOT {
599 assert!(self.canonicalize_mode.preserve_universes());
600
601 match query_state.universe_map.binary_search(&universe) {
605 Err(idx) => query_state.universe_map.insert(idx, universe),
606 Ok(_) => {}
607 }
608 }
609
610 if !var_values.spilled() {
616 if let Some(idx) = var_values.iter().position(|&v| v == value) {
619 BoundVar::new(idx)
621 } else {
622 variables.push(var_kind);
625 var_values.push(value);
626 assert_eq!(variables.len(), var_values.len());
627
628 if var_values.spilled() {
631 assert!(indices.is_empty());
632 *indices = var_values
633 .iter()
634 .enumerate()
635 .map(|(i, &value)| (value, BoundVar::new(i)))
636 .collect();
637 }
638 BoundVar::new(var_values.len() - 1)
640 }
641 } else {
642 *indices.entry(value).or_insert_with(|| {
644 variables.push(var_kind);
645 var_values.push(value);
646 assert_eq!(variables.len(), var_values.len());
647 BoundVar::new(variables.len() - 1)
648 })
649 }
650 }
651
652 fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar {
653 let root_vid = self.infcx.unwrap().sub_unification_table_root_var(vid);
654 let idx =
655 *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len());
656 ty::BoundVar::from(idx)
657 }
658
659 fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarKind<'tcx>; 8]> {
663 if self.query_state.universe_map.len() == 1 {
664 return self.variables;
665 }
666
667 let reverse_universe_map: FxHashMap<ty::UniverseIndex, ty::UniverseIndex> = self
668 .query_state
669 .universe_map
670 .iter()
671 .enumerate()
672 .map(|(idx, universe)| (*universe, ty::UniverseIndex::from_usize(idx)))
673 .collect();
674
675 self.variables
676 .iter()
677 .map(|&kind| match kind {
678 CanonicalVarKind::Int | CanonicalVarKind::Float => {
679 return kind;
680 }
681 CanonicalVarKind::Ty { ui, sub_root } => {
682 CanonicalVarKind::Ty { ui: reverse_universe_map[&ui], sub_root }
683 }
684 CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]),
685 CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
686 CanonicalVarKind::PlaceholderTy(placeholder) => {
687 CanonicalVarKind::PlaceholderTy(ty::Placeholder {
688 universe: reverse_universe_map[&placeholder.universe],
689 ..placeholder
690 })
691 }
692 CanonicalVarKind::PlaceholderRegion(placeholder) => {
693 CanonicalVarKind::PlaceholderRegion(ty::Placeholder {
694 universe: reverse_universe_map[&placeholder.universe],
695 ..placeholder
696 })
697 }
698 CanonicalVarKind::PlaceholderConst(placeholder) => {
699 CanonicalVarKind::PlaceholderConst(ty::Placeholder {
700 universe: reverse_universe_map[&placeholder.universe],
701 ..placeholder
702 })
703 }
704 })
705 .collect()
706 }
707
708 fn canonical_var_for_region_in_root_universe(
722 &mut self,
723 r: ty::Region<'tcx>,
724 ) -> ty::Region<'tcx> {
725 self.canonical_var_for_region(CanonicalVarKind::Region(ty::UniverseIndex::ROOT), r)
726 }
727
728 fn canonical_var_for_region(
731 &mut self,
732 var_kind: CanonicalVarKind<'tcx>,
733 r: ty::Region<'tcx>,
734 ) -> ty::Region<'tcx> {
735 let var = self.canonical_var(var_kind, r.into());
736 ty::Region::new_canonical_bound(self.cx(), var)
737 }
738
739 fn canonicalize_ty_var(
744 &mut self,
745 var_kind: CanonicalVarKind<'tcx>,
746 ty_var: Ty<'tcx>,
747 ) -> Ty<'tcx> {
748 debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var)));
749 let var = self.canonical_var(var_kind, ty_var.into());
750 Ty::new_canonical_bound(self.tcx, var)
751 }
752
753 fn canonicalize_const_var(
758 &mut self,
759 var_kind: CanonicalVarKind<'tcx>,
760 ct_var: ty::Const<'tcx>,
761 ) -> ty::Const<'tcx> {
762 debug_assert!(
763 !self.infcx.is_some_and(|infcx| ct_var != infcx.shallow_resolve_const(ct_var))
764 );
765 let var = self.canonical_var(var_kind, ct_var.into());
766 ty::Const::new_canonical_bound(self.tcx, var)
767 }
768}