]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/autoderef.rs
Auto merge of #86765 - cuviper:fuse-less-specialized, r=joshtriplett
[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, WithConstness};
7 use rustc_middle::ty::{ToPredicate, TypeFoldable};
8 use rustc_session::DiagnosticMessageId;
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             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         if let Err(e) = fulfillcx.select_where_possible(&self.infcx) {
156             // This shouldn't happen, except for evaluate/fulfill mismatches,
157             // but that's not a reason for an ICE (`predicate_may_hold` is conservative
158             // by design).
159             debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", e);
160             return None;
161         }
162         let obligations = fulfillcx.pending_obligations();
163         debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
164         self.state.obligations.extend(obligations);
165
166         Some(self.infcx.resolve_vars_if_possible(normalized_ty))
167     }
168
169     /// Returns the final type we ended up with, which may be an inference
170     /// variable (we will resolve it first, if we want).
171     pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> {
172         if resolve {
173             self.infcx.resolve_vars_if_possible(self.state.cur_ty)
174         } else {
175             self.state.cur_ty
176         }
177     }
178
179     pub fn step_count(&self) -> usize {
180         self.state.steps.len()
181     }
182
183     pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> {
184         self.state.obligations
185     }
186
187     pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] {
188         &self.state.steps
189     }
190
191     pub fn span(&self) -> Span {
192         self.span
193     }
194
195     pub fn overloaded_span(&self) -> Span {
196         self.overloaded_span
197     }
198
199     pub fn reached_recursion_limit(&self) -> bool {
200         self.state.reached_recursion_limit
201     }
202
203     /// also dereference through raw pointer types
204     /// e.g., assuming ptr_to_Foo is the type `*const Foo`
205     /// fcx.autoderef(span, ptr_to_Foo)  => [*const Foo]
206     /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo]
207     pub fn include_raw_pointers(mut self) -> Self {
208         self.include_raw_pointers = true;
209         self
210     }
211
212     pub fn silence_errors(mut self) -> Self {
213         self.silence_errors = true;
214         self
215     }
216 }
217
218 pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
219     // We've reached the recursion limit, error gracefully.
220     let suggested_limit = tcx.recursion_limit() * 2;
221     let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty);
222     let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg);
223     let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
224     if fresh {
225         struct_span_err!(
226             tcx.sess,
227             span,
228             E0055,
229             "reached the recursion limit while auto-dereferencing `{:?}`",
230             ty
231         )
232         .span_label(span, "deref recursion limit reached")
233         .help(&format!(
234             "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
235             suggested_limit,
236             tcx.crate_name(LOCAL_CRATE),
237         ))
238         .emit();
239     }
240 }