rustc_trait_selection/traits/query/
normalize.rs1use rustc_data_structures::sso::SsoHashMap;
6use rustc_data_structures::stack::ensure_sufficient_stack;
7use rustc_infer::traits::PredicateObligations;
8use rustc_macros::extension;
9pub use rustc_middle::traits::query::NormalizationResult;
10use rustc_middle::ty::{
11 self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
12 TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
13};
14use rustc_span::DUMMY_SP;
15use tracing::{debug, info, instrument};
16
17use super::NoSolution;
18use crate::error_reporting::InferCtxtErrorExt;
19use crate::error_reporting::traits::OverflowCause;
20use crate::infer::at::At;
21use crate::infer::canonical::OriginalQueryValues;
22use crate::infer::{InferCtxt, InferOk};
23use crate::traits::normalize::needs_normalization;
24use crate::traits::{
25 BoundVarReplacer, Normalized, ObligationCause, PlaceholderReplacer, ScrubbedTraitError,
26};
27
28#[extension(pub trait QueryNormalizeExt<'tcx>)]
29impl<'a, 'tcx> At<'a, 'tcx> {
30 fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
49 where
50 T: TypeFoldable<TyCtxt<'tcx>>,
51 {
52 debug!(
53 "normalize::<{}>(value={:?}, param_env={:?}, cause={:?})",
54 std::any::type_name::<T>(),
55 value,
56 self.param_env,
57 self.cause,
58 );
59
60 let universes = if value.has_escaping_bound_vars() {
71 let mut max_visitor =
72 MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 };
73 value.visit_with(&mut max_visitor);
74 vec![None; max_visitor.escaping]
75 } else {
76 vec![]
77 };
78
79 if self.infcx.next_trait_solver() {
80 match crate::solve::deeply_normalize_with_skipped_universes::<_, ScrubbedTraitError<'tcx>>(
81 self, value, universes,
82 ) {
83 Ok(value) => {
84 return Ok(Normalized { value, obligations: PredicateObligations::new() });
85 }
86 Err(_errors) => {
87 return Err(NoSolution);
88 }
89 }
90 }
91
92 if !needs_normalization(self.infcx, &value) {
93 return Ok(Normalized { value, obligations: PredicateObligations::new() });
94 }
95
96 let mut normalizer = QueryNormalizer {
97 infcx: self.infcx,
98 cause: self.cause,
99 param_env: self.param_env,
100 obligations: PredicateObligations::new(),
101 cache: SsoHashMap::new(),
102 anon_depth: 0,
103 universes,
104 };
105
106 let result = value.try_fold_with(&mut normalizer);
107 info!(
108 "normalize::<{}>: result={:?} with {} obligations",
109 std::any::type_name::<T>(),
110 result,
111 normalizer.obligations.len(),
112 );
113 debug!(
114 "normalize::<{}>: obligations={:?}",
115 std::any::type_name::<T>(),
116 normalizer.obligations,
117 );
118 result.map(|value| Normalized { value, obligations: normalizer.obligations })
119 }
120}
121
122struct MaxEscapingBoundVarVisitor {
124 outer_index: ty::DebruijnIndex,
126 escaping: usize,
127}
128
129impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor {
130 fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
131 self.outer_index.shift_in(1);
132 t.super_visit_with(self);
133 self.outer_index.shift_out(1);
134 }
135
136 #[inline]
137 fn visit_ty(&mut self, t: Ty<'tcx>) {
138 if t.outer_exclusive_binder() > self.outer_index {
139 self.escaping = self
140 .escaping
141 .max(t.outer_exclusive_binder().as_usize() - self.outer_index.as_usize());
142 }
143 }
144
145 #[inline]
146 fn visit_region(&mut self, r: ty::Region<'tcx>) {
147 match r.kind() {
148 ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _)
149 if debruijn > self.outer_index =>
150 {
151 self.escaping =
152 self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
153 }
154 _ => {}
155 }
156 }
157
158 fn visit_const(&mut self, ct: ty::Const<'tcx>) {
159 if ct.outer_exclusive_binder() > self.outer_index {
160 self.escaping = self
161 .escaping
162 .max(ct.outer_exclusive_binder().as_usize() - self.outer_index.as_usize());
163 }
164 }
165}
166
167struct QueryNormalizer<'a, 'tcx> {
168 infcx: &'a InferCtxt<'tcx>,
169 cause: &'a ObligationCause<'tcx>,
170 param_env: ty::ParamEnv<'tcx>,
171 obligations: PredicateObligations<'tcx>,
172 cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
173 anon_depth: usize,
174 universes: Vec<Option<ty::UniverseIndex>>,
175}
176
177impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> {
178 type Error = NoSolution;
179
180 fn cx(&self) -> TyCtxt<'tcx> {
181 self.infcx.tcx
182 }
183
184 fn try_fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
185 &mut self,
186 t: ty::Binder<'tcx, T>,
187 ) -> Result<ty::Binder<'tcx, T>, Self::Error> {
188 self.universes.push(None);
189 let t = t.try_super_fold_with(self);
190 self.universes.pop();
191 t
192 }
193
194 #[instrument(level = "debug", skip(self))]
195 fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
196 if !needs_normalization(self.infcx, &ty) {
197 return Ok(ty);
198 }
199
200 if let Some(ty) = self.cache.get(&ty) {
201 return Ok(*ty);
202 }
203
204 let (kind, data) = match *ty.kind() {
205 ty::Alias(kind, data) => (kind, data),
206 _ => {
207 let res = ty.try_super_fold_with(self)?;
208 self.cache.insert(ty, res);
209 return Ok(res);
210 }
211 };
212
213 let res = match kind {
216 ty::Opaque => {
217 match self.infcx.typing_mode() {
219 TypingMode::Coherence
220 | TypingMode::Analysis { .. }
221 | TypingMode::Borrowck { .. }
222 | TypingMode::PostBorrowckAnalysis { .. } => ty.try_super_fold_with(self)?,
223
224 TypingMode::PostAnalysis => {
225 let args = data.args.try_fold_with(self)?;
226 let recursion_limit = self.cx().recursion_limit();
227
228 if !recursion_limit.value_within_limit(self.anon_depth) {
229 let guar = self
230 .infcx
231 .err_ctxt()
232 .build_overflow_error(
233 OverflowCause::DeeplyNormalize(data.into()),
234 self.cause.span,
235 true,
236 )
237 .delay_as_bug();
238 return Ok(Ty::new_error(self.cx(), guar));
239 }
240
241 let generic_ty = self.cx().type_of(data.def_id);
242 let mut concrete_ty = generic_ty.instantiate(self.cx(), args);
243 self.anon_depth += 1;
244 if concrete_ty == ty {
245 concrete_ty = Ty::new_error_with_message(
246 self.cx(),
247 DUMMY_SP,
248 "recursive opaque type",
249 );
250 }
251 let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty));
252 self.anon_depth -= 1;
253 folded_ty?
254 }
255 }
256 }
257
258 ty::Projection | ty::Inherent | ty::Free => {
259 let infcx = self.infcx;
262 let tcx = infcx.tcx;
263 let (data, maps) = if data.has_escaping_bound_vars() {
266 let (data, mapped_regions, mapped_types, mapped_consts) =
267 BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
268 (data, Some((mapped_regions, mapped_types, mapped_consts)))
269 } else {
270 (data, None)
271 };
272 let data = data.try_fold_with(self)?;
273
274 let mut orig_values = OriginalQueryValues::default();
275 let c_data = infcx.canonicalize_query(self.param_env.and(data), &mut orig_values);
276 debug!("QueryNormalizer: c_data = {:#?}", c_data);
277 debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
278 let result = match kind {
279 ty::Projection => tcx.normalize_canonicalized_projection_ty(c_data),
280 ty::Free => tcx.normalize_canonicalized_free_alias(c_data),
281 ty::Inherent => tcx.normalize_canonicalized_inherent_projection_ty(c_data),
282 kind => unreachable!("did not expect {kind:?} due to match arm above"),
283 }?;
284 if !result.value.is_proven() {
286 if !tcx.sess.opts.actually_rustdoc {
289 tcx.dcx()
290 .delayed_bug(format!("unexpected ambiguity: {c_data:?} {result:?}"));
291 }
292 return Err(NoSolution);
293 }
294 let InferOk { value: result, obligations } = infcx
295 .instantiate_query_response_and_region_obligations(
296 self.cause,
297 self.param_env,
298 &orig_values,
299 result,
300 )?;
301 debug!("QueryNormalizer: result = {:#?}", result);
302 debug!("QueryNormalizer: obligations = {:#?}", obligations);
303 self.obligations.extend(obligations);
304 let res = if let Some((mapped_regions, mapped_types, mapped_consts)) = maps {
305 PlaceholderReplacer::replace_placeholders(
306 infcx,
307 mapped_regions,
308 mapped_types,
309 mapped_consts,
310 &self.universes,
311 result.normalized_ty,
312 )
313 } else {
314 result.normalized_ty
315 };
316 if res != ty
321 && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Free)
322 {
323 res.try_fold_with(self)?
324 } else {
325 res
326 }
327 }
328 };
329
330 self.cache.insert(ty, res);
331 Ok(res)
332 }
333
334 fn try_fold_const(
335 &mut self,
336 constant: ty::Const<'tcx>,
337 ) -> Result<ty::Const<'tcx>, Self::Error> {
338 if !needs_normalization(self.infcx, &constant) {
339 return Ok(constant);
340 }
341
342 let constant = crate::traits::with_replaced_escaping_bound_vars(
343 self.infcx,
344 &mut self.universes,
345 constant,
346 |constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env),
347 );
348 debug!(?constant, ?self.param_env);
349 constant.try_super_fold_with(self)
350 }
351
352 #[inline]
353 fn try_fold_predicate(
354 &mut self,
355 p: ty::Predicate<'tcx>,
356 ) -> Result<ty::Predicate<'tcx>, Self::Error> {
357 if p.allow_normalization() && needs_normalization(self.infcx, &p) {
358 p.try_super_fold_with(self)
359 } else {
360 Ok(p)
361 }
362 }
363}