]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/traits/query/normalize.rs
Merge commit '2f6439ae6a6803d030cceb3ee14c9150e91b328b' into clippyup
[rust.git] / compiler / rustc_trait_selection / src / traits / query / normalize.rs
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.
4
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::{Obligation, ObligationCause, PredicateObligation, Reveal};
10 use rustc_data_structures::sso::SsoHashMap;
11 use rustc_data_structures::stack::ensure_sufficient_stack;
12 use rustc_infer::traits::Normalized;
13 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
14 use rustc_middle::ty::subst::Subst;
15 use rustc_middle::ty::{self, Ty, TyCtxt};
16
17 use super::NoSolution;
18
19 pub use rustc_middle::traits::query::NormalizationResult;
20
21 pub trait AtExt<'tcx> {
22     fn normalize<T>(&self, value: &T) -> Result<Normalized<'tcx, T>, NoSolution>
23     where
24         T: TypeFoldable<'tcx>;
25 }
26
27 impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
28     /// Normalize `value` in the context of the inference context,
29     /// yielding a resulting type, or an error if `value` cannot be
30     /// normalized. If you don't care about regions, you should prefer
31     /// `normalize_erasing_regions`, which is more efficient.
32     ///
33     /// If the normalization succeeds and is unambiguous, returns back
34     /// the normalized value along with various outlives relations (in
35     /// the form of obligations that must be discharged).
36     ///
37     /// N.B., this will *eventually* be the main means of
38     /// normalizing, but for now should be used only when we actually
39     /// know that normalization will succeed, since error reporting
40     /// and other details are still "under development".
41     fn normalize<T>(&self, value: &T) -> Result<Normalized<'tcx, T>, NoSolution>
42     where
43         T: TypeFoldable<'tcx>,
44     {
45         debug!(
46             "normalize::<{}>(value={:?}, param_env={:?})",
47             ::std::any::type_name::<T>(),
48             value,
49             self.param_env,
50         );
51         if !value.has_projections() {
52             return Ok(Normalized { value: value.clone(), obligations: vec![] });
53         }
54
55         let mut normalizer = QueryNormalizer {
56             infcx: self.infcx,
57             cause: self.cause,
58             param_env: self.param_env,
59             obligations: vec![],
60             error: false,
61             cache: SsoHashMap::new(),
62             anon_depth: 0,
63         };
64
65         let result = value.fold_with(&mut normalizer);
66         debug!(
67             "normalize::<{}>: result={:?} with {} obligations",
68             ::std::any::type_name::<T>(),
69             result,
70             normalizer.obligations.len(),
71         );
72         debug!(
73             "normalize::<{}>: obligations={:?}",
74             ::std::any::type_name::<T>(),
75             normalizer.obligations,
76         );
77         if normalizer.error {
78             Err(NoSolution)
79         } else {
80             Ok(Normalized { value: result, obligations: normalizer.obligations })
81         }
82     }
83 }
84
85 struct QueryNormalizer<'cx, 'tcx> {
86     infcx: &'cx InferCtxt<'cx, 'tcx>,
87     cause: &'cx ObligationCause<'tcx>,
88     param_env: ty::ParamEnv<'tcx>,
89     obligations: Vec<PredicateObligation<'tcx>>,
90     cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
91     error: bool,
92     anon_depth: usize,
93 }
94
95 impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
96     fn tcx<'c>(&'c self) -> TyCtxt<'tcx> {
97         self.infcx.tcx
98     }
99
100     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
101         if !ty.has_projections() {
102             return ty;
103         }
104
105         if let Some(ty) = self.cache.get(&ty) {
106             return ty;
107         }
108
109         let ty = ty.super_fold_with(self);
110         let res = (|| match *ty.kind() {
111             ty::Opaque(def_id, substs) => {
112                 // Only normalize `impl Trait` after type-checking, usually in codegen.
113                 match self.param_env.reveal() {
114                     Reveal::UserFacing => ty,
115
116                     Reveal::All => {
117                         let recursion_limit = self.tcx().sess.recursion_limit();
118                         if !recursion_limit.value_within_limit(self.anon_depth) {
119                             let obligation = Obligation::with_depth(
120                                 self.cause.clone(),
121                                 recursion_limit.0,
122                                 self.param_env,
123                                 ty,
124                             );
125                             self.infcx.report_overflow_error(&obligation, true);
126                         }
127
128                         let generic_ty = self.tcx().type_of(def_id);
129                         let concrete_ty = generic_ty.subst(self.tcx(), substs);
130                         self.anon_depth += 1;
131                         if concrete_ty == ty {
132                             bug!(
133                                 "infinite recursion generic_ty: {:#?}, substs: {:#?}, \
134                                  concrete_ty: {:#?}, ty: {:#?}",
135                                 generic_ty,
136                                 substs,
137                                 concrete_ty,
138                                 ty
139                             );
140                         }
141                         let folded_ty = ensure_sufficient_stack(|| self.fold_ty(concrete_ty));
142                         self.anon_depth -= 1;
143                         folded_ty
144                     }
145                 }
146             }
147
148             ty::Projection(ref data) if !data.has_escaping_bound_vars() => {
149                 // This is kind of hacky -- we need to be able to
150                 // handle normalization within binders because
151                 // otherwise we wind up a need to normalize when doing
152                 // trait matching (since you can have a trait
153                 // obligation like `for<'a> T::B: Fn(&'a i32)`), but
154                 // we can't normalize with bound regions in scope. So
155                 // far now we just ignore binders but only normalize
156                 // if all bound regions are gone (and then we still
157                 // have to renormalize whenever we instantiate a
158                 // binder). It would be better to normalize in a
159                 // binding-aware fashion.
160
161                 let tcx = self.infcx.tcx;
162
163                 let mut orig_values = OriginalQueryValues::default();
164                 // HACK(matthewjasper) `'static` is special-cased in selection,
165                 // so we cannot canonicalize it.
166                 let c_data = self
167                     .infcx
168                     .canonicalize_hr_query_hack(&self.param_env.and(*data), &mut orig_values);
169                 debug!("QueryNormalizer: c_data = {:#?}", c_data);
170                 debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
171                 match tcx.normalize_projection_ty(c_data) {
172                     Ok(result) => {
173                         // We don't expect ambiguity.
174                         if result.is_ambiguous() {
175                             self.error = true;
176                             return ty;
177                         }
178
179                         match self.infcx.instantiate_query_response_and_region_obligations(
180                             self.cause,
181                             self.param_env,
182                             &orig_values,
183                             &result,
184                         ) {
185                             Ok(InferOk { value: result, obligations }) => {
186                                 debug!("QueryNormalizer: result = {:#?}", result);
187                                 debug!("QueryNormalizer: obligations = {:#?}", obligations);
188                                 self.obligations.extend(obligations);
189                                 result.normalized_ty
190                             }
191
192                             Err(_) => {
193                                 self.error = true;
194                                 ty
195                             }
196                         }
197                     }
198
199                     Err(NoSolution) => {
200                         self.error = true;
201                         ty
202                     }
203                 }
204             }
205
206             _ => ty,
207         })();
208         self.cache.insert(ty, res);
209         res
210     }
211
212     fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
213         let constant = constant.super_fold_with(self);
214         constant.eval(self.infcx.tcx, self.param_env)
215     }
216 }