]> git.proxmox.com Git - rustc.git/blame - src/librustc_trait_selection/autoderef.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_trait_selection / autoderef.rs
CommitLineData
f035d41b
XL
1use crate::traits::query::evaluate_obligation::InferCtxtExt;
2use crate::traits::{self, TraitEngine};
3use rustc_errors::struct_span_err;
4use rustc_hir as hir;
5use rustc_infer::infer::InferCtxt;
6use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness};
7use rustc_middle::ty::{ToPredicate, TypeFoldable};
8use rustc_session::DiagnosticMessageId;
3dfed10e 9use rustc_span::symbol::{sym, Ident};
f035d41b
XL
10use rustc_span::Span;
11
12#[derive(Copy, Clone, Debug)]
13pub enum AutoderefKind {
14 Builtin,
15 Overloaded,
16}
17
18struct 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
26pub 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
41impl<'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
94impl<'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
212pub 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}