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 std::sync::atomic::Ordering;
14 use crate::ty::fold::{TypeFoldable, TypeFolder};
15 use crate::ty::subst::Kind;
16 use crate::ty::{self, BoundVar, Lift, List, Ty, TyCtxt, TypeFlags};
18 use rustc_data_structures::fx::FxHashMap;
19 use rustc_data_structures::indexed_vec::Idx;
20 use smallvec::SmallVec;
22 impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
23 /// Canonicalizes a query value `V`. When we canonicalize a query,
24 /// we not only canonicalize unbound inference variables, but we
25 /// *also* replace all free regions whatsoever. So for example a
26 /// query like `T: Trait<'static>` would be canonicalized to
32 /// with a mapping M that maps `'?0` to `'static`.
34 /// To get a good understanding of what is happening here, check
35 /// out the [chapter in the rustc guide][c].
37 /// [c]: https://rust-lang.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query
38 pub fn canonicalize_query<V>(
41 query_state: &mut OriginalQueryValues<'tcx>,
42 ) -> Canonicalized<'gcx, V>
44 V: TypeFoldable<'tcx> + Lift<'gcx>,
49 .queries_canonicalized
50 .fetch_add(1, Ordering::Relaxed);
52 Canonicalizer::canonicalize(
56 &CanonicalizeAllFreeRegions,
61 /// Canonicalizes a query *response* `V`. When we canonicalize a
62 /// query response, we only canonicalize unbound inference
63 /// variables, and we leave other free regions alone. So,
64 /// continuing with the example from `canonicalize_query`, if
65 /// there was an input query `T: Trait<'static>`, it would have
66 /// been canonicalized to
72 /// with a mapping M that maps `'?0` to `'static`. But if we found that there
73 /// exists only one possible impl of `Trait`, and it looks like
75 /// impl<T> Trait<'static> for T { .. }
77 /// then we would prepare a query result R that (among other
78 /// things) includes a mapping to `'?0 := 'static`. When
79 /// canonicalizing this query result R, we would leave this
80 /// reference to `'static` alone.
82 /// To get a good understanding of what is happening here, check
83 /// out the [chapter in the rustc guide][c].
85 /// [c]: https://rust-lang.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query-result
86 pub fn canonicalize_response<V>(&self, value: &V) -> Canonicalized<'gcx, V>
88 V: TypeFoldable<'tcx> + Lift<'gcx>,
90 let mut query_state = OriginalQueryValues::default();
91 Canonicalizer::canonicalize(
95 &CanonicalizeQueryResponse,
100 pub fn canonicalize_user_type_annotation<V>(&self, value: &V) -> Canonicalized<'gcx, V>
102 V: TypeFoldable<'tcx> + Lift<'gcx>,
104 let mut query_state = OriginalQueryValues::default();
105 Canonicalizer::canonicalize(
109 &CanonicalizeUserTypeAnnotation,
114 /// A hacky variant of `canonicalize_query` that does not
115 /// canonicalize `'static`. Unfortunately, the existing leak
116 /// check treaks `'static` differently in some cases (see also
117 /// #33684), so if we are performing an operation that may need to
118 /// prove "leak-check" related things, we leave `'static`
121 // FIXME(#48536): once we have universes, we can remove this and just use
122 // `canonicalize_query`.
123 pub fn canonicalize_hr_query_hack<V>(
126 query_state: &mut OriginalQueryValues<'tcx>,
127 ) -> Canonicalized<'gcx, V>
129 V: TypeFoldable<'tcx> + Lift<'gcx>,
134 .queries_canonicalized
135 .fetch_add(1, Ordering::Relaxed);
137 Canonicalizer::canonicalize(
141 &CanonicalizeFreeRegionsOtherThanStatic,
147 /// Controls how we canonicalize "free regions" that are not inference
148 /// variables. This depends on what we are canonicalizing *for* --
149 /// e.g., if we are canonicalizing to create a query, we want to
150 /// replace those with inference variables, since we want to make a
151 /// maximally general query. But if we are canonicalizing a *query
152 /// response*, then we don't typically replace free regions, as they
153 /// must have been introduced from other parts of the system.
154 trait CanonicalizeRegionMode {
155 fn canonicalize_free_region(
157 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
159 ) -> ty::Region<'tcx>;
161 fn any(&self) -> bool;
164 struct CanonicalizeQueryResponse;
166 impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
167 fn canonicalize_free_region(
169 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
171 ) -> ty::Region<'tcx> {
173 ty::ReFree(_) | ty::ReEmpty | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r,
174 ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
176 kind: CanonicalVarKind::PlaceholderRegion(*placeholder),
181 let universe = canonicalizer.region_var_universe(*vid);
182 canonicalizer.canonical_var_for_region(
184 kind: CanonicalVarKind::Region(universe),
190 // Other than `'static` or `'empty`, the query
191 // response should be executing in a fully
192 // canonicalized environment, so there shouldn't be
193 // any other region names it can come up.
195 // rust-lang/rust#57464: `impl Trait` can leak local
196 // scopes (in manner violating typeck). Therefore, use
197 // `delay_span_bug` to allow type error over an ICE.
198 ty::tls::with_context(|c| {
199 c.tcx.sess.delay_span_bug(
200 syntax_pos::DUMMY_SP,
201 &format!("unexpected region in query response: `{:?}`", r));
208 fn any(&self) -> bool {
213 struct CanonicalizeUserTypeAnnotation;
215 impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
216 fn canonicalize_free_region(
218 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
220 ) -> ty::Region<'tcx> {
222 ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReEmpty | ty::ReStatic => r,
223 ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
225 // We only expect region names that the user can type.
226 bug!("unexpected region in query response: `{:?}`", r)
231 fn any(&self) -> bool {
236 struct CanonicalizeAllFreeRegions;
238 impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions {
239 fn canonicalize_free_region(
241 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
243 ) -> ty::Region<'tcx> {
244 canonicalizer.canonical_var_for_region_in_root_universe(r)
247 fn any(&self) -> bool {
252 struct CanonicalizeFreeRegionsOtherThanStatic;
254 impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
255 fn canonicalize_free_region(
257 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
259 ) -> ty::Region<'tcx> {
260 if let ty::ReStatic = r {
263 canonicalizer.canonical_var_for_region_in_root_universe(r)
267 fn any(&self) -> bool {
272 struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
273 infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>,
274 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
275 variables: SmallVec<[CanonicalVarInfo; 8]>,
276 query_state: &'cx mut OriginalQueryValues<'tcx>,
277 // Note that indices is only used once `var_values` is big enough to be
279 indices: FxHashMap<Kind<'tcx>, BoundVar>,
280 canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode,
281 needs_canonical_flags: TypeFlags,
283 binder_index: ty::DebruijnIndex,
286 impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> {
287 fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> {
291 fn fold_binder<T>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T>
292 where T: TypeFoldable<'tcx>
294 self.binder_index.shift_in(1);
295 let t = t.super_fold_with(self);
296 self.binder_index.shift_out(1);
300 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
302 ty::ReLateBound(index, ..) => {
303 if index >= self.binder_index {
304 bug!("escaping late bound region during canonicalization")
313 .borrow_region_constraints()
314 .opportunistic_resolve_var(self.tcx, vid);
316 "canonical: region var found with vid {:?}, \
317 opportunistically resolved to {:?}",
320 self.canonicalize_region_mode
321 .canonicalize_free_region(self, r)
325 | ty::ReEarlyBound(..)
328 | ty::RePlaceholder(..)
330 | ty::ReErased => self.canonicalize_region_mode
331 .canonicalize_free_region(self, r),
333 ty::ReClosureBound(..) => {
334 bug!("closure bound region encountered during canonicalization")
339 fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
341 ty::Infer(ty::TyVar(vid)) => {
342 debug!("canonical: type var found with vid {:?}", vid);
343 match self.infcx.unwrap().probe_ty_var(vid) {
344 // `t` could be a float / int variable: canonicalize that instead
346 debug!("(resolved to {:?})", t);
350 // `TyVar(vid)` is unresolved, track its universe index in the canonicalized
353 if !self.infcx.unwrap().tcx.sess.opts.debugging_opts.chalk {
354 // FIXME: perf problem described in #55921.
355 ui = ty::UniverseIndex::ROOT;
357 self.canonicalize_ty_var(
359 kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
367 ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var(
369 kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
374 ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var(
376 kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
381 ty::Infer(ty::FreshTy(_))
382 | ty::Infer(ty::FreshIntTy(_))
383 | ty::Infer(ty::FreshFloatTy(_)) => {
384 bug!("encountered a fresh type during canonicalization")
387 ty::Placeholder(placeholder) => self.canonicalize_ty_var(
389 kind: CanonicalVarKind::PlaceholderTy(placeholder)
394 ty::Bound(debruijn, _) => {
395 if debruijn >= self.binder_index {
396 bug!("escaping bound type during canonicalization")
404 | ty::GeneratorWitness(..)
423 | ty::UnnormalizedProjection(..)
426 | ty::Opaque(..) => {
427 if t.flags.intersects(self.needs_canonical_flags) {
428 t.super_fold_with(self)
437 impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
438 /// The main `canonicalize` method, shared impl of
439 /// `canonicalize_query` and `canonicalize_response`.
442 infcx: Option<&InferCtxt<'_, 'gcx, 'tcx>>,
443 tcx: TyCtxt<'_, 'gcx, 'tcx>,
444 canonicalize_region_mode: &dyn CanonicalizeRegionMode,
445 query_state: &mut OriginalQueryValues<'tcx>,
446 ) -> Canonicalized<'gcx, V>
448 V: TypeFoldable<'tcx> + Lift<'gcx>,
450 let needs_canonical_flags = if canonicalize_region_mode.any() {
451 TypeFlags::KEEP_IN_LOCAL_TCX |
452 TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS`
453 TypeFlags::HAS_TY_PLACEHOLDER
455 TypeFlags::KEEP_IN_LOCAL_TCX |
456 TypeFlags::HAS_RE_PLACEHOLDER |
457 TypeFlags::HAS_TY_PLACEHOLDER
460 let gcx = tcx.global_tcx();
462 // Fast path: nothing that needs to be canonicalized.
463 if !value.has_type_flags(needs_canonical_flags) {
464 let out_value = gcx.lift(value).unwrap_or_else(|| {
466 "failed to lift `{:?}` (nothing to canonicalize)",
470 let canon_value = Canonical {
471 max_universe: ty::UniverseIndex::ROOT,
472 variables: List::empty(),
478 let mut canonicalizer = Canonicalizer {
481 canonicalize_region_mode,
482 needs_canonical_flags,
483 variables: SmallVec::new(),
485 indices: FxHashMap::default(),
486 binder_index: ty::INNERMOST,
488 let out_value = value.fold_with(&mut canonicalizer);
490 // Once we have canonicalized `out_value`, it should not
491 // contain anything that ties it to this inference context
492 // anymore, so it should live in the global arena.
493 let out_value = gcx.lift(&out_value).unwrap_or_else(|| {
495 "failed to lift `{:?}`, canonicalized from `{:?}`",
501 let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables);
503 let max_universe = canonical_variables
505 .map(|cvar| cvar.universe())
507 .unwrap_or(ty::UniverseIndex::ROOT);
511 variables: canonical_variables,
516 /// Creates a canonical variable replacing `kind` from the input,
517 /// or returns an existing variable if `kind` has already been
518 /// seen. `kind` is expected to be an unbound variable (or
519 /// potentially a free region).
520 fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> BoundVar {
528 let var_values = &mut query_state.var_values;
530 // This code is hot. `variables` and `var_values` are usually small
531 // (fewer than 8 elements ~95% of the time). They are SmallVec's to
532 // avoid allocations in those cases. We also don't use `indices` to
533 // determine if a kind has been seen before until the limit of 8 has
534 // been exceeded, to also avoid allocations for `indices`.
535 let var = if !var_values.spilled() {
536 // `var_values` is stack-allocated. `indices` isn't used yet. Do a
537 // direct linear search of `var_values`.
538 if let Some(idx) = var_values.iter().position(|&k| k == kind) {
539 // `kind` is already present in `var_values`.
542 // `kind` isn't present in `var_values`. Append it. Likewise
543 // for `info` and `variables`.
544 variables.push(info);
545 var_values.push(kind);
546 assert_eq!(variables.len(), var_values.len());
548 // If `var_values` has become big enough to be heap-allocated,
549 // fill up `indices` to facilitate subsequent lookups.
550 if var_values.spilled() {
551 assert!(indices.is_empty());
552 *indices = var_values
555 .map(|(i, &kind)| (kind, BoundVar::new(i)))
558 // The cv is the index of the appended element.
559 BoundVar::new(var_values.len() - 1)
562 // `var_values` is large. Do a hashmap search via `indices`.
563 *indices.entry(kind).or_insert_with(|| {
564 variables.push(info);
565 var_values.push(kind);
566 assert_eq!(variables.len(), var_values.len());
567 BoundVar::new(variables.len() - 1)
574 /// Shorthand helper that creates a canonical region variable for
575 /// `r` (always in the root universe). The reason that we always
576 /// put these variables into the root universe is because this
577 /// method is used during **query construction:** in that case, we
578 /// are taking all the regions and just putting them into the most
579 /// generic context we can. This may generate solutions that don't
580 /// fit (e.g., that equate some region variable with a placeholder
581 /// it can't name) on the caller side, but that's ok, the caller
582 /// can figure that out. In the meantime, it maximizes our
585 /// (This works because unification never fails -- and hence trait
586 /// selection is never affected -- due to a universe mismatch.)
587 fn canonical_var_for_region_in_root_universe(
590 ) -> ty::Region<'tcx> {
591 self.canonical_var_for_region(
593 kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
599 /// Returns the universe in which `vid` is defined.
600 fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
603 .borrow_region_constraints()
607 /// Creates a canonical variable (with the given `info`)
608 /// representing the region `r`; return a region referencing it.
609 fn canonical_var_for_region(
611 info: CanonicalVarInfo,
613 ) -> ty::Region<'tcx> {
614 let var = self.canonical_var(info, r.into());
615 let region = ty::ReLateBound(
617 ty::BoundRegion::BrAnon(var.as_u32())
619 self.tcx().mk_region(region)
622 /// Given a type variable `ty_var` of the given kind, first check
623 /// if `ty_var` is bound to anything; if so, canonicalize
624 /// *that*. Otherwise, create a new canonical variable for
626 fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo, ty_var: Ty<'tcx>) -> Ty<'tcx> {
627 let infcx = self.infcx.expect("encountered ty-var without infcx");
628 let bound_to = infcx.shallow_resolve(ty_var);
629 if bound_to != ty_var {
630 self.fold_ty(bound_to)
632 let var = self.canonical_var(info, ty_var.into());
633 self.tcx().mk_ty(ty::Bound(self.binder_index, var.into()))
637 /// Given a type variable `const_var` of the given kind, first check
638 /// if `const_var` is bound to anything; if so, canonicalize
639 /// *that*. Otherwise, create a new canonical variable for
641 fn canonicalize_const_var(
643 info: CanonicalVarInfo,
644 const_var: &'tcx ty::LazyConst<'tcx>
645 ) -> &'tcx ty::LazyConst<'tcx> {
646 let infcx = self.infcx.expect("encountered const-var without infcx");
647 let bound_to = infcx.resolve_const_var(const_var);
648 if bound_to != const_var {
649 self.fold_const(bound_to)
651 let ty = match const_var {
652 ty::LazyConst::Unevaluated(def_id, _) => {
653 self.tcx.type_of(*def_id)
655 ty::LazyConst::Evaluated(ty::Const { ty, .. }) => ty,
657 let var = self.canonical_var(info, const_var.into());
658 self.tcx().mk_lazy_const(
659 ty::LazyConst::Evaluated(ty::Const {
660 val: ConstValue::Infer(InferConst::Canonical(self.binder_index, var.into())),