]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | use crate::traits::query::evaluate_obligation::InferCtxtExt; |
2 | use crate::traits::{self, TraitEngine}; | |
3 | use rustc_errors::struct_span_err; | |
4 | use rustc_hir as hir; | |
5 | use rustc_infer::infer::InferCtxt; | |
6 | use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness}; | |
7 | use rustc_middle::ty::{ToPredicate, TypeFoldable}; | |
8 | use rustc_session::DiagnosticMessageId; | |
3dfed10e | 9 | use rustc_span::symbol::{sym, Ident}; |
f035d41b XL |
10 | use rustc_span::Span; |
11 | ||
12 | #[derive(Copy, Clone, Debug)] | |
13 | pub enum AutoderefKind { | |
14 | Builtin, | |
15 | Overloaded, | |
16 | } | |
17 | ||
18 | struct AutoderefSnapshot<'tcx> { | |
19 | at_start: bool, | |
20 | reached_recursion_limit: bool, | |
21 | steps: Vec<(Ty<'tcx>, AutoderefKind)>, | |
22 | cur_ty: Ty<'tcx>, | |
23 | obligations: Vec<traits::PredicateObligation<'tcx>>, | |
24 | } | |
25 | ||
26 | pub struct Autoderef<'a, 'tcx> { | |
27 | // Meta infos: | |
28 | infcx: &'a InferCtxt<'a, 'tcx>, | |
29 | span: Span, | |
30 | body_id: hir::HirId, | |
31 | param_env: ty::ParamEnv<'tcx>, | |
32 | ||
33 | // Current state: | |
34 | state: AutoderefSnapshot<'tcx>, | |
35 | ||
36 | // Configurations: | |
37 | include_raw_pointers: bool, | |
38 | silence_errors: bool, | |
39 | } | |
40 | ||
41 | impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { | |
42 | type Item = (Ty<'tcx>, usize); | |
43 | ||
44 | fn next(&mut self) -> Option<Self::Item> { | |
45 | let tcx = self.infcx.tcx; | |
46 | ||
47 | debug!("autoderef: steps={:?}, cur_ty={:?}", self.state.steps, self.state.cur_ty); | |
48 | if self.state.at_start { | |
49 | self.state.at_start = false; | |
50 | debug!("autoderef stage #0 is {:?}", self.state.cur_ty); | |
51 | return Some((self.state.cur_ty, 0)); | |
52 | } | |
53 | ||
54 | // If we have reached the recursion limit, error gracefully. | |
55 | if !tcx.sess.recursion_limit().value_within_limit(self.state.steps.len()) { | |
56 | if !self.silence_errors { | |
57 | report_autoderef_recursion_limit_error(tcx, self.span, self.state.cur_ty); | |
58 | } | |
59 | self.state.reached_recursion_limit = true; | |
60 | return None; | |
61 | } | |
62 | ||
63 | if self.state.cur_ty.is_ty_var() { | |
64 | return None; | |
65 | } | |
66 | ||
67 | // Otherwise, deref if type is derefable: | |
68 | let (kind, new_ty) = | |
69 | if let Some(mt) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { | |
70 | (AutoderefKind::Builtin, mt.ty) | |
71 | } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { | |
72 | (AutoderefKind::Overloaded, ty) | |
73 | } else { | |
74 | return None; | |
75 | }; | |
76 | ||
77 | if new_ty.references_error() { | |
78 | return None; | |
79 | } | |
80 | ||
81 | self.state.steps.push((self.state.cur_ty, kind)); | |
82 | debug!( | |
83 | "autoderef stage #{:?} is {:?} from {:?}", | |
84 | self.step_count(), | |
85 | new_ty, | |
86 | (self.state.cur_ty, kind) | |
87 | ); | |
88 | self.state.cur_ty = new_ty; | |
89 | ||
90 | Some((self.state.cur_ty, self.step_count())) | |
91 | } | |
92 | } | |
93 | ||
94 | impl<'a, 'tcx> Autoderef<'a, 'tcx> { | |
95 | pub fn new( | |
96 | infcx: &'a InferCtxt<'a, 'tcx>, | |
97 | param_env: ty::ParamEnv<'tcx>, | |
98 | body_id: hir::HirId, | |
99 | span: Span, | |
100 | base_ty: Ty<'tcx>, | |
101 | ) -> Autoderef<'a, 'tcx> { | |
102 | Autoderef { | |
103 | infcx, | |
104 | span, | |
105 | body_id, | |
106 | param_env, | |
107 | state: AutoderefSnapshot { | |
108 | steps: vec![], | |
109 | cur_ty: infcx.resolve_vars_if_possible(&base_ty), | |
110 | obligations: vec![], | |
111 | at_start: true, | |
112 | reached_recursion_limit: false, | |
113 | }, | |
114 | include_raw_pointers: false, | |
115 | silence_errors: false, | |
116 | } | |
117 | } | |
118 | ||
119 | fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { | |
120 | debug!("overloaded_deref_ty({:?})", ty); | |
121 | ||
122 | let tcx = self.infcx.tcx; | |
123 | ||
124 | // <ty as Deref> | |
125 | let trait_ref = TraitRef { | |
126 | def_id: tcx.lang_items().deref_trait()?, | |
127 | substs: tcx.mk_substs_trait(ty, &[]), | |
128 | }; | |
129 | ||
130 | let cause = traits::ObligationCause::misc(self.span, self.body_id); | |
131 | ||
132 | let obligation = traits::Obligation::new( | |
133 | cause.clone(), | |
134 | self.param_env, | |
135 | trait_ref.without_const().to_predicate(tcx), | |
136 | ); | |
137 | if !self.infcx.predicate_may_hold(&obligation) { | |
138 | debug!("overloaded_deref_ty: cannot match obligation"); | |
139 | return None; | |
140 | } | |
141 | ||
142 | let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot(); | |
143 | let normalized_ty = fulfillcx.normalize_projection_type( | |
144 | &self.infcx, | |
145 | self.param_env, | |
3dfed10e XL |
146 | ty::ProjectionTy::from_ref_and_name( |
147 | tcx, | |
148 | trait_ref, | |
149 | Ident::with_dummy_span(sym::Target), | |
150 | ), | |
f035d41b XL |
151 | cause, |
152 | ); | |
153 | if let Err(e) = fulfillcx.select_where_possible(&self.infcx) { | |
154 | // This shouldn't happen, except for evaluate/fulfill mismatches, | |
155 | // but that's not a reason for an ICE (`predicate_may_hold` is conservative | |
156 | // by design). | |
157 | debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", e); | |
158 | return None; | |
159 | } | |
160 | let obligations = fulfillcx.pending_obligations(); | |
161 | debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); | |
162 | self.state.obligations.extend(obligations); | |
163 | ||
164 | Some(self.infcx.resolve_vars_if_possible(&normalized_ty)) | |
165 | } | |
166 | ||
167 | /// Returns the final type we ended up with, which may be an inference | |
168 | /// variable (we will resolve it first, if we want). | |
169 | pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> { | |
170 | if resolve { | |
171 | self.infcx.resolve_vars_if_possible(&self.state.cur_ty) | |
172 | } else { | |
173 | self.state.cur_ty | |
174 | } | |
175 | } | |
176 | ||
177 | pub fn step_count(&self) -> usize { | |
178 | self.state.steps.len() | |
179 | } | |
180 | ||
181 | pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> { | |
182 | self.state.obligations | |
183 | } | |
184 | ||
185 | pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] { | |
186 | &self.state.steps | |
187 | } | |
188 | ||
189 | pub fn span(&self) -> Span { | |
3dfed10e | 190 | self.span |
f035d41b XL |
191 | } |
192 | ||
193 | pub fn reached_recursion_limit(&self) -> bool { | |
194 | self.state.reached_recursion_limit | |
195 | } | |
196 | ||
197 | /// also dereference through raw pointer types | |
198 | /// e.g., assuming ptr_to_Foo is the type `*const Foo` | |
199 | /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] | |
200 | /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] | |
201 | pub fn include_raw_pointers(mut self) -> Self { | |
202 | self.include_raw_pointers = true; | |
203 | self | |
204 | } | |
205 | ||
206 | pub fn silence_errors(mut self) -> Self { | |
207 | self.silence_errors = true; | |
208 | self | |
209 | } | |
210 | } | |
211 | ||
212 | pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { | |
213 | // We've reached the recursion limit, error gracefully. | |
214 | let suggested_limit = tcx.sess.recursion_limit() * 2; | |
215 | let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty); | |
216 | let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg); | |
217 | let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); | |
218 | if fresh { | |
219 | struct_span_err!( | |
220 | tcx.sess, | |
221 | span, | |
222 | E0055, | |
223 | "reached the recursion limit while auto-dereferencing `{:?}`", | |
224 | ty | |
225 | ) | |
226 | .span_label(span, "deref recursion limit reached") | |
227 | .help(&format!( | |
228 | "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)", | |
229 | suggested_limit, tcx.crate_name, | |
230 | )) | |
231 | .emit(); | |
232 | } | |
233 | } |