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 dev guide][c].
6 //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
8 use crate::infer::canonical::{
9 Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized,
12 use crate::infer::InferCtxt;
13 use rustc_middle::ty::flags::FlagComputation;
14 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
15 use rustc_middle::ty::subst::GenericArg;
16 use rustc_middle::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags};
17 use std::sync::atomic::Ordering;
19 use rustc_data_structures::fx::FxHashMap;
20 use rustc_index::vec::Idx;
21 use smallvec::SmallVec;
23 impl<'cx, 'tcx> InferCtxt<'cx, '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 dev guide][c].
38 /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query
39 pub fn canonicalize_query<V>(
42 query_state: &mut OriginalQueryValues<'tcx>,
43 ) -> Canonicalized<'tcx, V>
45 V: TypeFoldable<'tcx>,
47 self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
49 Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state)
52 /// Canonicalizes a query *response* `V`. When we canonicalize a
53 /// query response, we only canonicalize unbound inference
54 /// variables, and we leave other free regions alone. So,
55 /// continuing with the example from `canonicalize_query`, if
56 /// there was an input query `T: Trait<'static>`, it would have
57 /// been canonicalized to
63 /// with a mapping M that maps `'?0` to `'static`. But if we found that there
64 /// exists only one possible impl of `Trait`, and it looks like
66 /// impl<T> Trait<'static> for T { .. }
68 /// then we would prepare a query result R that (among other
69 /// things) includes a mapping to `'?0 := 'static`. When
70 /// canonicalizing this query result R, we would leave this
71 /// reference to `'static` alone.
73 /// To get a good understanding of what is happening here, check
74 /// out the [chapter in the rustc dev guide][c].
76 /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query-result
77 pub fn canonicalize_response<V>(&self, value: V) -> Canonicalized<'tcx, V>
79 V: TypeFoldable<'tcx>,
81 let mut query_state = OriginalQueryValues::default();
82 Canonicalizer::canonicalize(
86 &CanonicalizeQueryResponse,
91 pub fn canonicalize_user_type_annotation<V>(&self, value: V) -> Canonicalized<'tcx, V>
93 V: TypeFoldable<'tcx>,
95 let mut query_state = OriginalQueryValues::default();
96 Canonicalizer::canonicalize(
100 &CanonicalizeUserTypeAnnotation,
105 /// A hacky variant of `canonicalize_query` that does not
106 /// canonicalize `'static`. Unfortunately, the existing leak
107 /// check treats `'static` differently in some cases (see also
108 /// #33684), so if we are performing an operation that may need to
109 /// prove "leak-check" related things, we leave `'static`
112 /// `'static` is also special cased when winnowing candidates when
113 /// selecting implementation candidates, so we also have to leave `'static`
114 /// alone for queries that do selection.
116 // FIXME(#48536): once the above issues are resolved, we can remove this
117 // and just use `canonicalize_query`.
118 pub fn canonicalize_hr_query_hack<V>(
121 query_state: &mut OriginalQueryValues<'tcx>,
122 ) -> Canonicalized<'tcx, V>
124 V: TypeFoldable<'tcx>,
126 self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
128 Canonicalizer::canonicalize(
132 &CanonicalizeFreeRegionsOtherThanStatic,
138 /// Controls how we canonicalize "free regions" that are not inference
139 /// variables. This depends on what we are canonicalizing *for* --
140 /// e.g., if we are canonicalizing to create a query, we want to
141 /// replace those with inference variables, since we want to make a
142 /// maximally general query. But if we are canonicalizing a *query
143 /// response*, then we don't typically replace free regions, as they
144 /// must have been introduced from other parts of the system.
145 trait CanonicalizeRegionMode {
146 fn canonicalize_free_region(
148 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
150 ) -> ty::Region<'tcx>;
152 fn any(&self) -> bool;
155 struct CanonicalizeQueryResponse;
157 impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
158 fn canonicalize_free_region(
160 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
162 ) -> ty::Region<'tcx> {
167 | ty::ReEmpty(ty::UniverseIndex::ROOT)
168 | ty::ReEarlyBound(..) => r,
170 ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
171 CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(*placeholder) },
176 let universe = canonicalizer.region_var_universe(*vid);
177 canonicalizer.canonical_var_for_region(
178 CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) },
184 bug!("canonicalizing 'empty in universe {:?}", ui) // FIXME
188 // Other than `'static` or `'empty`, the query
189 // response should be executing in a fully
190 // canonicalized environment, so there shouldn't be
191 // any other region names it can come up.
193 // rust-lang/rust#57464: `impl Trait` can leak local
194 // scopes (in manner violating typeck). Therefore, use
195 // `delay_span_bug` to allow type error over an ICE.
196 ty::tls::with(|tcx| {
197 tcx.sess.delay_span_bug(
198 rustc_span::DUMMY_SP,
199 &format!("unexpected region in query response: `{:?}`", r),
207 fn any(&self) -> bool {
212 struct CanonicalizeUserTypeAnnotation;
214 impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
215 fn canonicalize_free_region(
217 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
219 ) -> ty::Region<'tcx> {
221 ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReStatic => r,
222 ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
224 // We only expect region names that the user can type.
225 bug!("unexpected region in query response: `{:?}`", r)
230 fn any(&self) -> bool {
235 struct CanonicalizeAllFreeRegions;
237 impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions {
238 fn canonicalize_free_region(
240 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
242 ) -> ty::Region<'tcx> {
243 canonicalizer.canonical_var_for_region_in_root_universe(r)
246 fn any(&self) -> bool {
251 struct CanonicalizeFreeRegionsOtherThanStatic;
253 impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
254 fn canonicalize_free_region(
256 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
258 ) -> ty::Region<'tcx> {
259 if let ty::ReStatic = r {
262 canonicalizer.canonical_var_for_region_in_root_universe(r)
266 fn any(&self) -> bool {
271 struct Canonicalizer<'cx, 'tcx> {
272 infcx: &'cx InferCtxt<'cx, 'tcx>,
274 variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>,
275 query_state: &'cx mut OriginalQueryValues<'tcx>,
276 // Note that indices is only used once `var_values` is big enough to be
278 indices: FxHashMap<GenericArg<'tcx>, BoundVar>,
279 canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode,
280 needs_canonical_flags: TypeFlags,
282 binder_index: ty::DebruijnIndex,
285 impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
286 fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
290 fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T>
292 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");
311 let resolved_vid = self
315 .unwrap_region_constraints()
316 .opportunistic_resolve_var(vid);
318 "canonical: region var found with vid {:?}, \
319 opportunistically resolved to {:?}",
322 let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid));
323 self.canonicalize_region_mode.canonicalize_free_region(self, r)
327 | ty::ReEarlyBound(..)
330 | ty::RePlaceholder(..)
331 | ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r),
335 fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
337 ty::Infer(ty::TyVar(vid)) => {
338 debug!("canonical: type var found with vid {:?}", vid);
339 match self.infcx.probe_ty_var(vid) {
340 // `t` could be a float / int variable; canonicalize that instead.
342 debug!("(resolved to {:?})", t);
346 // `TyVar(vid)` is unresolved, track its universe index in the canonicalized
349 // FIXME: perf problem described in #55921.
350 ui = ty::UniverseIndex::ROOT;
351 self.canonicalize_ty_var(
353 kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
361 ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var(
362 CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) },
366 ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var(
367 CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) },
371 ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
372 bug!("encountered a fresh type during canonicalization")
375 ty::Placeholder(placeholder) => self.canonicalize_ty_var(
376 CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) },
380 ty::Bound(debruijn, _) => {
381 if debruijn >= self.binder_index {
382 bug!("escaping bound type during canonicalization")
390 | ty::GeneratorWitness(..)
411 | ty::Opaque(..) => {
412 if t.flags().intersects(self.needs_canonical_flags) {
413 t.super_fold_with(self)
421 fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
423 ty::ConstKind::Infer(InferConst::Var(vid)) => {
424 debug!("canonical: const var found with vid {:?}", vid);
425 match self.infcx.probe_const_var(vid) {
427 debug!("(resolved to {:?})", c);
428 return self.fold_const(c);
431 // `ConstVar(vid)` is unresolved, track its universe index in the
432 // canonicalized result
434 // FIXME: perf problem described in #55921.
435 ui = ty::UniverseIndex::ROOT;
436 return self.canonicalize_const_var(
437 CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) },
443 ty::ConstKind::Infer(InferConst::Fresh(_)) => {
444 bug!("encountered a fresh const during canonicalization")
446 ty::ConstKind::Bound(debruijn, _) => {
447 if debruijn >= self.binder_index {
448 bug!("escaping bound type during canonicalization")
453 ty::ConstKind::Placeholder(placeholder) => {
454 return self.canonicalize_const_var(
455 CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderConst(placeholder) },
462 let flags = FlagComputation::for_const(ct);
463 if flags.intersects(self.needs_canonical_flags) { ct.super_fold_with(self) } else { ct }
467 impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
468 /// The main `canonicalize` method, shared impl of
469 /// `canonicalize_query` and `canonicalize_response`.
472 infcx: &InferCtxt<'_, 'tcx>,
474 canonicalize_region_mode: &dyn CanonicalizeRegionMode,
475 query_state: &mut OriginalQueryValues<'tcx>,
476 ) -> Canonicalized<'tcx, V>
478 V: TypeFoldable<'tcx>,
480 let needs_canonical_flags = if canonicalize_region_mode.any() {
481 TypeFlags::NEEDS_INFER |
482 TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS`
483 TypeFlags::HAS_TY_PLACEHOLDER |
484 TypeFlags::HAS_CT_PLACEHOLDER
486 TypeFlags::NEEDS_INFER
487 | TypeFlags::HAS_RE_PLACEHOLDER
488 | TypeFlags::HAS_TY_PLACEHOLDER
489 | TypeFlags::HAS_CT_PLACEHOLDER
492 // Fast path: nothing that needs to be canonicalized.
493 if !value.has_type_flags(needs_canonical_flags) {
494 let canon_value = Canonical {
495 max_universe: ty::UniverseIndex::ROOT,
496 variables: List::empty(),
502 let mut canonicalizer = Canonicalizer {
505 canonicalize_region_mode,
506 needs_canonical_flags,
507 variables: SmallVec::new(),
509 indices: FxHashMap::default(),
510 binder_index: ty::INNERMOST,
512 let out_value = value.fold_with(&mut canonicalizer);
514 // Once we have canonicalized `out_value`, it should not
515 // contain anything that ties it to this inference context
516 // anymore, so it should live in the global arena.
517 debug_assert!(!out_value.needs_infer());
519 let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables);
521 let max_universe = canonical_variables
523 .map(|cvar| cvar.universe())
525 .unwrap_or(ty::UniverseIndex::ROOT);
527 Canonical { max_universe, variables: canonical_variables, value: out_value }
530 /// Creates a canonical variable replacing `kind` from the input,
531 /// or returns an existing variable if `kind` has already been
532 /// seen. `kind` is expected to be an unbound variable (or
533 /// potentially a free region).
534 fn canonical_var(&mut self, info: CanonicalVarInfo<'tcx>, kind: GenericArg<'tcx>) -> BoundVar {
535 let Canonicalizer { variables, query_state, indices, .. } = self;
537 let var_values = &mut query_state.var_values;
539 // This code is hot. `variables` and `var_values` are usually small
540 // (fewer than 8 elements ~95% of the time). They are SmallVec's to
541 // avoid allocations in those cases. We also don't use `indices` to
542 // determine if a kind has been seen before until the limit of 8 has
543 // been exceeded, to also avoid allocations for `indices`.
544 if !var_values.spilled() {
545 // `var_values` is stack-allocated. `indices` isn't used yet. Do a
546 // direct linear search of `var_values`.
547 if let Some(idx) = var_values.iter().position(|&k| k == kind) {
548 // `kind` is already present in `var_values`.
551 // `kind` isn't present in `var_values`. Append it. Likewise
552 // for `info` and `variables`.
553 variables.push(info);
554 var_values.push(kind);
555 assert_eq!(variables.len(), var_values.len());
557 // If `var_values` has become big enough to be heap-allocated,
558 // fill up `indices` to facilitate subsequent lookups.
559 if var_values.spilled() {
560 assert!(indices.is_empty());
561 *indices = var_values
564 .map(|(i, &kind)| (kind, BoundVar::new(i)))
567 // The cv is the index of the appended element.
568 BoundVar::new(var_values.len() - 1)
571 // `var_values` is large. Do a hashmap search via `indices`.
572 *indices.entry(kind).or_insert_with(|| {
573 variables.push(info);
574 var_values.push(kind);
575 assert_eq!(variables.len(), var_values.len());
576 BoundVar::new(variables.len() - 1)
581 /// Shorthand helper that creates a canonical region variable for
582 /// `r` (always in the root universe). The reason that we always
583 /// put these variables into the root universe is because this
584 /// method is used during **query construction:** in that case, we
585 /// are taking all the regions and just putting them into the most
586 /// generic context we can. This may generate solutions that don't
587 /// fit (e.g., that equate some region variable with a placeholder
588 /// it can't name) on the caller side, but that's ok, the caller
589 /// can figure that out. In the meantime, it maximizes our
592 /// (This works because unification never fails -- and hence trait
593 /// selection is never affected -- due to a universe mismatch.)
594 fn canonical_var_for_region_in_root_universe(
597 ) -> ty::Region<'tcx> {
598 self.canonical_var_for_region(
599 CanonicalVarInfo { kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT) },
604 /// Returns the universe in which `vid` is defined.
605 fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
606 self.infcx.inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
609 /// Creates a canonical variable (with the given `info`)
610 /// representing the region `r`; return a region referencing it.
611 fn canonical_var_for_region(
613 info: CanonicalVarInfo<'tcx>,
615 ) -> ty::Region<'tcx> {
616 let var = self.canonical_var(info, r.into());
617 let br = ty::BoundRegion { var, kind: ty::BrAnon(var.as_u32()) };
618 let region = ty::ReLateBound(self.binder_index, br);
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<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> {
627 let infcx = self.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<'tcx>,
644 const_var: &'tcx ty::Const<'tcx>,
645 ) -> &'tcx ty::Const<'tcx> {
646 let infcx = self.infcx;
647 let bound_to = infcx.shallow_resolve(const_var);
648 if bound_to != const_var {
649 self.fold_const(bound_to)
651 let var = self.canonical_var(info, const_var.into());
652 self.tcx().mk_const(ty::Const {
653 val: ty::ConstKind::Bound(self.binder_index, var),
654 ty: self.fold_ty(const_var.ty),