1 use crate::traits
::query
::evaluate_obligation
::InferCtxtExt
;
2 use crate::traits
::{self, TraitEngine}
;
3 use rustc_errors
::struct_span_err
;
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
;
12 #[derive(Copy, Clone, Debug)]
13 pub enum AutoderefKind
{
18 struct AutoderefSnapshot
<'tcx
> {
20 reached_recursion_limit
: bool
,
21 steps
: Vec
<(Ty
<'tcx
>, AutoderefKind
)>,
23 obligations
: Vec
<traits
::PredicateObligation
<'tcx
>>,
26 pub struct Autoderef
<'a
, 'tcx
> {
28 infcx
: &'a InferCtxt
<'a
, 'tcx
>,
30 overloaded_span
: Span
,
32 param_env
: ty
::ParamEnv
<'tcx
>,
35 state
: AutoderefSnapshot
<'tcx
>,
38 include_raw_pointers
: bool
,
42 impl<'a
, 'tcx
> Iterator
for Autoderef
<'a
, 'tcx
> {
43 type Item
= (Ty
<'tcx
>, usize);
45 fn next(&mut self) -> Option
<Self::Item
> {
46 let tcx
= self.infcx
.tcx
;
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));
55 // If we have reached the recursion limit, error gracefully.
56 if !tcx
.sess
.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
);
60 self.state
.reached_recursion_limit
= true;
64 if self.state
.cur_ty
.is_ty_var() {
68 // Otherwise, deref if type is derefable:
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
)
78 if new_ty
.references_error() {
82 self.state
.steps
.push((self.state
.cur_ty
, kind
));
84 "autoderef stage #{:?} is {:?} from {:?}",
87 (self.state
.cur_ty
, kind
)
89 self.state
.cur_ty
= new_ty
;
91 Some((self.state
.cur_ty
, self.step_count()))
95 impl<'a
, 'tcx
> Autoderef
<'a
, 'tcx
> {
97 infcx
: &'a InferCtxt
<'a
, 'tcx
>,
98 param_env
: ty
::ParamEnv
<'tcx
>,
102 overloaded_span
: Span
,
103 ) -> Autoderef
<'a
, 'tcx
> {
110 state
: AutoderefSnapshot
{
112 cur_ty
: infcx
.resolve_vars_if_possible(base_ty
),
115 reached_recursion_limit
: false,
117 include_raw_pointers
: false,
118 silence_errors
: false,
122 fn overloaded_deref_ty(&mut self, ty
: Ty
<'tcx
>) -> Option
<Ty
<'tcx
>> {
123 debug
!("overloaded_deref_ty({:?})", ty
);
125 let tcx
= self.infcx
.tcx
;
128 let trait_ref
= TraitRef
{
129 def_id
: tcx
.lang_items().deref_trait()?
,
130 substs
: tcx
.mk_substs_trait(ty
, &[]),
133 let cause
= traits
::ObligationCause
::misc(self.span
, self.body_id
);
135 let obligation
= traits
::Obligation
::new(
138 trait_ref
.without_const().to_predicate(tcx
),
140 if !self.infcx
.predicate_may_hold(&obligation
) {
141 debug
!("overloaded_deref_ty: cannot match obligation");
145 let mut fulfillcx
= traits
::FulfillmentContext
::new_in_snapshot();
146 let normalized_ty
= fulfillcx
.normalize_projection_type(
150 item_def_id
: tcx
.lang_items().deref_target()?
,
151 substs
: trait_ref
.substs
,
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
159 debug
!("overloaded_deref_ty: encountered errors {:?} while fulfilling", e
);
162 let obligations
= fulfillcx
.pending_obligations();
163 debug
!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty
, normalized_ty
, obligations
);
164 self.state
.obligations
.extend(obligations
);
166 Some(self.infcx
.resolve_vars_if_possible(normalized_ty
))
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
> {
173 self.infcx
.resolve_vars_if_possible(self.state
.cur_ty
)
179 pub fn step_count(&self) -> usize {
180 self.state
.steps
.len()
183 pub fn into_obligations(self) -> Vec
<traits
::PredicateObligation
<'tcx
>> {
184 self.state
.obligations
187 pub fn steps(&self) -> &[(Ty
<'tcx
>, AutoderefKind
)] {
191 pub fn span(&self) -> Span
{
195 pub fn overloaded_span(&self) -> Span
{
199 pub fn reached_recursion_limit(&self) -> bool
{
200 self.state
.reached_recursion_limit
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;
212 pub fn silence_errors(mut self) -> Self {
213 self.silence_errors
= true;
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
.sess
.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
);
229 "reached the recursion limit while auto-dereferencing `{:?}`",
232 .span_label(span
, "deref recursion limit reached")
234 "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
236 tcx
.crate_name(LOCAL_CRATE
),