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