]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/autoderef.rs
Auto merge of #41764 - scottmcm:faster-reverse, r=brson
[rust.git] / src / librustc_typeck / check / autoderef.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use astconv::AstConv;
12
13 use super::{FnCtxt, LvalueOp};
14
15 use check::coercion::AsCoercionSite;
16 use rustc::infer::InferOk;
17 use rustc::traits;
18 use rustc::ty::{self, Ty, TraitRef};
19 use rustc::ty::{ToPredicate, TypeFoldable};
20 use rustc::ty::{MethodCall, MethodCallee};
21 use rustc::ty::{LvaluePreference, NoPreference};
22 use rustc::hir;
23
24 use syntax_pos::Span;
25 use syntax::symbol::Symbol;
26
27 #[derive(Copy, Clone, Debug)]
28 enum AutoderefKind {
29     Builtin,
30     Overloaded,
31 }
32
33 pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
34     fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
35     steps: Vec<(Ty<'tcx>, AutoderefKind)>,
36     cur_ty: Ty<'tcx>,
37     obligations: Vec<traits::PredicateObligation<'tcx>>,
38     at_start: bool,
39     span: Span,
40 }
41
42 impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
43     type Item = (Ty<'tcx>, usize);
44
45     fn next(&mut self) -> Option<Self::Item> {
46         let tcx = self.fcx.tcx;
47
48         debug!("autoderef: steps={:?}, cur_ty={:?}",
49                self.steps,
50                self.cur_ty);
51         if self.at_start {
52             self.at_start = false;
53             debug!("autoderef stage #0 is {:?}", self.cur_ty);
54             return Some((self.cur_ty, 0));
55         }
56
57         if self.steps.len() == tcx.sess.recursion_limit.get() {
58             // We've reached the recursion limit, error gracefully.
59             let suggested_limit = tcx.sess.recursion_limit.get() * 2;
60             struct_span_err!(tcx.sess,
61                              self.span,
62                              E0055,
63                              "reached the recursion limit while auto-dereferencing {:?}",
64                              self.cur_ty)
65                 .span_label(self.span, "deref recursion limit reached")
66                 .help(&format!(
67                         "consider adding a `#[recursion_limit=\"{}\"]` attribute to your crate",
68                         suggested_limit))
69                 .emit();
70             return None;
71         }
72
73         if self.cur_ty.is_ty_var() {
74             return None;
75         }
76
77         // Otherwise, deref if type is derefable:
78         let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(false, NoPreference) {
79             (AutoderefKind::Builtin, mt.ty)
80         } else {
81             match self.overloaded_deref_ty(self.cur_ty) {
82                 Some(ty) => (AutoderefKind::Overloaded, ty),
83                 _ => return None,
84             }
85         };
86
87         if new_ty.references_error() {
88             return None;
89         }
90
91         self.steps.push((self.cur_ty, kind));
92         debug!("autoderef stage #{:?} is {:?} from {:?}",
93                self.steps.len(),
94                new_ty,
95                (self.cur_ty, kind));
96         self.cur_ty = new_ty;
97
98         Some((self.cur_ty, self.steps.len()))
99     }
100 }
101
102 impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
103     fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
104         debug!("overloaded_deref_ty({:?})", ty);
105
106         let tcx = self.fcx.tcx();
107
108         // <cur_ty as Deref>
109         let trait_ref = TraitRef {
110             def_id: match tcx.lang_items.deref_trait() {
111                 Some(f) => f,
112                 None => return None,
113             },
114             substs: tcx.mk_substs_trait(self.cur_ty, &[]),
115         };
116
117         let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
118
119         let mut selcx = traits::SelectionContext::new(self.fcx);
120         let obligation = traits::Obligation::new(cause.clone(), trait_ref.to_predicate());
121         if !selcx.evaluate_obligation(&obligation) {
122             debug!("overloaded_deref_ty: cannot match obligation");
123             return None;
124         }
125
126         let normalized = traits::normalize_projection_type(&mut selcx,
127                                                            ty::ProjectionTy {
128                                                                trait_ref: trait_ref,
129                                                                item_name: Symbol::intern("Target"),
130                                                            },
131                                                            cause,
132                                                            0);
133
134         debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
135         self.obligations.extend(normalized.obligations);
136
137         Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
138     }
139
140     /// Returns the final type, generating an error if it is an
141     /// unresolved inference variable.
142     pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
143         self.fcx.structurally_resolved_type(self.span, self.cur_ty)
144     }
145
146     /// Returns the final type we ended up with, which may well be an
147     /// inference variable (we will resolve it first, if possible).
148     pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> {
149         self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
150     }
151
152     pub fn finalize(self, pref: LvaluePreference, expr: &hir::Expr) {
153         let fcx = self.fcx;
154         fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, &[expr]));
155     }
156
157     pub fn finalize_as_infer_ok<E>(self, pref: LvaluePreference, exprs: &[E])
158                                    -> InferOk<'tcx, ()>
159         where E: AsCoercionSite
160     {
161         let Autoderef { fcx, span, mut obligations, steps, .. } = self;
162         let methods: Vec<_> = steps
163             .iter()
164             .map(|&(ty, kind)| {
165                 if let AutoderefKind::Overloaded = kind {
166                     fcx.try_overloaded_deref(span, None, ty, pref)
167                         .map(|InferOk { value, obligations: o }| {
168                             obligations.extend(o);
169                             value
170                         })
171                 } else {
172                     None
173                 }
174             })
175             .collect();
176
177         debug!("finalize({:?}) - {:?},{:?}",
178                pref,
179                methods,
180                obligations);
181
182         for expr in exprs {
183             let expr = expr.as_coercion_site();
184             debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
185             for (n, method) in methods.iter().enumerate() {
186                 if let &Some(method) = method {
187                     let method_call = MethodCall::autoderef(expr.id, n as u32);
188                     fcx.tables.borrow_mut().method_map.insert(method_call, method);
189                 }
190             }
191         }
192
193         InferOk {
194             value: (),
195             obligations
196         }
197     }
198 }
199
200 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
201     pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'gcx, 'tcx> {
202         Autoderef {
203             fcx: self,
204             steps: vec![],
205             cur_ty: self.resolve_type_vars_if_possible(&base_ty),
206             obligations: vec![],
207             at_start: true,
208             span: span,
209         }
210     }
211
212     pub fn try_overloaded_deref(&self,
213                                 span: Span,
214                                 base_expr: Option<&hir::Expr>,
215                                 base_ty: Ty<'tcx>,
216                                 pref: LvaluePreference)
217                                 -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
218         let rcvr = base_expr.map(|base_expr| super::AdjustedRcvr {
219             rcvr_expr: base_expr, autoderefs: 0, unsize: false
220         });
221
222         self.try_overloaded_lvalue_op(span, rcvr, base_ty, &[], pref, LvalueOp::Deref)
223     }
224 }