]>
Commit | Line | Data |
---|---|---|
f2b60f7d | 1 | use crate::errors::AutoDerefReachedRecursionLimit; |
f035d41b | 2 | use crate::traits::query::evaluate_obligation::InferCtxtExt; |
487cf647 FG |
3 | use crate::traits::NormalizeExt; |
4 | use crate::traits::{self, TraitEngine, TraitEngineExt}; | |
f035d41b XL |
5 | use rustc_hir as hir; |
6 | use rustc_infer::infer::InferCtxt; | |
487cf647 FG |
7 | use rustc_middle::ty::TypeVisitable; |
8 | use rustc_middle::ty::{self, Ty, TyCtxt}; | |
5e7ed085 | 9 | use rustc_session::Limit; |
17df50a5 | 10 | use rustc_span::def_id::LOCAL_CRATE; |
f035d41b XL |
11 | use rustc_span::Span; |
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 XL |
30 | span: Span, |
31 | body_id: hir::HirId, | |
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: | |
69 | let (kind, new_ty) = | |
70 | if let Some(mt) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { | |
71 | (AutoderefKind::Builtin, mt.ty) | |
72 | } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { | |
73 | (AutoderefKind::Overloaded, ty) | |
74 | } else { | |
75 | return None; | |
76 | }; | |
77 | ||
78 | if new_ty.references_error() { | |
79 | return None; | |
80 | } | |
81 | ||
82 | self.state.steps.push((self.state.cur_ty, kind)); | |
83 | debug!( | |
84 | "autoderef stage #{:?} is {:?} from {:?}", | |
85 | self.step_count(), | |
86 | new_ty, | |
87 | (self.state.cur_ty, kind) | |
88 | ); | |
89 | self.state.cur_ty = new_ty; | |
90 | ||
91 | Some((self.state.cur_ty, self.step_count())) | |
92 | } | |
93 | } | |
94 | ||
95 | impl<'a, 'tcx> Autoderef<'a, 'tcx> { | |
96 | pub fn new( | |
2b03887a | 97 | infcx: &'a InferCtxt<'tcx>, |
f035d41b XL |
98 | param_env: ty::ParamEnv<'tcx>, |
99 | body_id: hir::HirId, | |
100 | span: Span, | |
101 | base_ty: Ty<'tcx>, | |
102 | ) -> Autoderef<'a, 'tcx> { | |
103 | Autoderef { | |
104 | infcx, | |
105 | span, | |
106 | body_id, | |
107 | param_env, | |
108 | state: AutoderefSnapshot { | |
109 | steps: vec![], | |
fc512014 | 110 | cur_ty: infcx.resolve_vars_if_possible(base_ty), |
f035d41b XL |
111 | obligations: vec![], |
112 | at_start: true, | |
113 | reached_recursion_limit: false, | |
114 | }, | |
115 | include_raw_pointers: false, | |
116 | silence_errors: false, | |
117 | } | |
118 | } | |
119 | ||
120 | fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { | |
121 | debug!("overloaded_deref_ty({:?})", ty); | |
122 | ||
123 | let tcx = self.infcx.tcx; | |
124 | ||
125 | // <ty as Deref> | |
487cf647 | 126 | let trait_ref = tcx.mk_trait_ref(tcx.lang_items().deref_trait()?, [ty]); |
f035d41b XL |
127 | |
128 | let cause = traits::ObligationCause::misc(self.span, self.body_id); | |
129 | ||
130 | let obligation = traits::Obligation::new( | |
487cf647 | 131 | tcx, |
f035d41b XL |
132 | cause.clone(), |
133 | self.param_env, | |
487cf647 | 134 | ty::Binder::dummy(trait_ref), |
f035d41b XL |
135 | ); |
136 | if !self.infcx.predicate_may_hold(&obligation) { | |
137 | debug!("overloaded_deref_ty: cannot match obligation"); | |
138 | return None; | |
139 | } | |
140 | ||
487cf647 FG |
141 | let normalized_ty = self |
142 | .infcx | |
143 | .at(&cause, self.param_env) | |
144 | .normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, trait_ref.substs)); | |
145 | let mut fulfillcx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx); | |
146 | let normalized_ty = | |
147 | normalized_ty.into_value_registering_obligations(self.infcx, &mut *fulfillcx); | |
3c0e092e XL |
148 | let errors = fulfillcx.select_where_possible(&self.infcx); |
149 | if !errors.is_empty() { | |
f035d41b XL |
150 | // This shouldn't happen, except for evaluate/fulfill mismatches, |
151 | // but that's not a reason for an ICE (`predicate_may_hold` is conservative | |
152 | // by design). | |
3c0e092e | 153 | debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", errors); |
f035d41b XL |
154 | return None; |
155 | } | |
156 | let obligations = fulfillcx.pending_obligations(); | |
157 | debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); | |
158 | self.state.obligations.extend(obligations); | |
159 | ||
fc512014 | 160 | Some(self.infcx.resolve_vars_if_possible(normalized_ty)) |
f035d41b XL |
161 | } |
162 | ||
163 | /// Returns the final type we ended up with, which may be an inference | |
164 | /// variable (we will resolve it first, if we want). | |
165 | pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> { | |
166 | if resolve { | |
fc512014 | 167 | self.infcx.resolve_vars_if_possible(self.state.cur_ty) |
f035d41b XL |
168 | } else { |
169 | self.state.cur_ty | |
170 | } | |
171 | } | |
172 | ||
173 | pub fn step_count(&self) -> usize { | |
174 | self.state.steps.len() | |
175 | } | |
176 | ||
177 | pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> { | |
178 | self.state.obligations | |
179 | } | |
180 | ||
181 | pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] { | |
182 | &self.state.steps | |
183 | } | |
184 | ||
185 | pub fn span(&self) -> Span { | |
3dfed10e | 186 | self.span |
1b1a35ee XL |
187 | } |
188 | ||
f035d41b XL |
189 | pub fn reached_recursion_limit(&self) -> bool { |
190 | self.state.reached_recursion_limit | |
191 | } | |
192 | ||
193 | /// also dereference through raw pointer types | |
194 | /// e.g., assuming ptr_to_Foo is the type `*const Foo` | |
195 | /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] | |
196 | /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] | |
197 | pub fn include_raw_pointers(mut self) -> Self { | |
198 | self.include_raw_pointers = true; | |
199 | self | |
200 | } | |
201 | ||
202 | pub fn silence_errors(mut self) -> Self { | |
203 | self.silence_errors = true; | |
204 | self | |
205 | } | |
206 | } | |
207 | ||
208 | pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { | |
209 | // We've reached the recursion limit, error gracefully. | |
c295e0f8 XL |
210 | let suggested_limit = match tcx.recursion_limit() { |
211 | Limit(0) => Limit(2), | |
212 | limit => limit * 2, | |
213 | }; | |
f2b60f7d | 214 | tcx.sess.emit_err(AutoDerefReachedRecursionLimit { |
5e7ed085 | 215 | span, |
f2b60f7d | 216 | ty, |
5e7ed085 | 217 | suggested_limit, |
f2b60f7d FG |
218 | crate_name: tcx.crate_name(LOCAL_CRATE), |
219 | }); | |
f035d41b | 220 | } |