]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/autoderef.rs
Rollup merge of #93649 - WaffleLapkin:regression_test_80309, r=oli-obk
[rust.git] / compiler / rustc_trait_selection / src / autoderef.rs
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};
7 use rustc_middle::ty::{ToPredicate, TypeFoldable};
8 use rustc_session::{DiagnosticMessageId, Limit};
9 use rustc_span::def_id::LOCAL_CRATE;
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     overloaded_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.
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) =
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(
97         infcx: &'a InferCtxt<'a, 'tcx>,
98         param_env: ty::ParamEnv<'tcx>,
99         body_id: hir::HirId,
100         span: Span,
101         base_ty: Ty<'tcx>,
102         overloaded_span: Span,
103     ) -> Autoderef<'a, 'tcx> {
104         Autoderef {
105             infcx,
106             span,
107             overloaded_span,
108             body_id,
109             param_env,
110             state: AutoderefSnapshot {
111                 steps: vec![],
112                 cur_ty: infcx.resolve_vars_if_possible(base_ty),
113                 obligations: vec![],
114                 at_start: true,
115                 reached_recursion_limit: false,
116             },
117             include_raw_pointers: false,
118             silence_errors: false,
119         }
120     }
121
122     fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
123         debug!("overloaded_deref_ty({:?})", ty);
124
125         let tcx = self.infcx.tcx;
126
127         // <ty as Deref>
128         let trait_ref = TraitRef {
129             def_id: tcx.lang_items().deref_trait()?,
130             substs: tcx.mk_substs_trait(ty, &[]),
131         };
132
133         let cause = traits::ObligationCause::misc(self.span, self.body_id);
134
135         let obligation = traits::Obligation::new(
136             cause.clone(),
137             self.param_env,
138             ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx),
139         );
140         if !self.infcx.predicate_may_hold(&obligation) {
141             debug!("overloaded_deref_ty: cannot match obligation");
142             return None;
143         }
144
145         let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
146         let normalized_ty = fulfillcx.normalize_projection_type(
147             &self.infcx,
148             self.param_env,
149             ty::ProjectionTy {
150                 item_def_id: tcx.lang_items().deref_target()?,
151                 substs: trait_ref.substs,
152             },
153             cause,
154         );
155         let errors = fulfillcx.select_where_possible(&self.infcx);
156         if !errors.is_empty() {
157             // This shouldn't happen, except for evaluate/fulfill mismatches,
158             // but that's not a reason for an ICE (`predicate_may_hold` is conservative
159             // by design).
160             debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", errors);
161             return None;
162         }
163         let obligations = fulfillcx.pending_obligations();
164         debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
165         self.state.obligations.extend(obligations);
166
167         Some(self.infcx.resolve_vars_if_possible(normalized_ty))
168     }
169
170     /// Returns the final type we ended up with, which may be an inference
171     /// variable (we will resolve it first, if we want).
172     pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> {
173         if resolve {
174             self.infcx.resolve_vars_if_possible(self.state.cur_ty)
175         } else {
176             self.state.cur_ty
177         }
178     }
179
180     pub fn step_count(&self) -> usize {
181         self.state.steps.len()
182     }
183
184     pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> {
185         self.state.obligations
186     }
187
188     pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] {
189         &self.state.steps
190     }
191
192     pub fn span(&self) -> Span {
193         self.span
194     }
195
196     pub fn overloaded_span(&self) -> Span {
197         self.overloaded_span
198     }
199
200     pub fn reached_recursion_limit(&self) -> bool {
201         self.state.reached_recursion_limit
202     }
203
204     /// also dereference through raw pointer types
205     /// e.g., assuming ptr_to_Foo is the type `*const Foo`
206     /// fcx.autoderef(span, ptr_to_Foo)  => [*const Foo]
207     /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo]
208     pub fn include_raw_pointers(mut self) -> Self {
209         self.include_raw_pointers = true;
210         self
211     }
212
213     pub fn silence_errors(mut self) -> Self {
214         self.silence_errors = true;
215         self
216     }
217 }
218
219 pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
220     // We've reached the recursion limit, error gracefully.
221     let suggested_limit = match tcx.recursion_limit() {
222         Limit(0) => Limit(2),
223         limit => limit * 2,
224     };
225     let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty);
226     let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg);
227     let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
228     if fresh {
229         struct_span_err!(
230             tcx.sess,
231             span,
232             E0055,
233             "reached the recursion limit while auto-dereferencing `{:?}`",
234             ty
235         )
236         .span_label(span, "deref recursion limit reached")
237         .help(&format!(
238             "consider increasing the recursion limit by adding a \
239              `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
240             suggested_limit,
241             tcx.crate_name(LOCAL_CRATE),
242         ))
243         .emit();
244     }
245 }