]>
Commit | Line | Data |
---|---|---|
f2b60f7d | 1 | use crate::errors::AutoDerefReachedRecursionLimit; |
f035d41b | 2 | use crate::traits::query::evaluate_obligation::InferCtxtExt; |
487cf647 | 3 | use crate::traits::{self, TraitEngine, TraitEngineExt}; |
f035d41b | 4 | use rustc_infer::infer::InferCtxt; |
9ffffee4 | 5 | use rustc_middle::ty::TypeVisitableExt; |
487cf647 | 6 | use rustc_middle::ty::{self, Ty, TyCtxt}; |
5e7ed085 | 7 | use rustc_session::Limit; |
9ffffee4 | 8 | use rustc_span::def_id::LocalDefId; |
17df50a5 | 9 | use rustc_span::def_id::LOCAL_CRATE; |
f035d41b | 10 | use rustc_span::Span; |
49aad941 | 11 | use rustc_trait_selection::traits::StructurallyNormalizeExt; |
f035d41b XL |
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: | |
2b03887a | 29 | infcx: &'a InferCtxt<'tcx>, |
f035d41b | 30 | span: Span, |
9ffffee4 | 31 | body_id: LocalDefId, |
f035d41b XL |
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 | ||
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. | |
136023e0 | 56 | if !tcx.recursion_limit().value_within_limit(self.state.steps.len()) { |
f035d41b XL |
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: | |
49aad941 FG |
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.tcx.trait_solver_next() | |
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) | |
f035d41b | 82 | } else { |
49aad941 FG |
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 | }; | |
f035d41b XL |
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> { | |
109 | pub fn new( | |
2b03887a | 110 | infcx: &'a InferCtxt<'tcx>, |
f035d41b | 111 | param_env: ty::ParamEnv<'tcx>, |
9ffffee4 | 112 | body_def_id: LocalDefId, |
f035d41b XL |
113 | span: Span, |
114 | base_ty: Ty<'tcx>, | |
115 | ) -> Autoderef<'a, 'tcx> { | |
116 | Autoderef { | |
117 | infcx, | |
118 | span, | |
9ffffee4 | 119 | body_id: body_def_id, |
f035d41b XL |
120 | param_env, |
121 | state: AutoderefSnapshot { | |
122 | steps: vec![], | |
fc512014 | 123 | cur_ty: infcx.resolve_vars_if_possible(base_ty), |
f035d41b XL |
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 | ||
133 | fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { | |
134 | debug!("overloaded_deref_ty({:?})", ty); | |
f035d41b XL |
135 | let tcx = self.infcx.tcx; |
136 | ||
137 | // <ty as Deref> | |
49aad941 | 138 | let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]); |
f035d41b | 139 | let cause = traits::ObligationCause::misc(self.span, self.body_id); |
f035d41b | 140 | let obligation = traits::Obligation::new( |
487cf647 | 141 | tcx, |
f035d41b XL |
142 | cause.clone(), |
143 | self.param_env, | |
487cf647 | 144 | ty::Binder::dummy(trait_ref), |
f035d41b XL |
145 | ); |
146 | if !self.infcx.predicate_may_hold(&obligation) { | |
147 | debug!("overloaded_deref_ty: cannot match obligation"); | |
148 | return None; | |
149 | } | |
150 | ||
49aad941 FG |
151 | let (normalized_ty, obligations) = |
152 | self.structurally_normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, [ty]))?; | |
153 | debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); | |
154 | self.state.obligations.extend(obligations); | |
155 | ||
156 | Some(self.infcx.resolve_vars_if_possible(normalized_ty)) | |
157 | } | |
158 | ||
159 | #[instrument(level = "debug", skip(self), ret)] | |
160 | pub fn structurally_normalize( | |
161 | &self, | |
162 | ty: Ty<'tcx>, | |
163 | ) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> { | |
164 | let tcx = self.infcx.tcx; | |
165 | let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx); | |
166 | ||
167 | let cause = traits::ObligationCause::misc(self.span, self.body_id); | |
168 | let normalized_ty = match self | |
487cf647 FG |
169 | .infcx |
170 | .at(&cause, self.param_env) | |
49aad941 FG |
171 | .structurally_normalize(ty, &mut *fulfill_cx) |
172 | { | |
173 | Ok(normalized_ty) => normalized_ty, | |
174 | Err(errors) => { | |
175 | // This shouldn't happen, except for evaluate/fulfill mismatches, | |
176 | // but that's not a reason for an ICE (`predicate_may_hold` is conservative | |
177 | // by design). | |
178 | debug!(?errors, "encountered errors while fulfilling"); | |
179 | return None; | |
180 | } | |
181 | }; | |
182 | ||
183 | let errors = fulfill_cx.select_where_possible(&self.infcx); | |
3c0e092e | 184 | if !errors.is_empty() { |
f035d41b XL |
185 | // This shouldn't happen, except for evaluate/fulfill mismatches, |
186 | // but that's not a reason for an ICE (`predicate_may_hold` is conservative | |
187 | // by design). | |
49aad941 | 188 | debug!(?errors, "encountered errors while fulfilling"); |
f035d41b XL |
189 | return None; |
190 | } | |
f035d41b | 191 | |
49aad941 | 192 | Some((normalized_ty, fulfill_cx.pending_obligations())) |
f035d41b XL |
193 | } |
194 | ||
195 | /// Returns the final type we ended up with, which may be an inference | |
196 | /// variable (we will resolve it first, if we want). | |
197 | pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> { | |
198 | if resolve { | |
fc512014 | 199 | self.infcx.resolve_vars_if_possible(self.state.cur_ty) |
f035d41b XL |
200 | } else { |
201 | self.state.cur_ty | |
202 | } | |
203 | } | |
204 | ||
205 | pub fn step_count(&self) -> usize { | |
206 | self.state.steps.len() | |
207 | } | |
208 | ||
209 | pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> { | |
210 | self.state.obligations | |
211 | } | |
212 | ||
9c376795 FG |
213 | pub fn current_obligations(&self) -> Vec<traits::PredicateObligation<'tcx>> { |
214 | self.state.obligations.clone() | |
215 | } | |
216 | ||
f035d41b XL |
217 | pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] { |
218 | &self.state.steps | |
219 | } | |
220 | ||
221 | pub fn span(&self) -> Span { | |
3dfed10e | 222 | self.span |
1b1a35ee XL |
223 | } |
224 | ||
f035d41b XL |
225 | pub fn reached_recursion_limit(&self) -> bool { |
226 | self.state.reached_recursion_limit | |
227 | } | |
228 | ||
229 | /// also dereference through raw pointer types | |
230 | /// e.g., assuming ptr_to_Foo is the type `*const Foo` | |
231 | /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] | |
232 | /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] | |
233 | pub fn include_raw_pointers(mut self) -> Self { | |
234 | self.include_raw_pointers = true; | |
235 | self | |
236 | } | |
237 | ||
238 | pub fn silence_errors(mut self) -> Self { | |
239 | self.silence_errors = true; | |
240 | self | |
241 | } | |
242 | } | |
243 | ||
244 | pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { | |
245 | // We've reached the recursion limit, error gracefully. | |
c295e0f8 XL |
246 | let suggested_limit = match tcx.recursion_limit() { |
247 | Limit(0) => Limit(2), | |
248 | limit => limit * 2, | |
249 | }; | |
f2b60f7d | 250 | tcx.sess.emit_err(AutoDerefReachedRecursionLimit { |
5e7ed085 | 251 | span, |
f2b60f7d | 252 | ty, |
5e7ed085 | 253 | suggested_limit, |
f2b60f7d FG |
254 | crate_name: tcx.crate_name(LOCAL_CRATE), |
255 | }); | |
f035d41b | 256 | } |