1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! This module contains the "canonicalizer" itself.
13 //! For an overview of what canonicaliation is and how it fits into
14 //! rustc, check out the [chapter in the rustc guide][c].
16 //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html
18 use infer::canonical::{
19 Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized,
23 use std::sync::atomic::Ordering;
24 use ty::fold::{TypeFoldable, TypeFolder};
26 use ty::{self, BoundTy, BoundTyIndex, Lift, List, Ty, TyCtxt, TypeFlags};
28 use rustc_data_structures::fx::FxHashMap;
29 use rustc_data_structures::indexed_vec::Idx;
30 use smallvec::SmallVec;
32 impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
33 /// Canonicalizes a query value `V`. When we canonicalize a query,
34 /// we not only canonicalize unbound inference variables, but we
35 /// *also* replace all free regions whatsoever. So for example a
36 /// query like `T: Trait<'static>` would be canonicalized to
42 /// with a mapping M that maps `'?0` to `'static`.
44 /// To get a good understanding of what is happening here, check
45 /// out the [chapter in the rustc guide][c].
47 /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query
48 pub fn canonicalize_query<V>(
51 query_state: &mut OriginalQueryValues<'tcx>,
52 ) -> Canonicalized<'gcx, V>
54 V: TypeFoldable<'tcx> + Lift<'gcx>,
59 .queries_canonicalized
60 .fetch_add(1, Ordering::Relaxed);
62 Canonicalizer::canonicalize(
66 &CanonicalizeAllFreeRegions,
71 /// Canonicalizes a query *response* `V`. When we canonicalize a
72 /// query response, we only canonicalize unbound inference
73 /// variables, and we leave other free regions alone. So,
74 /// continuing with the example from `canonicalize_query`, if
75 /// there was an input query `T: Trait<'static>`, it would have
76 /// been canonicalized to
82 /// with a mapping M that maps `'?0` to `'static`. But if we found that there
83 /// exists only one possible impl of `Trait`, and it looks like
85 /// impl<T> Trait<'static> for T { .. }
87 /// then we would prepare a query result R that (among other
88 /// things) includes a mapping to `'?0 := 'static`. When
89 /// canonicalizing this query result R, we would leave this
90 /// reference to `'static` alone.
92 /// To get a good understanding of what is happening here, check
93 /// out the [chapter in the rustc guide][c].
95 /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query-result
96 pub fn canonicalize_response<V>(&self, value: &V) -> Canonicalized<'gcx, V>
98 V: TypeFoldable<'tcx> + Lift<'gcx>,
100 let mut query_state = OriginalQueryValues::default();
101 Canonicalizer::canonicalize(
105 &CanonicalizeQueryResponse,
110 pub fn canonicalize_user_type_annotation<V>(&self, value: &V) -> Canonicalized<'gcx, V>
112 V: TypeFoldable<'tcx> + Lift<'gcx>,
114 let mut query_state = OriginalQueryValues::default();
115 Canonicalizer::canonicalize(
119 &CanonicalizeUserTypeAnnotation,
124 /// A hacky variant of `canonicalize_query` that does not
125 /// canonicalize `'static`. Unfortunately, the existing leak
126 /// check treaks `'static` differently in some cases (see also
127 /// #33684), so if we are performing an operation that may need to
128 /// prove "leak-check" related things, we leave `'static`
131 /// FIXME(#48536) -- once we have universes, we can remove this and just use
132 /// `canonicalize_query`.
133 pub fn canonicalize_hr_query_hack<V>(
136 query_state: &mut OriginalQueryValues<'tcx>,
137 ) -> Canonicalized<'gcx, V>
139 V: TypeFoldable<'tcx> + Lift<'gcx>,
144 .queries_canonicalized
145 .fetch_add(1, Ordering::Relaxed);
147 Canonicalizer::canonicalize(
151 &CanonicalizeFreeRegionsOtherThanStatic,
157 /// Controls how we canonicalize "free regions" that are not inference
158 /// variables. This depends on what we are canonicalizing *for* --
159 /// e.g., if we are canonicalizing to create a query, we want to
160 /// replace those with inference variables, since we want to make a
161 /// maximally general query. But if we are canonicalizing a *query
162 /// response*, then we don't typically replace free regions, as they
163 /// must have been introduced from other parts of the system.
164 trait CanonicalizeRegionMode {
165 fn canonicalize_free_region(
167 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
169 ) -> ty::Region<'tcx>;
171 fn any(&self) -> bool;
174 struct CanonicalizeQueryResponse;
176 impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
177 fn canonicalize_free_region(
179 canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>,
181 ) -> ty::Region<'tcx> {
183 ty::ReFree(_) | ty::ReEmpty | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r,
184 ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
186 kind: CanonicalVarKind::PlaceholderRegion(*placeholder),
191 let universe = canonicalizer.region_var_universe(*vid);
192 canonicalizer.canonical_var_for_region(
194 kind: CanonicalVarKind::Region(universe),
200 // Other than `'static` or `'empty`, the query
201 // response should be executing in a fully
202 // canonicalized environment, so there shouldn't be
203 // any other region names it can come up.
204 bug!("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>, BoundTyIndex>,
281 canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode,
282 needs_canonical_flags: TypeFlags,
285 impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> {
286 fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> {
290 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
292 ty::ReLateBound(..) => {
293 // leave bound regions alone
300 .borrow_region_constraints()
301 .opportunistic_resolve_var(self.tcx, vid);
303 "canonical: region var found with vid {:?}, \
304 opportunistically resolved to {:?}",
307 self.canonicalize_region_mode
308 .canonicalize_free_region(self, r)
312 | ty::ReEarlyBound(..)
315 | ty::RePlaceholder(..)
317 | ty::ReErased => self.canonicalize_region_mode
318 .canonicalize_free_region(self, r),
320 ty::ReClosureBound(..) | ty::ReCanonical(_) => {
321 bug!("canonical region encountered during canonicalization")
326 fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
328 ty::Infer(ty::TyVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::General, t),
330 ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Int, t),
332 ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Float, t),
334 ty::Infer(ty::FreshTy(_))
335 | ty::Infer(ty::FreshIntTy(_))
336 | ty::Infer(ty::FreshFloatTy(_)) => {
337 bug!("encountered a fresh type during canonicalization")
341 bug!("encountered a bound type during canonicalization")
346 | ty::GeneratorWitness(..)
365 | ty::UnnormalizedProjection(..)
368 | ty::Opaque(..) => {
369 if t.flags.intersects(self.needs_canonical_flags) {
370 t.super_fold_with(self)
379 impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
380 /// The main `canonicalize` method, shared impl of
381 /// `canonicalize_query` and `canonicalize_response`.
384 infcx: Option<&InferCtxt<'_, 'gcx, 'tcx>>,
385 tcx: TyCtxt<'_, 'gcx, 'tcx>,
386 canonicalize_region_mode: &dyn CanonicalizeRegionMode,
387 query_state: &mut OriginalQueryValues<'tcx>,
388 ) -> Canonicalized<'gcx, V>
390 V: TypeFoldable<'tcx> + Lift<'gcx>,
393 !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS),
394 "canonicalizing a canonical value: {:?}",
398 let needs_canonical_flags = if canonicalize_region_mode.any() {
399 TypeFlags::HAS_FREE_REGIONS | TypeFlags::KEEP_IN_LOCAL_TCX
401 TypeFlags::KEEP_IN_LOCAL_TCX
404 let gcx = tcx.global_tcx();
406 // Fast path: nothing that needs to be canonicalized.
407 if !value.has_type_flags(needs_canonical_flags) {
408 let out_value = gcx.lift(value).unwrap();
409 let canon_value = Canonical {
410 max_universe: ty::UniverseIndex::ROOT,
411 variables: List::empty(),
417 let mut canonicalizer = Canonicalizer {
420 canonicalize_region_mode,
421 needs_canonical_flags,
422 variables: SmallVec::new(),
424 indices: FxHashMap::default(),
426 let out_value = value.fold_with(&mut canonicalizer);
428 // Once we have canonicalized `out_value`, it should not
429 // contain anything that ties it to this inference context
430 // anymore, so it should live in the global arena.
431 let out_value = gcx.lift(&out_value).unwrap_or_else(|| {
433 "failed to lift `{:?}`, canonicalized from `{:?}`",
439 let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables);
441 let max_universe = canonical_variables
443 .map(|cvar| cvar.universe())
445 .unwrap_or(ty::UniverseIndex::ROOT);
449 variables: canonical_variables,
454 /// Creates a canonical variable replacing `kind` from the input,
455 /// or returns an existing variable if `kind` has already been
456 /// seen. `kind` is expected to be an unbound variable (or
457 /// potentially a free region).
458 fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> BoundTyIndex {
466 let var_values = &mut query_state.var_values;
468 // This code is hot. `variables` and `var_values` are usually small
469 // (fewer than 8 elements ~95% of the time). They are SmallVec's to
470 // avoid allocations in those cases. We also don't use `indices` to
471 // determine if a kind has been seen before until the limit of 8 has
472 // been exceeded, to also avoid allocations for `indices`.
473 let var = if !var_values.spilled() {
474 // `var_values` is stack-allocated. `indices` isn't used yet. Do a
475 // direct linear search of `var_values`.
476 if let Some(idx) = var_values.iter().position(|&k| k == kind) {
477 // `kind` is already present in `var_values`.
478 BoundTyIndex::new(idx)
480 // `kind` isn't present in `var_values`. Append it. Likewise
481 // for `info` and `variables`.
482 variables.push(info);
483 var_values.push(kind);
484 assert_eq!(variables.len(), var_values.len());
486 // If `var_values` has become big enough to be heap-allocated,
487 // fill up `indices` to facilitate subsequent lookups.
488 if var_values.spilled() {
489 assert!(indices.is_empty());
490 *indices = var_values
493 .map(|(i, &kind)| (kind, BoundTyIndex::new(i)))
496 // The cv is the index of the appended element.
497 BoundTyIndex::new(var_values.len() - 1)
500 // `var_values` is large. Do a hashmap search via `indices`.
501 *indices.entry(kind).or_insert_with(|| {
502 variables.push(info);
503 var_values.push(kind);
504 assert_eq!(variables.len(), var_values.len());
505 BoundTyIndex::new(variables.len() - 1)
512 /// Shorthand helper that creates a canonical region variable for
513 /// `r` (always in the root universe). The reason that we always
514 /// put these variables into the root universe is because this
515 /// method is used during **query construction:** in that case, we
516 /// are taking all the regions and just putting them into the most
517 /// generic context we can. This may generate solutions that don't
518 /// fit (e.g., that equate some region variable with a placeholder
519 /// it can't name) on the caller side, but that's ok, the caller
520 /// can figure that out. In the meantime, it maximizes our
523 /// (This works because unification never fails -- and hence trait
524 /// selection is never affected -- due to a universe mismatch.)
525 fn canonical_var_for_region_in_root_universe(
528 ) -> ty::Region<'tcx> {
529 self.canonical_var_for_region(
531 kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
537 /// Returns the universe in which `vid` is defined.
538 fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
541 .borrow_region_constraints()
545 /// Create a canonical variable (with the given `info`)
546 /// representing the region `r`; return a region referencing it.
547 fn canonical_var_for_region(
549 info: CanonicalVarInfo,
551 ) -> ty::Region<'tcx> {
552 let var = self.canonical_var(info, r.into());
553 self.tcx().mk_region(ty::ReCanonical(var))
556 /// Given a type variable `ty_var` of the given kind, first check
557 /// if `ty_var` is bound to anything; if so, canonicalize
558 /// *that*. Otherwise, create a new canonical variable for
560 fn canonicalize_ty_var(&mut self, ty_kind: CanonicalTyVarKind, ty_var: Ty<'tcx>) -> Ty<'tcx> {
561 let infcx = self.infcx.expect("encountered ty-var without infcx");
562 let bound_to = infcx.shallow_resolve(ty_var);
563 if bound_to != ty_var {
564 self.fold_ty(bound_to)
566 let info = CanonicalVarInfo {
567 kind: CanonicalVarKind::Ty(ty_kind),
569 let var = self.canonical_var(info, ty_var.into());
570 self.tcx().mk_ty(ty::Bound(BoundTy::new(ty::INNERMOST, var)))