1use rustc_macros::{HashStable, TyDecodable, TyEncodable};
11use tracing::{debug, instrument};
12
13use crate::traits::query::NoSolution;
14use crate::ty::{
15    self, EarlyBinder, FallibleTypeFolder, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeFolder,
16    TypeVisitableExt,
17};
18
19#[derive(Debug, Copy, Clone, HashStable, TyEncodable, TyDecodable)]
20pub enum NormalizationError<'tcx> {
21    Type(Ty<'tcx>),
22    Const(ty::Const<'tcx>),
23}
24
25impl<'tcx> NormalizationError<'tcx> {
26    pub fn get_type_for_failure(&self) -> String {
27        match self {
28            NormalizationError::Type(t) => format!("{t}"),
29            NormalizationError::Const(c) => format!("{c}"),
30        }
31    }
32}
33
34impl<'tcx> TyCtxt<'tcx> {
35    #[tracing::instrument(level = "debug", skip(self, typing_env), ret)]
41    pub fn normalize_erasing_regions<T>(self, typing_env: ty::TypingEnv<'tcx>, value: T) -> T
42    where
43        T: TypeFoldable<TyCtxt<'tcx>>,
44    {
45        debug!(
46            "normalize_erasing_regions::<{}>(value={:?}, typing_env={:?})",
47            std::any::type_name::<T>(),
48            value,
49            typing_env,
50        );
51
52        let value = self.erase_and_anonymize_regions(value);
55        debug!(?value);
56
57        if !value.has_aliases() {
58            value
59        } else {
60            value.fold_with(&mut NormalizeAfterErasingRegionsFolder { tcx: self, typing_env })
61        }
62    }
63
64    pub fn try_normalize_erasing_regions<T>(
70        self,
71        typing_env: ty::TypingEnv<'tcx>,
72        value: T,
73    ) -> Result<T, NormalizationError<'tcx>>
74    where
75        T: TypeFoldable<TyCtxt<'tcx>>,
76    {
77        debug!(
78            "try_normalize_erasing_regions::<{}>(value={:?}, typing_env={:?})",
79            std::any::type_name::<T>(),
80            value,
81            typing_env,
82        );
83
84        let value = self.erase_and_anonymize_regions(value);
87        debug!(?value);
88
89        if !value.has_aliases() {
90            Ok(value)
91        } else {
92            let mut folder = TryNormalizeAfterErasingRegionsFolder::new(self, typing_env);
93            value.try_fold_with(&mut folder)
94        }
95    }
96
97    #[tracing::instrument(level = "debug", skip(self, typing_env))]
109    pub fn normalize_erasing_late_bound_regions<T>(
110        self,
111        typing_env: ty::TypingEnv<'tcx>,
112        value: ty::Binder<'tcx, T>,
113    ) -> T
114    where
115        T: TypeFoldable<TyCtxt<'tcx>>,
116    {
117        let value = self.instantiate_bound_regions_with_erased(value);
118        self.normalize_erasing_regions(typing_env, value)
119    }
120
121    #[instrument(level = "debug", skip(self))]
127    pub fn instantiate_and_normalize_erasing_regions<T>(
128        self,
129        param_args: GenericArgsRef<'tcx>,
130        typing_env: ty::TypingEnv<'tcx>,
131        value: EarlyBinder<'tcx, T>,
132    ) -> T
133    where
134        T: TypeFoldable<TyCtxt<'tcx>>,
135    {
136        let instantiated = value.instantiate(self, param_args);
137        self.normalize_erasing_regions(typing_env, instantiated)
138    }
139
140    #[instrument(level = "debug", skip(self))]
145    pub fn try_instantiate_and_normalize_erasing_regions<T>(
146        self,
147        param_args: GenericArgsRef<'tcx>,
148        typing_env: ty::TypingEnv<'tcx>,
149        value: EarlyBinder<'tcx, T>,
150    ) -> Result<T, NormalizationError<'tcx>>
151    where
152        T: TypeFoldable<TyCtxt<'tcx>>,
153    {
154        let instantiated = value.instantiate(self, param_args);
155        self.try_normalize_erasing_regions(typing_env, instantiated)
156    }
157}
158
159struct NormalizeAfterErasingRegionsFolder<'tcx> {
160    tcx: TyCtxt<'tcx>,
161    typing_env: ty::TypingEnv<'tcx>,
162}
163
164impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> {
165    fn normalize_generic_arg_after_erasing_regions(
166        &self,
167        arg: ty::GenericArg<'tcx>,
168    ) -> ty::GenericArg<'tcx> {
169        let arg = self.typing_env.as_query_input(arg);
170        self.tcx.try_normalize_generic_arg_after_erasing_regions(arg).unwrap_or_else(|_| {
171            bug!(
172                "Failed to normalize {:?} in typing_env={:?}, \
173                maybe try to call `try_normalize_erasing_regions` instead",
174                arg.value,
175                self.typing_env,
176            )
177        })
178    }
179}
180
181impl<'tcx> TypeFolder<TyCtxt<'tcx>> for NormalizeAfterErasingRegionsFolder<'tcx> {
182    fn cx(&self) -> TyCtxt<'tcx> {
183        self.tcx
184    }
185
186    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
187        self.normalize_generic_arg_after_erasing_regions(ty.into()).expect_ty()
188    }
189
190    fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
191        self.normalize_generic_arg_after_erasing_regions(c.into()).expect_const()
192    }
193}
194
195struct TryNormalizeAfterErasingRegionsFolder<'tcx> {
196    tcx: TyCtxt<'tcx>,
197    typing_env: ty::TypingEnv<'tcx>,
198}
199
200impl<'tcx> TryNormalizeAfterErasingRegionsFolder<'tcx> {
201    fn new(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Self {
202        TryNormalizeAfterErasingRegionsFolder { tcx, typing_env }
203    }
204
205    #[instrument(skip(self), level = "debug")]
206    fn try_normalize_generic_arg_after_erasing_regions(
207        &self,
208        arg: ty::GenericArg<'tcx>,
209    ) -> Result<ty::GenericArg<'tcx>, NoSolution> {
210        let input = self.typing_env.as_query_input(arg);
211        self.tcx.try_normalize_generic_arg_after_erasing_regions(input)
212    }
213}
214
215impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for TryNormalizeAfterErasingRegionsFolder<'tcx> {
216    type Error = NormalizationError<'tcx>;
217
218    fn cx(&self) -> TyCtxt<'tcx> {
219        self.tcx
220    }
221
222    fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
223        match self.try_normalize_generic_arg_after_erasing_regions(ty.into()) {
224            Ok(t) => Ok(t.expect_ty()),
225            Err(_) => Err(NormalizationError::Type(ty)),
226        }
227    }
228
229    fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
230        match self.try_normalize_generic_arg_after_erasing_regions(c.into()) {
231            Ok(t) => Ok(t.expect_const()),
232            Err(_) => Err(NormalizationError::Const(c)),
233        }
234    }
235}