1 //! This module contains the "canonicalizer" itself.
3 //! For an overview of what canonicalization is and how it fits into
4 //! rustc, check out the [chapter in the rustc guide][c].
6 //! [c]: https://rust-lang.github.io/rustc-guide/traits/canonicalization.html
8 use crate::infer::canonical::{
9 Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized,
12 use crate::infer::InferCtxt;
13 use crate::mir::interpret::ConstValue;
14 use std::sync::atomic::Ordering;
15 use crate::ty::fold::{TypeFoldable, TypeFolder};
16 use crate::ty::subst::Kind;
17 use crate::ty::{self, BoundVar, InferConst, Lift, List, Ty, TyCtxt, TypeFlags};
19 use rustc_data_structures::fx::FxHashMap;
20 use rustc_data_structures::indexed_vec::Idx;
21 use smallvec::SmallVec;
23 impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
24 /// Canonicalizes a query value `V`. When we canonicalize a query,
25 /// we not only canonicalize unbound inference variables, but we
26 /// *also* replace all free regions whatsoever. So for example a
27 /// query like `T: Trait<'static>` would be canonicalized to
33 /// with a mapping M that maps `'?0` to `'static`.
35 /// To get a good understanding of what is happening here, check
36 /// out the [chapter in the rustc guide][c].
38 /// [c]: https://rust-lang.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query
39 pub fn canonicalize_query<V>(
42 query_state: &mut OriginalQueryValues<'tcx>,
43 ) -> Canonicalized<'gcx, V>
45 V: TypeFoldable<'tcx> + Lift<'gcx>,
50 .queries_canonicalized
51 .fetch_add(1, Ordering::Relaxed);
53 Canonicalizer::canonicalize(
57 &CanonicalizeAllFreeRegions,
62 /// Canonicalizes a query *response* `V`. When we canonicalize a
63 /// query response, we only canonicalize unbound inference
64 /// variables, and we leave other free regions alone. So,
65 /// continuing with the example from `canonicalize_query`, if
66 /// there was an input query `T: Trait<'static>`, it would have
67 /// been canonicalized to
73 /// with a mapping M that maps `'?0` to `'static`. But if we found that there
74 /// exists only one possible impl of `Trait`, and it looks like
76 /// impl<T> Trait<'static> for T { .. }
78 /// then we would prepare a query result R that (among other
79 /// things) includes a mapping to `'?0 := 'static`. When
80 /// canonicalizing this query result R, we would leave this
81 /// reference to `'static` alone.
83 /// To get a good understanding of what is happening here, check
84 /// out the [chapter in the rustc guide][c].
86 /// [c]: https://rust-lang.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query-result
87 pub fn canonicalize_response<V>(&self, value: &V) -> Canonicalized<'gcx, V>
89 V: TypeFoldable<'tcx> + Lift<'gcx>,
91 let mut query_state = OriginalQueryValues::default();
92 Canonicalizer::canonicalize(
96 &CanonicalizeQueryResponse,
101 pub fn canonicalize_user_type_annotation<V>(&self, value: &V) -> Canonicalized<'gcx, V>
103 V: TypeFoldable<'tcx> + Lift<'gcx>,
105 let mut query_state = OriginalQueryValues::default();
106 Canonicalizer::canonicalize(
110 &CanonicalizeUserTypeAnnotation,
115 /// A hacky variant of `canonicalize_query` that does not
116 /// canonicalize `'static`. Unfortunately, the existing leak
117 /// check treaks `'static` differently in some cases (see also
118 /// #33684), so if we are performing an operation that may need to
119 /// prove "leak-check" related things, we leave `'static`
122 // FIXME(#48536): once we have universes, we can remove this and just use
123 // `canonicalize_query`.
124 pub fn canonicalize_hr_query_hack<V>(
127 query_state: &mut OriginalQueryValues<'tcx>,
128 ) -> Canonicalized<'gcx, V>
130 V: TypeFoldable<'tcx> + Lift<'gcx>,
135 .queries_canonicalized
136 .fetch_add(1, Ordering::Relaxed);
138 Canonicalizer::canonicalize(
142 &CanonicalizeFreeRegionsOtherThanStatic,
148 /// Controls how we canonicalize "free regions" that are not inference
149 /// variables. This depends on what we are canonicalizing *for* --
150 /// e.g., if we are canonicalizing to create a query, we want to
151 /// replace those with inference variables, since we want to make a
152 /// maximally general query. But if we are canonicalizing a *query
153 /// response*, then we don't typically replace free regions, as they
154 /// must have been introduced from other parts of the system.
155 trait CanonicalizeRegionMode {
156 fn canonicalize_free_region(
158 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
160 ) -> ty::Region<'tcx>;
162 fn any(&self) -> bool;
165 struct CanonicalizeQueryResponse;
167 impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
168 fn canonicalize_free_region(
170 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
172 ) -> ty::Region<'tcx> {
174 ty::ReFree(_) | ty::ReEmpty | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r,
175 ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
177 kind: CanonicalVarKind::PlaceholderRegion(*placeholder),
182 let universe = canonicalizer.region_var_universe(*vid);
183 canonicalizer.canonical_var_for_region(
185 kind: CanonicalVarKind::Region(universe),
191 // Other than `'static` or `'empty`, the query
192 // response should be executing in a fully
193 // canonicalized environment, so there shouldn't be
194 // any other region names it can come up.
196 // rust-lang/rust#57464: `impl Trait` can leak local
197 // scopes (in manner violating typeck). Therefore, use
198 // `delay_span_bug` to allow type error over an ICE.
199 ty::tls::with_context(|c| {
200 c.tcx.sess.delay_span_bug(
201 syntax_pos::DUMMY_SP,
202 &format!("unexpected region in query response: `{:?}`", r));
209 fn any(&self) -> bool {
214 struct CanonicalizeUserTypeAnnotation;
216 impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
217 fn canonicalize_free_region(
219 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
221 ) -> ty::Region<'tcx> {
223 ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReEmpty | ty::ReStatic => r,
224 ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
226 // We only expect region names that the user can type.
227 bug!("unexpected region in query response: `{:?}`", r)
232 fn any(&self) -> bool {
237 struct CanonicalizeAllFreeRegions;
239 impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions {
240 fn canonicalize_free_region(
242 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
244 ) -> ty::Region<'tcx> {
245 canonicalizer.canonical_var_for_region_in_root_universe(r)
248 fn any(&self) -> bool {
253 struct CanonicalizeFreeRegionsOtherThanStatic;
255 impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
256 fn canonicalize_free_region(
258 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
260 ) -> ty::Region<'tcx> {
261 if let ty::ReStatic = r {
264 canonicalizer.canonical_var_for_region_in_root_universe(r)
268 fn any(&self) -> bool {
273 struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
274 infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>,
275 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
276 variables: SmallVec<[CanonicalVarInfo; 8]>,
277 query_state: &'cx mut OriginalQueryValues<'tcx>,
278 // Note that indices is only used once `var_values` is big enough to be
280 indices: FxHashMap<Kind<'tcx>, BoundVar>,
281 canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode,
282 needs_canonical_flags: TypeFlags,
284 binder_index: ty::DebruijnIndex,
287 impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> {
288 fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> {
292 fn fold_binder<T>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T>
293 where T: TypeFoldable<'tcx>
295 self.binder_index.shift_in(1);
296 let t = t.super_fold_with(self);
297 self.binder_index.shift_out(1);
301 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
303 ty::ReLateBound(index, ..) => {
304 if index >= self.binder_index {
305 bug!("escaping late bound region during canonicalization")
314 .borrow_region_constraints()
315 .opportunistic_resolve_var(self.tcx, vid);
317 "canonical: region var found with vid {:?}, \
318 opportunistically resolved to {:?}",
321 self.canonicalize_region_mode
322 .canonicalize_free_region(self, r)
326 | ty::ReEarlyBound(..)
329 | ty::RePlaceholder(..)
331 | ty::ReErased => self.canonicalize_region_mode
332 .canonicalize_free_region(self, r),
334 ty::ReClosureBound(..) => {
335 bug!("closure bound region encountered during canonicalization")
340 fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
342 ty::Infer(ty::TyVar(vid)) => {
343 debug!("canonical: type var found with vid {:?}", vid);
344 match self.infcx.unwrap().probe_ty_var(vid) {
345 // `t` could be a float / int variable: canonicalize that instead
347 debug!("(resolved to {:?})", t);
351 // `TyVar(vid)` is unresolved, track its universe index in the canonicalized
354 if !self.infcx.unwrap().tcx.sess.opts.debugging_opts.chalk {
355 // FIXME: perf problem described in #55921.
356 ui = ty::UniverseIndex::ROOT;
358 self.canonicalize_ty_var(
360 kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
368 ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var(
370 kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
375 ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var(
377 kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
382 ty::Infer(ty::FreshTy(_))
383 | ty::Infer(ty::FreshIntTy(_))
384 | ty::Infer(ty::FreshFloatTy(_)) => {
385 bug!("encountered a fresh type during canonicalization")
388 ty::Placeholder(placeholder) => self.canonicalize_ty_var(
390 kind: CanonicalVarKind::PlaceholderTy(placeholder)
395 ty::Bound(debruijn, _) => {
396 if debruijn >= self.binder_index {
397 bug!("escaping bound type during canonicalization")
405 | ty::GeneratorWitness(..)
424 | ty::UnnormalizedProjection(..)
427 | ty::Opaque(..) => {
428 if t.flags.intersects(self.needs_canonical_flags) {
429 t.super_fold_with(self)
437 fn fold_const(&mut self, c: &'tcx ty::LazyConst<'tcx>) -> &'tcx ty::LazyConst<'tcx> {
438 if let ty::LazyConst::Evaluated(ct) = c {
440 ConstValue::Infer(InferConst::Var(vid)) => {
441 debug!("canonical: const var found with vid {:?}", vid);
442 match self.infcx.unwrap().probe_const_var(vid) {
444 debug!("(resolved to {:?})", c);
445 return self.fold_const(c);
448 // `ConstVar(vid)` is unresolved, track its universe index in the
449 // canonicalized result
451 if !self.infcx.unwrap().tcx.sess.opts.debugging_opts.chalk {
452 // FIXME: perf problem described in #55921.
453 ui = ty::UniverseIndex::ROOT;
455 return self.canonicalize_const_var(
457 kind: CanonicalVarKind::Const(ui),
464 ConstValue::Infer(InferConst::Fresh(_)) => {
465 bug!("encountered a fresh const during canonicalization")
467 ConstValue::Infer(InferConst::Canonical(debruijn, _)) => {
468 if debruijn >= self.binder_index {
469 bug!("escaping bound type during canonicalization")
474 ConstValue::Placeholder(placeholder) => {
475 return self.canonicalize_const_var(
477 kind: CanonicalVarKind::PlaceholderConst(placeholder),
486 if c.type_flags().intersects(self.needs_canonical_flags) {
487 c.super_fold_with(self)
494 impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
495 /// The main `canonicalize` method, shared impl of
496 /// `canonicalize_query` and `canonicalize_response`.
499 infcx: Option<&InferCtxt<'_, 'gcx, 'tcx>>,
500 tcx: TyCtxt<'_, 'gcx, 'tcx>,
501 canonicalize_region_mode: &dyn CanonicalizeRegionMode,
502 query_state: &mut OriginalQueryValues<'tcx>,
503 ) -> Canonicalized<'gcx, V>
505 V: TypeFoldable<'tcx> + Lift<'gcx>,
507 let needs_canonical_flags = if canonicalize_region_mode.any() {
508 TypeFlags::KEEP_IN_LOCAL_TCX |
509 TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS`
510 TypeFlags::HAS_TY_PLACEHOLDER |
511 TypeFlags::HAS_CT_PLACEHOLDER
513 TypeFlags::KEEP_IN_LOCAL_TCX |
514 TypeFlags::HAS_RE_PLACEHOLDER |
515 TypeFlags::HAS_TY_PLACEHOLDER |
516 TypeFlags::HAS_CT_PLACEHOLDER
519 let gcx = tcx.global_tcx();
521 // Fast path: nothing that needs to be canonicalized.
522 if !value.has_type_flags(needs_canonical_flags) {
523 let out_value = gcx.lift(value).unwrap_or_else(|| {
525 "failed to lift `{:?}` (nothing to canonicalize)",
529 let canon_value = Canonical {
530 max_universe: ty::UniverseIndex::ROOT,
531 variables: List::empty(),
537 let mut canonicalizer = Canonicalizer {
540 canonicalize_region_mode,
541 needs_canonical_flags,
542 variables: SmallVec::new(),
544 indices: FxHashMap::default(),
545 binder_index: ty::INNERMOST,
547 let out_value = value.fold_with(&mut canonicalizer);
549 // Once we have canonicalized `out_value`, it should not
550 // contain anything that ties it to this inference context
551 // anymore, so it should live in the global arena.
552 let out_value = gcx.lift(&out_value).unwrap_or_else(|| {
554 "failed to lift `{:?}`, canonicalized from `{:?}`",
560 let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables);
562 let max_universe = canonical_variables
564 .map(|cvar| cvar.universe())
566 .unwrap_or(ty::UniverseIndex::ROOT);
570 variables: canonical_variables,
575 /// Creates a canonical variable replacing `kind` from the input,
576 /// or returns an existing variable if `kind` has already been
577 /// seen. `kind` is expected to be an unbound variable (or
578 /// potentially a free region).
579 fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> BoundVar {
587 let var_values = &mut query_state.var_values;
589 // This code is hot. `variables` and `var_values` are usually small
590 // (fewer than 8 elements ~95% of the time). They are SmallVec's to
591 // avoid allocations in those cases. We also don't use `indices` to
592 // determine if a kind has been seen before until the limit of 8 has
593 // been exceeded, to also avoid allocations for `indices`.
594 let var = if !var_values.spilled() {
595 // `var_values` is stack-allocated. `indices` isn't used yet. Do a
596 // direct linear search of `var_values`.
597 if let Some(idx) = var_values.iter().position(|&k| k == kind) {
598 // `kind` is already present in `var_values`.
601 // `kind` isn't present in `var_values`. Append it. Likewise
602 // for `info` and `variables`.
603 variables.push(info);
604 var_values.push(kind);
605 assert_eq!(variables.len(), var_values.len());
607 // If `var_values` has become big enough to be heap-allocated,
608 // fill up `indices` to facilitate subsequent lookups.
609 if var_values.spilled() {
610 assert!(indices.is_empty());
611 *indices = var_values
614 .map(|(i, &kind)| (kind, BoundVar::new(i)))
617 // The cv is the index of the appended element.
618 BoundVar::new(var_values.len() - 1)
621 // `var_values` is large. Do a hashmap search via `indices`.
622 *indices.entry(kind).or_insert_with(|| {
623 variables.push(info);
624 var_values.push(kind);
625 assert_eq!(variables.len(), var_values.len());
626 BoundVar::new(variables.len() - 1)
633 /// Shorthand helper that creates a canonical region variable for
634 /// `r` (always in the root universe). The reason that we always
635 /// put these variables into the root universe is because this
636 /// method is used during **query construction:** in that case, we
637 /// are taking all the regions and just putting them into the most
638 /// generic context we can. This may generate solutions that don't
639 /// fit (e.g., that equate some region variable with a placeholder
640 /// it can't name) on the caller side, but that's ok, the caller
641 /// can figure that out. In the meantime, it maximizes our
644 /// (This works because unification never fails -- and hence trait
645 /// selection is never affected -- due to a universe mismatch.)
646 fn canonical_var_for_region_in_root_universe(
649 ) -> ty::Region<'tcx> {
650 self.canonical_var_for_region(
652 kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
658 /// Returns the universe in which `vid` is defined.
659 fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
662 .borrow_region_constraints()
666 /// Creates a canonical variable (with the given `info`)
667 /// representing the region `r`; return a region referencing it.
668 fn canonical_var_for_region(
670 info: CanonicalVarInfo,
672 ) -> ty::Region<'tcx> {
673 let var = self.canonical_var(info, r.into());
674 let region = ty::ReLateBound(
676 ty::BoundRegion::BrAnon(var.as_u32())
678 self.tcx().mk_region(region)
681 /// Given a type variable `ty_var` of the given kind, first check
682 /// if `ty_var` is bound to anything; if so, canonicalize
683 /// *that*. Otherwise, create a new canonical variable for
685 fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo, ty_var: Ty<'tcx>) -> Ty<'tcx> {
686 let infcx = self.infcx.expect("encountered ty-var without infcx");
687 let bound_to = infcx.shallow_resolve_type(ty_var);
688 if bound_to != ty_var {
689 self.fold_ty(bound_to)
691 let var = self.canonical_var(info, ty_var.into());
692 self.tcx().mk_ty(ty::Bound(self.binder_index, var.into()))
696 /// Given a type variable `const_var` of the given kind, first check
697 /// if `const_var` is bound to anything; if so, canonicalize
698 /// *that*. Otherwise, create a new canonical variable for
700 fn canonicalize_const_var(
702 info: CanonicalVarInfo,
703 const_var: &'tcx ty::LazyConst<'tcx>
704 ) -> &'tcx ty::LazyConst<'tcx> {
705 let infcx = self.infcx.expect("encountered const-var without infcx");
706 let bound_to = infcx.resolve_const_var(const_var);
707 if bound_to != const_var {
708 self.fold_const(bound_to)
710 let ty = match const_var {
711 ty::LazyConst::Unevaluated(def_id, _) => {
712 self.tcx.type_of(*def_id)
714 ty::LazyConst::Evaluated(ty::Const { ty, .. }) => ty,
716 let var = self.canonical_var(info, const_var.into());
717 self.tcx().mk_lazy_const(
718 ty::LazyConst::Evaluated(ty::Const {
719 val: ConstValue::Infer(InferConst::Canonical(self.binder_index, var.into())),