1//! This module contains the "canonicalizer" itself.
2//!
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].
5//!
6//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
78use rustc_data_structures::fx::FxHashMap;
9use rustc_data_structures::sso::SsoHashMap;
10use rustc_index::Idx;
11use rustc_middle::bug;
12use rustc_middle::ty::{
13self, BoundVar, GenericArg, InferConst, List, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeFolder,
14TypeSuperFoldable, TypeVisitableExt,
15};
16use smallvec::SmallVec;
17use tracing::debug;
1819use crate::infer::InferCtxt;
20use crate::infer::canonical::{
21Canonical, CanonicalQueryInput, CanonicalVarKind, OriginalQueryValues,
22};
2324impl<'tcx> InferCtxt<'tcx> {
25/// Canonicalizes a query value `V`. When we canonicalize a query,
26 /// we not only canonicalize unbound inference variables, but we
27 /// *also* replace all free regions whatsoever. So for example a
28 /// query like `T: Trait<'static>` would be canonicalized to
29 ///
30 /// ```text
31 /// T: Trait<'?0>
32 /// ```
33 ///
34 /// with a mapping M that maps `'?0` to `'static`.
35 ///
36 /// To get a good understanding of what is happening here, check
37 /// out the [chapter in the rustc dev guide][c].
38 ///
39 /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query
40pub 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>>
45where
46V: TypeFoldable<TyCtxt<'tcx>>,
47 {
48let ty::ParamEnvAnd { param_env, value } = value;
49let canonical_param_env = self.tcx.canonical_param_env_cache.get_or_insert(
50self.tcx,
51param_env,
52query_state,
53 |tcx, param_env, query_state| {
54// FIXME(#118965): We don't canonicalize the static lifetimes that appear in the
55 // `param_env` because they are treated differently by trait selection.
56Canonicalizer::canonicalize(
57param_env,
58None,
59tcx,
60&CanonicalizeFreeRegionsOtherThanStatic,
61query_state,
62 )
63 },
64 );
6566let canonical = Canonicalizer::canonicalize_with_base(
67canonical_param_env,
68value,
69Some(self),
70self.tcx,
71&CanonicalizeAllFreeRegions,
72query_state,
73 )
74 .unchecked_map(|(param_env, value)| param_env.and(value));
75CanonicalQueryInput { canonical, typing_mode: self.typing_mode() }
76 }
7778/// Canonicalizes a query *response* `V`. When we canonicalize a
79 /// query response, we only canonicalize unbound inference
80 /// variables, and we leave other free regions alone. So,
81 /// continuing with the example from `canonicalize_query`, if
82 /// there was an input query `T: Trait<'static>`, it would have
83 /// been canonicalized to
84 ///
85 /// ```text
86 /// T: Trait<'?0>
87 /// ```
88 ///
89 /// with a mapping M that maps `'?0` to `'static`. But if we found that there
90 /// exists only one possible impl of `Trait`, and it looks like
91 /// ```ignore (illustrative)
92 /// impl<T> Trait<'static> for T { .. }
93 /// ```
94 /// then we would prepare a query result R that (among other
95 /// things) includes a mapping to `'?0 := 'static`. When
96 /// canonicalizing this query result R, we would leave this
97 /// reference to `'static` alone.
98 ///
99 /// To get a good understanding of what is happening here, check
100 /// out the [chapter in the rustc dev guide][c].
101 ///
102 /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query-result
103pub fn canonicalize_response<V>(&self, value: V) -> Canonical<'tcx, V>
104where
105V: TypeFoldable<TyCtxt<'tcx>>,
106 {
107let mut query_state = OriginalQueryValues::default();
108Canonicalizer::canonicalize(
109value,
110Some(self),
111self.tcx,
112&CanonicalizeQueryResponse,
113&mut query_state,
114 )
115 }
116117pub fn canonicalize_user_type_annotation<V>(&self, value: V) -> Canonical<'tcx, V>
118where
119V: TypeFoldable<TyCtxt<'tcx>>,
120 {
121let mut query_state = OriginalQueryValues::default();
122Canonicalizer::canonicalize(
123value,
124Some(self),
125self.tcx,
126&CanonicalizeUserTypeAnnotation,
127&mut query_state,
128 )
129 }
130}
131132/// Controls how we canonicalize "free regions" that are not inference
133/// variables. This depends on what we are canonicalizing *for* --
134/// e.g., if we are canonicalizing to create a query, we want to
135/// replace those with inference variables, since we want to make a
136/// maximally general query. But if we are canonicalizing a *query
137/// response*, then we don't typically replace free regions, as they
138/// must have been introduced from other parts of the system.
139trait CanonicalizeMode {
140fn canonicalize_free_region<'tcx>(
141&self,
142 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
143 r: ty::Region<'tcx>,
144 ) -> ty::Region<'tcx>;
145146fn any(&self) -> bool;
147148// Do we preserve universe of variables.
149fn preserve_universes(&self) -> bool;
150}
151152struct CanonicalizeQueryResponse;
153154impl CanonicalizeModefor CanonicalizeQueryResponse {
155fn canonicalize_free_region<'tcx>(
156&self,
157 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
158mut r: ty::Region<'tcx>,
159 ) -> ty::Region<'tcx> {
160let infcx = canonicalizer.infcx.unwrap();
161162if let ty::ReVar(vid) = r.kind() {
163r = infcx164 .inner
165 .borrow_mut()
166 .unwrap_region_constraints()
167 .opportunistic_resolve_var(canonicalizer.tcx, vid);
168{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_infer/src/infer/canonical/canonicalizer.rs:168",
"rustc_infer::infer::canonical::canonicalizer",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/infer/canonical/canonicalizer.rs"),
::tracing_core::__macro_support::Option::Some(168u32),
::tracing_core::__macro_support::Option::Some("rustc_infer::infer::canonical::canonicalizer"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("canonical: region var found with vid {0:?}, opportunistically resolved to {1:?}",
vid, r) as &dyn Value))])
});
} else { ; }
};debug!(
169"canonical: region var found with vid {vid:?}, \
170 opportunistically resolved to {r:?}",
171 );
172 };
173174match r.kind() {
175 ty::ReLateParam(_) | ty::ReErased | ty::ReStatic | ty::ReEarlyParam(..) => r,
176177 ty::RePlaceholder(placeholder) => canonicalizer178 .canonical_var_for_region(CanonicalVarKind::PlaceholderRegion(placeholder), r),
179180 ty::ReVar(vid) => {
181let universe = infcx182 .inner
183 .borrow_mut()
184 .unwrap_region_constraints()
185 .probe_value(vid)
186 .unwrap_err();
187canonicalizer.canonical_var_for_region(CanonicalVarKind::Region(universe), r)
188 }
189190_ => {
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.
195 //
196 // rust-lang/rust#57464: `impl Trait` can leak local
197 // scopes (in manner violating typeck). Therefore, use
198 // `delayed_bug` to allow type error over an ICE.
199canonicalizer200 .tcx
201 .dcx()
202 .delayed_bug(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("unexpected region in query response: `{0:?}`",
r))
})format!("unexpected region in query response: `{r:?}`"));
203r204 }
205 }
206 }
207208fn any(&self) -> bool {
209false
210}
211212fn preserve_universes(&self) -> bool {
213true
214}
215}
216217struct CanonicalizeUserTypeAnnotation;
218219impl CanonicalizeModefor CanonicalizeUserTypeAnnotation {
220fn canonicalize_free_region<'tcx>(
221&self,
222 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
223 r: ty::Region<'tcx>,
224 ) -> ty::Region<'tcx> {
225match r.kind() {
226 ty::ReEarlyParam(_)
227 | ty::ReLateParam(_)
228 | ty::ReErased229 | ty::ReStatic230 | ty::ReError(_) => r,
231 ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
232 ty::RePlaceholder(..) | ty::ReBound(..) => {
233// We only expect region names that the user can type.
234::rustc_middle::util::bug::bug_fmt(format_args!("unexpected region in query response: `{0:?}`",
r))bug!("unexpected region in query response: `{:?}`", r)235 }
236 }
237 }
238239fn any(&self) -> bool {
240false
241}
242243fn preserve_universes(&self) -> bool {
244false
245}
246}
247248struct CanonicalizeAllFreeRegions;
249250impl CanonicalizeModefor CanonicalizeAllFreeRegions {
251fn canonicalize_free_region<'tcx>(
252&self,
253 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
254 r: ty::Region<'tcx>,
255 ) -> ty::Region<'tcx> {
256canonicalizer.canonical_var_for_region_in_root_universe(r)
257 }
258259fn any(&self) -> bool {
260true
261}
262263fn preserve_universes(&self) -> bool {
264false
265}
266}
267268struct CanonicalizeFreeRegionsOtherThanStatic;
269270impl CanonicalizeModefor CanonicalizeFreeRegionsOtherThanStatic {
271fn canonicalize_free_region<'tcx>(
272&self,
273 canonicalizer: &mut Canonicalizer<'_, 'tcx>,
274 r: ty::Region<'tcx>,
275 ) -> ty::Region<'tcx> {
276if r.is_static() { r } else { canonicalizer.canonical_var_for_region_in_root_universe(r) }
277 }
278279fn any(&self) -> bool {
280true
281}
282283fn preserve_universes(&self) -> bool {
284false
285}
286}
287288struct Canonicalizer<'cx, 'tcx> {
289/// Set to `None` to disable the resolution of inference variables.
290infcx: Option<&'cx InferCtxt<'tcx>>,
291 tcx: TyCtxt<'tcx>,
292 var_kinds: SmallVec<[CanonicalVarKind<'tcx>; 8]>,
293 query_state: &'cx mut OriginalQueryValues<'tcx>,
294// Note that indices is only used once `var_values` is big enough to be
295 // heap-allocated.
296indices: FxHashMap<GenericArg<'tcx>, BoundVar>,
297/// Maps each `sub_unification_table_root_var` to the index of the first
298 /// variable which used it.
299 ///
300 /// This means in case two type variables have the same sub relations root,
301 /// we set the `sub_root` of the second variable to the position of the first.
302 /// Otherwise the `sub_root` of each type variable is just its own position.
303sub_root_lookup_table: SsoHashMap<ty::TyVid, usize>,
304 canonicalize_mode: &'cx dyn CanonicalizeMode,
305 needs_canonical_flags: TypeFlags,
306}
307308impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
309fn cx(&self) -> TyCtxt<'tcx> {
310self.tcx
311 }
312313fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
314match r.kind() {
315 ty::ReBound(ty::BoundVarIndexKind::Bound(_), ..) => r,
316317 ty::ReBound(ty::BoundVarIndexKind::Canonical, _) => {
318::rustc_middle::util::bug::bug_fmt(format_args!("canonicalized bound var found during canonicalization"));bug!("canonicalized bound var found during canonicalization");
319 }
320321 ty::ReStatic322 | 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 }
330331fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
332match *t.kind() {
333 ty::Infer(ty::TyVar(mut vid)) => {
334// We need to canonicalize the *root* of our ty var.
335 // This is so that our canonical response correctly reflects
336 // any equated inference vars correctly!
337let root_vid = self.infcx.unwrap().root_var(vid);
338if root_vid != vid {
339t = Ty::new_var(self.tcx, root_vid);
340vid = root_vid;
341 }
342343{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_infer/src/infer/canonical/canonicalizer.rs:343",
"rustc_infer::infer::canonical::canonicalizer",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/infer/canonical/canonicalizer.rs"),
::tracing_core::__macro_support::Option::Some(343u32),
::tracing_core::__macro_support::Option::Some("rustc_infer::infer::canonical::canonicalizer"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("canonical: type var found with vid {0:?}",
vid) as &dyn Value))])
});
} else { ; }
};debug!("canonical: type var found with vid {:?}", vid);
344match self.infcx.unwrap().probe_ty_var(vid) {
345// `t` could be a float / int variable; canonicalize that instead.
346Ok(t) => {
347{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_infer/src/infer/canonical/canonicalizer.rs:347",
"rustc_infer::infer::canonical::canonicalizer",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/infer/canonical/canonicalizer.rs"),
::tracing_core::__macro_support::Option::Some(347u32),
::tracing_core::__macro_support::Option::Some("rustc_infer::infer::canonical::canonicalizer"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("(resolved to {0:?})",
t) as &dyn Value))])
});
} else { ; }
};debug!("(resolved to {:?})", t);
348self.fold_ty(t)
349 }
350351// `TyVar(vid)` is unresolved, track its universe index in the canonicalized
352 // result.
353Err(mut ui) => {
354if !self.canonicalize_mode.preserve_universes() {
355// FIXME: perf problem described in #55921.
356ui = ty::UniverseIndex::ROOT;
357 }
358let sub_root = self.get_or_insert_sub_root(vid);
359self.canonicalize_ty_var(CanonicalVarKind::Ty { ui, sub_root }, t)
360 }
361 }
362 }
363364 ty::Infer(ty::IntVar(vid)) => {
365let nt = self.infcx.unwrap().opportunistic_resolve_int_var(vid);
366if nt != t {
367return self.fold_ty(nt);
368 } else {
369self.canonicalize_ty_var(CanonicalVarKind::Int, t)
370 }
371 }
372 ty::Infer(ty::FloatVar(vid)) => {
373let nt = self.infcx.unwrap().opportunistic_resolve_float_var(vid);
374if nt != t {
375return self.fold_ty(nt);
376 } else {
377self.canonicalize_ty_var(CanonicalVarKind::Float, t)
378 }
379 }
380381 ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
382::rustc_middle::util::bug::bug_fmt(format_args!("encountered a fresh type during canonicalization"))bug!("encountered a fresh type during canonicalization")383 }
384385 ty::Placeholder(mut placeholder) => {
386if !self.canonicalize_mode.preserve_universes() {
387placeholder.universe = ty::UniverseIndex::ROOT;
388 }
389self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t)
390 }
391392 ty::Bound(ty::BoundVarIndexKind::Bound(_), _) => t,
393394 ty::Bound(ty::BoundVarIndexKind::Canonical, _) => {
395::rustc_middle::util::bug::bug_fmt(format_args!("canonicalized bound var found during canonicalization"));bug!("canonicalized bound var found during canonicalization");
396 }
397398 ty::Closure(..)
399 | ty::CoroutineClosure(..)
400 | ty::Coroutine(..)
401 | ty::CoroutineWitness(..)
402 | ty::Bool403 | ty::Char404 | ty::Int(..)
405 | ty::Uint(..)
406 | ty::Float(..)
407 | ty::Adt(..)
408 | ty::Str409 | 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::Never419 | ty::Tuple(..)
420 | ty::Alias(..)
421 | ty::Foreign(..)
422 | ty::Pat(..)
423 | ty::Param(..) => {
424if t.flags().intersects(self.needs_canonical_flags) {
425t.super_fold_with(self)
426 } else {
427t428 }
429 }
430 }
431 }
432433fn fold_const(&mut self, mut ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
434match ct.kind() {
435 ty::ConstKind::Infer(InferConst::Var(mut vid)) => {
436// We need to canonicalize the *root* of our const var.
437 // This is so that our canonical response correctly reflects
438 // any equated inference vars correctly!
439let root_vid = self.infcx.unwrap().root_const_var(vid);
440if root_vid != vid {
441ct = ty::Const::new_var(self.tcx, root_vid);
442vid = root_vid;
443 }
444445{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_infer/src/infer/canonical/canonicalizer.rs:445",
"rustc_infer::infer::canonical::canonicalizer",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/infer/canonical/canonicalizer.rs"),
::tracing_core::__macro_support::Option::Some(445u32),
::tracing_core::__macro_support::Option::Some("rustc_infer::infer::canonical::canonicalizer"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("canonical: const var found with vid {0:?}",
vid) as &dyn Value))])
});
} else { ; }
};debug!("canonical: const var found with vid {:?}", vid);
446match self.infcx.unwrap().probe_const_var(vid) {
447Ok(c) => {
448{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_infer/src/infer/canonical/canonicalizer.rs:448",
"rustc_infer::infer::canonical::canonicalizer",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/infer/canonical/canonicalizer.rs"),
::tracing_core::__macro_support::Option::Some(448u32),
::tracing_core::__macro_support::Option::Some("rustc_infer::infer::canonical::canonicalizer"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("(resolved to {0:?})",
c) as &dyn Value))])
});
} else { ; }
};debug!("(resolved to {:?})", c);
449return self.fold_const(c);
450 }
451452// `ConstVar(vid)` is unresolved, track its universe index in the
453 // canonicalized result
454Err(mut ui) => {
455if !self.canonicalize_mode.preserve_universes() {
456// FIXME: perf problem described in #55921.
457ui = ty::UniverseIndex::ROOT;
458 }
459return self.canonicalize_const_var(CanonicalVarKind::Const(ui), ct);
460 }
461 }
462 }
463 ty::ConstKind::Infer(InferConst::Fresh(_)) => {
464::rustc_middle::util::bug::bug_fmt(format_args!("encountered a fresh const during canonicalization"))bug!("encountered a fresh const during canonicalization")465 }
466 ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(_), _) => {
467return ct;
468 }
469 ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, _) => {
470::rustc_middle::util::bug::bug_fmt(format_args!("canonicalized bound var found during canonicalization"));bug!("canonicalized bound var found during canonicalization");
471 }
472 ty::ConstKind::Placeholder(placeholder) => {
473return self474 .canonicalize_const_var(CanonicalVarKind::PlaceholderConst(placeholder), ct);
475 }
476_ => {}
477 }
478479if ct.flags().intersects(self.needs_canonical_flags) {
480ct.super_fold_with(self)
481 } else {
482ct483 }
484 }
485486fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
487if p.flags().intersects(self.needs_canonical_flags) { p.super_fold_with(self) } else { p }
488 }
489490fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
491if c.flags().intersects(self.needs_canonical_flags) { c.super_fold_with(self) } else { c }
492 }
493}
494495impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
496/// The main `canonicalize` method, shared impl of
497 /// `canonicalize_query` and `canonicalize_response`.
498fn 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>
505where
506V: TypeFoldable<TyCtxt<'tcx>>,
507 {
508let base = Canonical {
509 max_universe: ty::UniverseIndex::ROOT,
510 var_kinds: List::empty(),
511 value: (),
512 };
513Canonicalizer::canonicalize_with_base(
514base,
515value,
516infcx,
517tcx,
518canonicalize_region_mode,
519query_state,
520 )
521 .unchecked_map(|((), val)| val)
522 }
523524fn 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)>
532where
533V: TypeFoldable<TyCtxt<'tcx>>,
534 {
535let needs_canonical_flags = if canonicalize_region_mode.any() {
536TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS537 } else {
538TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER539 };
540541// Fast path: nothing that needs to be canonicalized.
542if !value.has_type_flags(needs_canonical_flags) {
543return base.unchecked_map(|b| (b, value));
544 }
545546let mut canonicalizer = Canonicalizer {
547infcx,
548tcx,
549 canonicalize_mode: canonicalize_region_mode,
550needs_canonical_flags,
551 var_kinds: SmallVec::from_slice(base.var_kinds),
552query_state,
553 indices: FxHashMap::default(),
554 sub_root_lookup_table: Default::default(),
555 };
556if canonicalizer.query_state.var_values.spilled() {
557canonicalizer.indices = canonicalizer558 .query_state
559 .var_values
560 .iter()
561 .enumerate()
562 .map(|(i, &kind)| (kind, BoundVar::new(i)))
563 .collect();
564 }
565let out_value = value.fold_with(&mut canonicalizer);
566567// Once we have canonicalized `out_value`, it should not
568 // contain anything that ties it to this inference context
569 // anymore.
570if true {
if !(!out_value.has_infer() && !out_value.has_placeholders()) {
::core::panicking::panic("assertion failed: !out_value.has_infer() && !out_value.has_placeholders()")
};
};debug_assert!(!out_value.has_infer() && !out_value.has_placeholders());
571572let canonical_var_kinds =
573tcx.mk_canonical_var_kinds(&canonicalizer.universe_canonicalized_var_kinds());
574575let max_universe = canonical_var_kinds576 .iter()
577 .map(|cvar| cvar.universe())
578 .max()
579 .unwrap_or(ty::UniverseIndex::ROOT);
580581Canonical { max_universe, var_kinds: canonical_var_kinds, value: (base.value, out_value) }
582 }
583584/// Creates a canonical variable replacing `kind` from the input,
585 /// or returns an existing variable if `kind` has already been
586 /// seen. `kind` is expected to be an unbound variable (or
587 /// potentially a free region).
588fn canonical_var(
589&mut self,
590 var_kind: CanonicalVarKind<'tcx>,
591 value: GenericArg<'tcx>,
592 ) -> BoundVar {
593let Canonicalizer { var_kinds, query_state, indices, .. } = self;
594595let var_values = &mut query_state.var_values;
596597let universe = var_kind.universe();
598if universe != ty::UniverseIndex::ROOT {
599if !self.canonicalize_mode.preserve_universes() {
::core::panicking::panic("assertion failed: self.canonicalize_mode.preserve_universes()")
};assert!(self.canonicalize_mode.preserve_universes());
600601// Insert universe into the universe map. To preserve the order of the
602 // universes in the value being canonicalized, we don't update the
603 // universe in `var_kind` until we have finished canonicalizing.
604match query_state.universe_map.binary_search(&universe) {
605Err(idx) => query_state.universe_map.insert(idx, universe),
606Ok(_) => {}
607 }
608 }
609610// This code is hot. `var_kinds` and `var_values` are usually small
611 // (fewer than 8 elements ~95% of the time). They are SmallVec's to
612 // avoid allocations in those cases. We also don't use `indices` to
613 // determine if a kind has been seen before until the limit of 8 has
614 // been exceeded, to also avoid allocations for `indices`.
615if !var_values.spilled() {
616// `var_values` is stack-allocated. `indices` isn't used yet. Do a
617 // direct linear search of `var_values`.
618if let Some(idx) = var_values.iter().position(|&v| v == value) {
619// `kind` is already present in `var_values`.
620BoundVar::new(idx)
621 } else {
622// `kind` isn't present in `var_values`. Append it. Likewise
623 // for `var_kind` and `var_kinds`.
624var_kinds.push(var_kind);
625var_values.push(value);
626match (&var_kinds.len(), &var_values.len()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(var_kinds.len(), var_values.len());
627628// If `var_values` has become big enough to be heap-allocated,
629 // fill up `indices` to facilitate subsequent lookups.
630if var_values.spilled() {
631if !indices.is_empty() {
::core::panicking::panic("assertion failed: indices.is_empty()")
};assert!(indices.is_empty());
632*indices = var_values633 .iter()
634 .enumerate()
635 .map(|(i, &value)| (value, BoundVar::new(i)))
636 .collect();
637 }
638// The cv is the index of the appended element.
639BoundVar::new(var_values.len() - 1)
640 }
641 } else {
642// `var_values` is large. Do a hashmap search via `indices`.
643*indices.entry(value).or_insert_with(|| {
644var_kinds.push(var_kind);
645var_values.push(value);
646match (&var_kinds.len(), &var_values.len()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(var_kinds.len(), var_values.len());
647BoundVar::new(var_kinds.len() - 1)
648 })
649 }
650 }
651652fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar {
653let root_vid = self.infcx.unwrap().sub_unification_table_root_var(vid);
654let idx =
655*self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.var_kinds.len());
656 ty::BoundVar::from(idx)
657 }
658659/// Replaces the universe indexes used in `var_values` with their index in
660 /// `query_state.universe_map`. This minimizes the maximum universe used in
661 /// the canonicalized value.
662fn universe_canonicalized_var_kinds(self) -> SmallVec<[CanonicalVarKind<'tcx>; 8]> {
663if self.query_state.universe_map.len() == 1 {
664return self.var_kinds;
665 }
666667let reverse_universe_map: FxHashMap<ty::UniverseIndex, ty::UniverseIndex> = self668 .query_state
669 .universe_map
670 .iter()
671 .enumerate()
672 .map(|(idx, universe)| (*universe, ty::UniverseIndex::from_usize(idx)))
673 .collect();
674675self.var_kinds
676 .iter()
677 .map(|&kind| match kind {
678CanonicalVarKind::Int | CanonicalVarKind::Float => {
679return kind;
680 }
681CanonicalVarKind::Ty { ui, sub_root } => {
682CanonicalVarKind::Ty { ui: reverse_universe_map[&ui], sub_root }
683 }
684CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]),
685CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
686CanonicalVarKind::PlaceholderTy(placeholder) => {
687CanonicalVarKind::PlaceholderTy(ty::PlaceholderType::new(
688reverse_universe_map[&placeholder.universe],
689placeholder.bound,
690 ))
691 }
692CanonicalVarKind::PlaceholderRegion(placeholder) => {
693CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new(
694reverse_universe_map[&placeholder.universe],
695placeholder.bound,
696 ))
697 }
698CanonicalVarKind::PlaceholderConst(placeholder) => {
699CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst::new(
700reverse_universe_map[&placeholder.universe],
701placeholder.bound,
702 ))
703 }
704 })
705 .collect()
706 }
707708/// Shorthand helper that creates a canonical region variable for
709 /// `r` (always in the root universe). The reason that we always
710 /// put these variables into the root universe is because this
711 /// method is used during **query construction:** in that case, we
712 /// are taking all the regions and just putting them into the most
713 /// generic context we can. This may generate solutions that don't
714 /// fit (e.g., that equate some region variable with a placeholder
715 /// it can't name) on the caller side, but that's ok, the caller
716 /// can figure that out. In the meantime, it maximizes our
717 /// caching.
718 ///
719 /// (This works because unification never fails -- and hence trait
720 /// selection is never affected -- due to a universe mismatch.)
721fn canonical_var_for_region_in_root_universe(
722&mut self,
723 r: ty::Region<'tcx>,
724 ) -> ty::Region<'tcx> {
725self.canonical_var_for_region(CanonicalVarKind::Region(ty::UniverseIndex::ROOT), r)
726 }
727728/// Creates a canonical variable (with the given `info`)
729 /// representing the region `r`; return a region referencing it.
730fn canonical_var_for_region(
731&mut self,
732 var_kind: CanonicalVarKind<'tcx>,
733 r: ty::Region<'tcx>,
734 ) -> ty::Region<'tcx> {
735let var = self.canonical_var(var_kind, r.into());
736 ty::Region::new_canonical_bound(self.cx(), var)
737 }
738739/// Given a type variable `ty_var` of the given kind, first check
740 /// if `ty_var` is bound to anything; if so, canonicalize
741 /// *that*. Otherwise, create a new canonical variable for
742 /// `ty_var`.
743fn canonicalize_ty_var(
744&mut self,
745 var_kind: CanonicalVarKind<'tcx>,
746 ty_var: Ty<'tcx>,
747 ) -> Ty<'tcx> {
748if true {
if !!self.infcx.is_some_and(|infcx|
ty_var != infcx.shallow_resolve(ty_var)) {
::core::panicking::panic("assertion failed: !self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var))")
};
};debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var)));
749let var = self.canonical_var(var_kind, ty_var.into());
750 Ty::new_canonical_bound(self.tcx, var)
751 }
752753/// Given a type variable `const_var` of the given kind, first check
754 /// if `const_var` is bound to anything; if so, canonicalize
755 /// *that*. Otherwise, create a new canonical variable for
756 /// `const_var`.
757fn canonicalize_const_var(
758&mut self,
759 var_kind: CanonicalVarKind<'tcx>,
760 ct_var: ty::Const<'tcx>,
761 ) -> ty::Const<'tcx> {
762if true {
if !!self.infcx.is_some_and(|infcx|
ct_var != infcx.shallow_resolve_const(ct_var)) {
::core::panicking::panic("assertion failed: !self.infcx.is_some_and(|infcx| ct_var != infcx.shallow_resolve_const(ct_var))")
};
};debug_assert!(
763 !self.infcx.is_some_and(|infcx| ct_var != infcx.shallow_resolve_const(ct_var))
764 );
765let var = self.canonical_var(var_kind, ct_var.into());
766 ty::Const::new_canonical_bound(self.tcx, var)
767 }
768}