• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::errors::AutoDerefReachedRecursionLimit;
2 use crate::traits::query::evaluate_obligation::InferCtxtExt;
3 use crate::traits::{self, TraitEngine, TraitEngineExt};
4 use rustc_infer::infer::InferCtxt;
5 use rustc_middle::ty::TypeVisitableExt;
6 use rustc_middle::ty::{self, Ty, TyCtxt};
7 use rustc_session::Limit;
8 use rustc_span::def_id::LocalDefId;
9 use rustc_span::def_id::LOCAL_CRATE;
10 use rustc_span::Span;
11 use rustc_trait_selection::traits::StructurallyNormalizeExt;
12 
13 #[derive(Copy, Clone, Debug)]
14 pub enum AutoderefKind {
15     Builtin,
16     Overloaded,
17 }
18 
19 struct AutoderefSnapshot<'tcx> {
20     at_start: bool,
21     reached_recursion_limit: bool,
22     steps: Vec<(Ty<'tcx>, AutoderefKind)>,
23     cur_ty: Ty<'tcx>,
24     obligations: Vec<traits::PredicateObligation<'tcx>>,
25 }
26 
27 pub struct Autoderef<'a, 'tcx> {
28     // Meta infos:
29     infcx: &'a InferCtxt<'tcx>,
30     span: Span,
31     body_id: LocalDefId,
32     param_env: ty::ParamEnv<'tcx>,
33 
34     // Current state:
35     state: AutoderefSnapshot<'tcx>,
36 
37     // Configurations:
38     include_raw_pointers: bool,
39     silence_errors: bool,
40 }
41 
42 impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
43     type Item = (Ty<'tcx>, usize);
44 
next(&mut self) -> Option<Self::Item>45     fn next(&mut self) -> Option<Self::Item> {
46         let tcx = self.infcx.tcx;
47 
48         debug!("autoderef: steps={:?}, cur_ty={:?}", self.state.steps, self.state.cur_ty);
49         if self.state.at_start {
50             self.state.at_start = false;
51             debug!("autoderef stage #0 is {:?}", self.state.cur_ty);
52             return Some((self.state.cur_ty, 0));
53         }
54 
55         // If we have reached the recursion limit, error gracefully.
56         if !tcx.recursion_limit().value_within_limit(self.state.steps.len()) {
57             if !self.silence_errors {
58                 report_autoderef_recursion_limit_error(tcx, self.span, self.state.cur_ty);
59             }
60             self.state.reached_recursion_limit = true;
61             return None;
62         }
63 
64         if self.state.cur_ty.is_ty_var() {
65             return None;
66         }
67 
68         // Otherwise, deref if type is derefable:
69         let (kind, new_ty) = if let Some(ty::TypeAndMut { ty, .. }) =
70             self.state.cur_ty.builtin_deref(self.include_raw_pointers)
71         {
72             debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty));
73             // NOTE: we may still need to normalize the built-in deref in case
74             // we have some type like `&<Ty as Trait>::Assoc`, since users of
75             // autoderef expect this type to have been structurally normalized.
76             if self.infcx.next_trait_solver()
77                 && let ty::Alias(ty::Projection, _) = ty.kind()
78             {
79                 let (normalized_ty, obligations) = self.structurally_normalize(ty)?;
80                 self.state.obligations.extend(obligations);
81                 (AutoderefKind::Builtin, normalized_ty)
82             } else {
83                 (AutoderefKind::Builtin, ty)
84             }
85         } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
86             (AutoderefKind::Overloaded, ty)
87         } else {
88             return None;
89         };
90 
91         if new_ty.references_error() {
92             return None;
93         }
94 
95         self.state.steps.push((self.state.cur_ty, kind));
96         debug!(
97             "autoderef stage #{:?} is {:?} from {:?}",
98             self.step_count(),
99             new_ty,
100             (self.state.cur_ty, kind)
101         );
102         self.state.cur_ty = new_ty;
103 
104         Some((self.state.cur_ty, self.step_count()))
105     }
106 }
107 
108 impl<'a, 'tcx> Autoderef<'a, 'tcx> {
new( infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_def_id: LocalDefId, span: Span, base_ty: Ty<'tcx>, ) -> Autoderef<'a, 'tcx>109     pub fn new(
110         infcx: &'a InferCtxt<'tcx>,
111         param_env: ty::ParamEnv<'tcx>,
112         body_def_id: LocalDefId,
113         span: Span,
114         base_ty: Ty<'tcx>,
115     ) -> Autoderef<'a, 'tcx> {
116         Autoderef {
117             infcx,
118             span,
119             body_id: body_def_id,
120             param_env,
121             state: AutoderefSnapshot {
122                 steps: vec![],
123                 cur_ty: infcx.resolve_vars_if_possible(base_ty),
124                 obligations: vec![],
125                 at_start: true,
126                 reached_recursion_limit: false,
127             },
128             include_raw_pointers: false,
129             silence_errors: false,
130         }
131     }
132 
overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>>133     fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
134         debug!("overloaded_deref_ty({:?})", ty);
135         let tcx = self.infcx.tcx;
136 
137         // <ty as Deref>
138         let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]);
139         let cause = traits::ObligationCause::misc(self.span, self.body_id);
140         let obligation = traits::Obligation::new(
141             tcx,
142             cause.clone(),
143             self.param_env,
144             ty::Binder::dummy(trait_ref),
145         );
146         if !self.infcx.predicate_may_hold(&obligation) {
147             debug!("overloaded_deref_ty: cannot match obligation");
148             return None;
149         }
150 
151         let (normalized_ty, obligations) = self.structurally_normalize(Ty::new_projection(
152             tcx,
153             tcx.lang_items().deref_target()?,
154             [ty],
155         ))?;
156         debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
157         self.state.obligations.extend(obligations);
158 
159         Some(self.infcx.resolve_vars_if_possible(normalized_ty))
160     }
161 
162     #[instrument(level = "debug", skip(self), ret)]
structurally_normalize( &self, ty: Ty<'tcx>, ) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)>163     pub fn structurally_normalize(
164         &self,
165         ty: Ty<'tcx>,
166     ) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> {
167         let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.infcx);
168 
169         let cause = traits::ObligationCause::misc(self.span, self.body_id);
170         let normalized_ty = match self
171             .infcx
172             .at(&cause, self.param_env)
173             .structurally_normalize(ty, &mut *fulfill_cx)
174         {
175             Ok(normalized_ty) => normalized_ty,
176             Err(errors) => {
177                 // This shouldn't happen, except for evaluate/fulfill mismatches,
178                 // but that's not a reason for an ICE (`predicate_may_hold` is conservative
179                 // by design).
180                 debug!(?errors, "encountered errors while fulfilling");
181                 return None;
182             }
183         };
184 
185         let errors = fulfill_cx.select_where_possible(&self.infcx);
186         if !errors.is_empty() {
187             // This shouldn't happen, except for evaluate/fulfill mismatches,
188             // but that's not a reason for an ICE (`predicate_may_hold` is conservative
189             // by design).
190             debug!(?errors, "encountered errors while fulfilling");
191             return None;
192         }
193 
194         Some((normalized_ty, fulfill_cx.pending_obligations()))
195     }
196 
197     /// Returns the final type we ended up with, which may be an inference
198     /// variable (we will resolve it first, if we want).
final_ty(&self, resolve: bool) -> Ty<'tcx>199     pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> {
200         if resolve {
201             self.infcx.resolve_vars_if_possible(self.state.cur_ty)
202         } else {
203             self.state.cur_ty
204         }
205     }
206 
step_count(&self) -> usize207     pub fn step_count(&self) -> usize {
208         self.state.steps.len()
209     }
210 
into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>>211     pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> {
212         self.state.obligations
213     }
214 
current_obligations(&self) -> Vec<traits::PredicateObligation<'tcx>>215     pub fn current_obligations(&self) -> Vec<traits::PredicateObligation<'tcx>> {
216         self.state.obligations.clone()
217     }
218 
steps(&self) -> &[(Ty<'tcx>, AutoderefKind)]219     pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] {
220         &self.state.steps
221     }
222 
span(&self) -> Span223     pub fn span(&self) -> Span {
224         self.span
225     }
226 
reached_recursion_limit(&self) -> bool227     pub fn reached_recursion_limit(&self) -> bool {
228         self.state.reached_recursion_limit
229     }
230 
231     /// also dereference through raw pointer types
232     /// e.g., assuming ptr_to_Foo is the type `*const Foo`
233     /// fcx.autoderef(span, ptr_to_Foo)  => [*const Foo]
234     /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo]
include_raw_pointers(mut self) -> Self235     pub fn include_raw_pointers(mut self) -> Self {
236         self.include_raw_pointers = true;
237         self
238     }
239 
silence_errors(mut self) -> Self240     pub fn silence_errors(mut self) -> Self {
241         self.silence_errors = true;
242         self
243     }
244 }
245 
report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>)246 pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
247     // We've reached the recursion limit, error gracefully.
248     let suggested_limit = match tcx.recursion_limit() {
249         Limit(0) => Limit(2),
250         limit => limit * 2,
251     };
252     tcx.sess.emit_err(AutoDerefReachedRecursionLimit {
253         span,
254         ty,
255         suggested_limit,
256         crate_name: tcx.crate_name(LOCAL_CRATE),
257     });
258 }
259