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