1 //! Code for the 'normalization' query. This consists of a wrapper
2 //! which folds deeply, invoking the underlying
3 //! `normalize_projection_ty` query when it encounters projections.
5 use crate::infer::at::At;
6 use crate::infer::canonical::OriginalQueryValues;
7 use crate::infer::{InferCtxt, InferOk};
8 use crate::traits::error_reporting::InferCtxtExt;
9 use crate::traits::project::needs_normalization;
10 use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
11 use rustc_data_structures::sso::SsoHashMap;
12 use rustc_data_structures::stack::ensure_sufficient_stack;
13 use rustc_infer::traits::Normalized;
14 use rustc_middle::mir;
15 use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder};
16 use rustc_middle::ty::subst::Subst;
17 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
19 use std::ops::ControlFlow;
21 use super::NoSolution;
23 pub use rustc_middle::traits::query::NormalizationResult;
25 pub trait AtExt<'tcx> {
26 fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
28 T: TypeFoldable<'tcx>;
31 impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
32 /// Normalize `value` in the context of the inference context,
33 /// yielding a resulting type, or an error if `value` cannot be
34 /// normalized. If you don't care about regions, you should prefer
35 /// `normalize_erasing_regions`, which is more efficient.
37 /// If the normalization succeeds and is unambiguous, returns back
38 /// the normalized value along with various outlives relations (in
39 /// the form of obligations that must be discharged).
41 /// N.B., this will *eventually* be the main means of
42 /// normalizing, but for now should be used only when we actually
43 /// know that normalization will succeed, since error reporting
44 /// and other details are still "under development".
45 fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
47 T: TypeFoldable<'tcx>,
50 "normalize::<{}>(value={:?}, param_env={:?})",
51 std::any::type_name::<T>(),
55 if !needs_normalization(&value, self.param_env.reveal()) {
56 return Ok(Normalized { value, obligations: vec![] });
59 let mut normalizer = QueryNormalizer {
62 param_env: self.param_env,
64 cache: SsoHashMap::new(),
69 // This is actually a consequence by the way `normalize_erasing_regions` works currently.
70 // Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds
71 // through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us
72 // with trying to normalize with escaping bound vars.
74 // Here, we just add the universes that we *would* have created had we passed through the binders.
76 // We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary.
77 // The rest of the code is already set up to be lazy about replacing bound vars,
78 // and only when we actually have to normalize.
79 if value.has_escaping_bound_vars() {
80 let mut max_visitor = MaxEscapingBoundVarVisitor {
82 outer_index: ty::INNERMOST,
85 value.visit_with(&mut max_visitor);
86 if max_visitor.escaping > 0 {
87 normalizer.universes.extend((0..max_visitor.escaping).map(|_| None));
90 let result = value.try_fold_with(&mut normalizer);
92 "normalize::<{}>: result={:?} with {} obligations",
93 std::any::type_name::<T>(),
95 normalizer.obligations.len(),
98 "normalize::<{}>: obligations={:?}",
99 std::any::type_name::<T>(),
100 normalizer.obligations,
102 result.map(|value| Normalized { value, obligations: normalizer.obligations })
106 /// Visitor to find the maximum escaping bound var
107 struct MaxEscapingBoundVarVisitor<'tcx> {
109 // The index which would count as escaping
110 outer_index: ty::DebruijnIndex,
114 impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor<'tcx> {
115 fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
119 fn visit_binder<T: TypeFoldable<'tcx>>(
121 t: &ty::Binder<'tcx, T>,
122 ) -> ControlFlow<Self::BreakTy> {
123 self.outer_index.shift_in(1);
124 let result = t.super_visit_with(self);
125 self.outer_index.shift_out(1);
130 fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
131 if t.outer_exclusive_binder() > self.outer_index {
134 .max(t.outer_exclusive_binder().as_usize() - self.outer_index.as_usize());
136 ControlFlow::CONTINUE
140 fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
142 ty::ReLateBound(debruijn, _) if debruijn > self.outer_index => {
144 self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
148 ControlFlow::CONTINUE
151 fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
153 ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => {
155 self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
156 ControlFlow::CONTINUE
158 _ => ct.super_visit_with(self),
163 struct QueryNormalizer<'cx, 'tcx> {
164 infcx: &'cx InferCtxt<'cx, 'tcx>,
165 cause: &'cx ObligationCause<'tcx>,
166 param_env: ty::ParamEnv<'tcx>,
167 obligations: Vec<PredicateObligation<'tcx>>,
168 cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
170 universes: Vec<Option<ty::UniverseIndex>>,
173 impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
174 type Error = NoSolution;
176 fn tcx<'c>(&'c self) -> TyCtxt<'tcx> {
181 impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
182 fn try_fold_binder<T: TypeFoldable<'tcx>>(
184 t: ty::Binder<'tcx, T>,
185 ) -> Result<ty::Binder<'tcx, T>, Self::Error> {
186 self.universes.push(None);
187 let t = t.try_super_fold_with(self);
188 self.universes.pop();
192 #[instrument(level = "debug", skip(self))]
193 fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
194 if !needs_normalization(&ty, self.param_env.reveal()) {
198 if let Some(ty) = self.cache.get(&ty) {
202 // See note in `rustc_trait_selection::traits::project` about why we
203 // wait to fold the substs.
205 // Wrap this in a closure so we don't accidentally return from the outer function
206 let res = (|| match *ty.kind() {
207 // This is really important. While we *can* handle this, this has
208 // severe performance implications for large opaque types with
209 // late-bound regions. See `issue-88862` benchmark.
210 ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => {
211 // Only normalize `impl Trait` after type-checking, usually in codegen.
212 match self.param_env.reveal() {
213 Reveal::UserFacing => ty.try_super_fold_with(self),
216 let substs = substs.try_super_fold_with(self)?;
217 let recursion_limit = self.tcx().recursion_limit();
218 if !recursion_limit.value_within_limit(self.anon_depth) {
219 let obligation = Obligation::with_depth(
225 self.infcx.report_overflow_error(&obligation, true);
228 let generic_ty = self.tcx().type_of(def_id);
229 let concrete_ty = generic_ty.subst(self.tcx(), substs);
230 self.anon_depth += 1;
231 if concrete_ty == ty {
233 "infinite recursion generic_ty: {:#?}, substs: {:#?}, \
234 concrete_ty: {:#?}, ty: {:#?}",
241 let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty));
242 self.anon_depth -= 1;
248 ty::Projection(data) if !data.has_escaping_bound_vars() => {
249 // This branch is just an optimization: when we don't have escaping bound vars,
250 // we don't need to replace them with placeholders (see branch below).
252 let tcx = self.infcx.tcx;
253 let data = data.try_super_fold_with(self)?;
255 let mut orig_values = OriginalQueryValues::default();
256 // HACK(matthewjasper) `'static` is special-cased in selection,
257 // so we cannot canonicalize it.
260 .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
261 debug!("QueryNormalizer: c_data = {:#?}", c_data);
262 debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
263 let result = tcx.normalize_projection_ty(c_data)?;
264 // We don't expect ambiguity.
265 if result.is_ambiguous() {
266 return Err(NoSolution);
268 let InferOk { value: result, obligations } =
269 self.infcx.instantiate_query_response_and_region_obligations(
275 debug!("QueryNormalizer: result = {:#?}", result);
276 debug!("QueryNormalizer: obligations = {:#?}", obligations);
277 self.obligations.extend(obligations);
278 Ok(result.normalized_ty)
281 ty::Projection(data) => {
282 // See note in `rustc_trait_selection::traits::project`
284 let tcx = self.infcx.tcx;
285 let infcx = self.infcx;
286 let (data, mapped_regions, mapped_types, mapped_consts) =
287 crate::traits::project::BoundVarReplacer::replace_bound_vars(
292 let data = data.try_super_fold_with(self)?;
294 let mut orig_values = OriginalQueryValues::default();
295 // HACK(matthewjasper) `'static` is special-cased in selection,
296 // so we cannot canonicalize it.
299 .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
300 debug!("QueryNormalizer: c_data = {:#?}", c_data);
301 debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
302 let result = tcx.normalize_projection_ty(c_data)?;
303 // We don't expect ambiguity.
304 if result.is_ambiguous() {
305 return Err(NoSolution);
307 let InferOk { value: result, obligations } =
308 self.infcx.instantiate_query_response_and_region_obligations(
314 debug!("QueryNormalizer: result = {:#?}", result);
315 debug!("QueryNormalizer: obligations = {:#?}", obligations);
316 self.obligations.extend(obligations);
317 Ok(crate::traits::project::PlaceholderReplacer::replace_placeholders(
323 result.normalized_ty,
327 _ => ty.try_super_fold_with(self),
329 self.cache.insert(ty, res);
335 constant: &'tcx ty::Const<'tcx>,
336 ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> {
337 let constant = constant.try_super_fold_with(self)?;
338 Ok(constant.eval(self.infcx.tcx, self.param_env))
341 fn try_fold_mir_const(
343 constant: mir::ConstantKind<'tcx>,
344 ) -> Result<mir::ConstantKind<'tcx>, Self::Error> {
345 constant.try_super_fold_with(self)