1use std::fmt::Debug;
2
3use rustc_type_ir::data_structures::ensure_sufficient_stack;
4use rustc_type_ir::inherent::*;
5use rustc_type_ir::{
6 self as ty, AliasTerm, Binder, FallibleTypeFolder, InferConst, InferCtxtLike, InferTy,
7 Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
8 TypeVisitor, UniverseIndex,
9};
10use tracing::instrument;
11
12use crate::placeholder::{BoundVarReplacer, PlaceholderReplacer};
13
14pub struct NormalizationFolder<'a, Infcx, I, F>
19where
20 Infcx: InferCtxtLike<Interner = I>,
21 I: Interner,
22{
23 infcx: &'a Infcx,
24 universes: Vec<Option<UniverseIndex>>,
25 normalize: F,
26}
27
28#[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)]
29enum HasEscapingBoundVars {
30 Yes,
31 No,
32}
33
34#[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)]
35pub enum NormalizationWasAmbiguous {
36 Yes,
37 No,
38}
39
40struct MaxUniverse<'a, Infcx, I>
42where
43 Infcx: InferCtxtLike<Interner = I>,
44 I: Interner,
45{
46 infcx: &'a Infcx,
47 max_universe: ty::UniverseIndex,
48}
49
50impl<'a, Infcx, I> MaxUniverse<'a, Infcx, I>
51where
52 Infcx: InferCtxtLike<Interner = I>,
53 I: Interner,
54{
55 fn new(infcx: &'a Infcx) -> Self {
56 MaxUniverse { infcx, max_universe: ty::UniverseIndex::ROOT }
57 }
58
59 fn max_universe(self) -> ty::UniverseIndex {
60 self.max_universe
61 }
62}
63
64impl<'a, Infcx, I> TypeVisitor<I> for MaxUniverse<'a, Infcx, I>
65where
66 Infcx: InferCtxtLike<Interner = I>,
67 I: Interner,
68{
69 type Result = ();
70
71 fn visit_ty(&mut self, t: I::Ty) {
72 if !t.has_infer() {
73 return;
74 }
75
76 if let ty::Infer(InferTy::TyVar(vid)) = t.kind() {
77 self.max_universe = self.max_universe.max(self.infcx.universe_of_ty(vid).unwrap());
80 }
81
82 t.super_visit_with(self)
83 }
84
85 fn visit_const(&mut self, c: I::Const) {
86 if !c.has_infer() {
87 return;
88 }
89
90 if let ty::ConstKind::Infer(InferConst::Var(vid)) = c.kind() {
91 self.max_universe = self.max_universe.max(self.infcx.universe_of_ct(vid).unwrap());
94 }
95
96 c.super_visit_with(self)
97 }
98
99 fn visit_region(&mut self, r: I::Region) {
100 if let ty::ReVar(vid) = r.kind() {
101 self.max_universe = self.max_universe.max(self.infcx.universe_of_lt(vid).unwrap());
102 }
103 }
104}
105
106impl<'a, Infcx, I, F, E> NormalizationFolder<'a, Infcx, I, F>
107where
108 Infcx: InferCtxtLike<Interner = I>,
109 I: Interner,
110 F: FnMut(AliasTerm<I>) -> Result<(I::Term, NormalizationWasAmbiguous), E>,
111{
112 pub fn new(infcx: &'a Infcx, universes: Vec<Option<UniverseIndex>>, normalize: F) -> Self {
113 Self { infcx, universes, normalize }
114 }
115
116 fn normalize_alias_term(
117 &mut self,
118 alias_term: AliasTerm<I>,
119 has_escaping: HasEscapingBoundVars,
120 ) -> Result<Option<I::Term>, E> {
121 let (normalized, normalization_was_ambiguous) = (self.normalize)(alias_term)?;
122
123 if normalization_was_ambiguous == NormalizationWasAmbiguous::Yes
131 && has_escaping == HasEscapingBoundVars::Yes
132 {
133 let mut visitor = MaxUniverse::new(self.infcx);
134 normalized.visit_with(&mut visitor);
135 let max_universe = visitor.max_universe();
136 if max_universe.can_name(self.universes.first().unwrap().unwrap()) {
137 return Ok(None);
138 }
139 }
140
141 Ok(Some(normalized))
142 }
143}
144
145impl<'a, Infcx, I, F, E> FallibleTypeFolder<I> for NormalizationFolder<'a, Infcx, I, F>
146where
147 Infcx: InferCtxtLike<Interner = I>,
148 I: Interner,
149 F: FnMut(AliasTerm<I>) -> Result<(I::Term, NormalizationWasAmbiguous), E>,
150 E: Debug,
151{
152 type Error = E;
153
154 fn cx(&self) -> I {
155 self.infcx.cx()
156 }
157
158 fn try_fold_binder<T: TypeFoldable<I>>(
159 &mut self,
160 t: Binder<I, T>,
161 ) -> Result<Binder<I, T>, Self::Error> {
162 self.universes.push(None);
163 let t = t.try_super_fold_with(self)?;
164 self.universes.pop();
165 Ok(t)
166 }
167
168 x;#[instrument(level = "trace", skip(self), ret)]
169 fn try_fold_ty(&mut self, ty: I::Ty) -> Result<I::Ty, Self::Error> {
170 let infcx = self.infcx;
171 let original = ty;
172
173 if !self.cx().renormalize_rigid_aliases() && !ty.has_non_rigid_aliases() {
174 return Ok(ty);
175 }
176
177 let ty = ty.try_super_fold_with(self)?;
180 let ty::Alias(orig_is_rigid, alias_ty) = ty.kind() else { return Ok(ty) };
181 if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
184 return Ok(ty);
185 }
186
187 let normalized = if ty.has_escaping_bound_vars() {
188 let (alias_ty, mapped_regions, mapped_types, mapped_consts) =
189 BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_ty);
190 let Some(result) = ensure_sufficient_stack(|| {
191 self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::Yes)
192 })?
193 else {
194 return Ok(ty);
195 };
196
197 PlaceholderReplacer::replace_placeholders(
198 infcx,
199 mapped_regions,
200 mapped_types,
201 mapped_consts,
202 &self.universes,
203 result.expect_ty(),
204 )
205 } else {
206 ensure_sufficient_stack(|| {
207 self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::No)
208 })?
209 .map(|term| term.expect_ty())
210 .unwrap_or(ty)
211 };
212
213 if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
214 let original = crate::resolve::eager_resolve_vars(infcx, original);
216 let normalized = crate::resolve::eager_resolve_vars(infcx, normalized);
217 assert_eq!(original, normalized, "rigid alias is further normalized");
218 }
219 Ok(normalized)
220 }
221
222 x;#[instrument(level = "trace", skip(self), ret)]
223 fn try_fold_const(&mut self, ct: I::Const) -> Result<I::Const, Self::Error> {
224 let infcx = self.infcx;
225 let original = ct;
226
227 if !self.cx().renormalize_rigid_aliases() && !ct.has_non_rigid_aliases() {
228 return Ok(ct);
229 }
230
231 let ct = ct.try_super_fold_with(self)?;
234 let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return Ok(ct) };
235 if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
238 return Ok(ct);
239 }
240
241 let normalized = if ct.has_escaping_bound_vars() {
242 let (alias_const, mapped_regions, mapped_types, mapped_consts) =
243 BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_const);
244 let Some(result) = ensure_sufficient_stack(|| {
245 self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::Yes)
246 })?
247 else {
248 return Ok(ct);
249 };
250 PlaceholderReplacer::replace_placeholders(
251 infcx,
252 mapped_regions,
253 mapped_types,
254 mapped_consts,
255 &self.universes,
256 result.expect_const(),
257 )
258 } else {
259 ensure_sufficient_stack(|| {
260 self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::No)
261 })?
262 .map(|term| term.expect_const())
263 .unwrap_or(ct)
264 };
265
266 if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
267 let original = crate::resolve::eager_resolve_vars(infcx, original);
269 let normalized = crate::resolve::eager_resolve_vars(infcx, normalized);
270 assert_eq!(original, normalized, "rigid alias is further normalized");
271 }
272
273 Ok(normalized)
274 }
275
276 fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error> {
277 if p.allow_normalization() { p.try_super_fold_with(self) } else { Ok(p) }
278 }
279}