1use std::fmt::Debug;
2
3use rustc_type_ir::inherent::*;
4use rustc_type_ir::{
5 self as ty, AliasTerm, Binder, FallibleTypeFolder, InferCtxtLike, Interner, TypeFoldable,
6 TypeSuperFoldable, TypeVisitableExt, UniverseIndex, eager_resolve_vars,
7};
8use tracing::instrument;
9
10use crate::placeholder::{BoundVarReplacer, PlaceholderReplacer};
11
12pub struct NormalizationFolder<'a, Infcx, I, F>
17where
18 Infcx: InferCtxtLike<Interner = I>,
19 I: Interner,
20{
21 infcx: &'a Infcx,
22 universes: Vec<Option<UniverseIndex>>,
23 normalize: F,
24}
25
26#[derive(#[automatically_derived]
impl ::core::cmp::PartialEq for HasEscapingBoundVars {
#[inline]
fn eq(&self, other: &HasEscapingBoundVars) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for HasEscapingBoundVars {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq)]
27enum HasEscapingBoundVars {
28 Yes,
29 No,
30}
31
32#[derive(#[automatically_derived]
impl ::core::cmp::PartialEq for NormalizationWasAmbiguous {
#[inline]
fn eq(&self, other: &NormalizationWasAmbiguous) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NormalizationWasAmbiguous {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq)]
33pub enum NormalizationWasAmbiguous {
34 Yes,
35 No,
36}
37
38impl<'a, Infcx, I, F, E> NormalizationFolder<'a, Infcx, I, F>
39where
40 Infcx: InferCtxtLike<Interner = I>,
41 I: Interner,
42 F: FnMut(AliasTerm<I>) -> Result<(I::Term, NormalizationWasAmbiguous), E>,
43{
44 pub fn new(infcx: &'a Infcx, universes: Vec<Option<UniverseIndex>>, normalize: F) -> Self {
45 Self { infcx, universes, normalize }
46 }
47
48 fn normalize_alias_term(
49 &mut self,
50 alias_term: AliasTerm<I>,
51 has_escaping: HasEscapingBoundVars,
52 ) -> Result<Option<I::Term>, E> {
53 let (normalized, normalization_was_ambiguous) = (self.normalize)(alias_term)?;
54
55 if normalization_was_ambiguous == NormalizationWasAmbiguous::Yes
63 && has_escaping == HasEscapingBoundVars::Yes
64 {
65 let max_universe = ty::max_universe_of_infer_vars(self.infcx, normalized);
66 if max_universe.can_name(self.universes.first().unwrap().unwrap()) {
67 return Ok(None);
68 }
69 }
70
71 Ok(Some(normalized))
72 }
73}
74
75impl<'a, Infcx, I, F, E> FallibleTypeFolder<I> for NormalizationFolder<'a, Infcx, I, F>
76where
77 Infcx: InferCtxtLike<Interner = I>,
78 I: Interner,
79 F: FnMut(AliasTerm<I>) -> Result<(I::Term, NormalizationWasAmbiguous), E>,
80 E: Debug,
81{
82 type Error = E;
83
84 fn cx(&self) -> I {
85 self.infcx.cx()
86 }
87
88 fn try_fold_binder<T: TypeFoldable<I>>(
89 &mut self,
90 t: Binder<I, T>,
91 ) -> Result<Binder<I, T>, Self::Error> {
92 self.universes.push(None);
93 let t = t.try_super_fold_with(self)?;
94 self.universes.pop();
95 Ok(t)
96 }
97
98 x;#[instrument(level = "trace", skip(self), ret)]
99 fn try_fold_ty(&mut self, ty: I::Ty) -> Result<I::Ty, Self::Error> {
100 let infcx = self.infcx;
101 let original = ty;
102
103 if !self.cx().renormalize_rigid_aliases() && !ty.has_non_rigid_aliases() {
104 return Ok(ty);
105 }
106
107 let ty = ty.try_super_fold_with(self)?;
110 let ty::Alias(orig_is_rigid, alias_ty) = ty.kind() else { return Ok(ty) };
111 if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
114 return Ok(ty);
115 }
116
117 let normalized = if ty.has_escaping_bound_vars() {
118 let (alias_ty, mapped_regions, mapped_types, mapped_consts) =
119 BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_ty);
120 let Some(result) =
121 self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::Yes)?
122 else {
123 return Ok(ty);
124 };
125
126 PlaceholderReplacer::replace_placeholders(
127 infcx,
128 mapped_regions,
129 mapped_types,
130 mapped_consts,
131 &self.universes,
132 result.expect_ty(),
133 )
134 } else {
135 self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::No)?
136 .map(|term| term.expect_ty())
137 .unwrap_or(ty)
138 };
139
140 if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
141 let original = eager_resolve_vars(infcx, original);
143 let normalized = eager_resolve_vars(infcx, normalized);
144 assert_eq!(original, normalized, "rigid alias is further normalized");
145 }
146 Ok(normalized)
147 }
148
149 x;#[instrument(level = "trace", skip(self), ret)]
150 fn try_fold_const(&mut self, ct: I::Const) -> Result<I::Const, Self::Error> {
151 let infcx = self.infcx;
152 let original = ct;
153
154 if !self.cx().renormalize_rigid_aliases() && !ct.has_non_rigid_aliases() {
155 return Ok(ct);
156 }
157
158 let ct = ct.try_super_fold_with(self)?;
161 let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return Ok(ct) };
162 if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
165 return Ok(ct);
166 }
167
168 let normalized = if ct.has_escaping_bound_vars() {
169 let (alias_const, mapped_regions, mapped_types, mapped_consts) =
170 BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_const);
171 let Some(result) =
172 self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::Yes)?
173 else {
174 return Ok(ct);
175 };
176 PlaceholderReplacer::replace_placeholders(
177 infcx,
178 mapped_regions,
179 mapped_types,
180 mapped_consts,
181 &self.universes,
182 result.expect_const(),
183 )
184 } else {
185 self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::No)?
186 .map(|term| term.expect_const())
187 .unwrap_or(ct)
188 };
189
190 if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
191 let original = eager_resolve_vars(infcx, original);
193 let normalized = eager_resolve_vars(infcx, normalized);
194 assert_eq!(original, normalized, "rigid alias is further normalized");
195 }
196
197 Ok(normalized)
198 }
199
200 fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error> {
201 if p.allow_normalization() { p.try_super_fold_with(self) } else { Ok(p) }
202 }
203}