]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/autoderef.rs
Auto merge of #40009 - clarcharr:box_to_buf, r=alexcrichton
[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;
14
15 use rustc::infer::InferOk;
16 use rustc::traits;
17 use rustc::ty::{self, Ty, TraitRef};
18 use rustc::ty::{ToPredicate, TypeFoldable};
19 use rustc::ty::{MethodCall, MethodCallee};
20 use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
21 use rustc::hir;
22
23 use syntax_pos::Span;
24 use syntax::symbol::Symbol;
25
26 #[derive(Copy, Clone, Debug)]
27 enum AutoderefKind {
28     Builtin,
29     Overloaded,
30 }
31
32 pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
33     fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
34     steps: Vec<(Ty<'tcx>, AutoderefKind)>,
35     cur_ty: Ty<'tcx>,
36     obligations: Vec<traits::PredicateObligation<'tcx>>,
37     at_start: bool,
38     span: Span,
39 }
40
41 impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
42     type Item = (Ty<'tcx>, usize);
43
44     fn next(&mut self) -> Option<Self::Item> {
45         let tcx = self.fcx.tcx;
46
47         debug!("autoderef: steps={:?}, cur_ty={:?}",
48                self.steps,
49                self.cur_ty);
50         if self.at_start {
51             self.at_start = false;
52             debug!("autoderef stage #0 is {:?}", self.cur_ty);
53             return Some((self.cur_ty, 0));
54         }
55
56         if self.steps.len() == tcx.sess.recursion_limit.get() {
57             // We've reached the recursion limit, error gracefully.
58             let suggested_limit = tcx.sess.recursion_limit.get() * 2;
59             struct_span_err!(tcx.sess,
60                              self.span,
61                              E0055,
62                              "reached the recursion limit while auto-dereferencing {:?}",
63                              self.cur_ty)
64                 .span_label(self.span, &format!("deref recursion limit reached"))
65                 .help(&format!(
66                         "consider adding a `#[recursion_limit=\"{}\"]` attribute to your crate",
67                         suggested_limit))
68                 .emit();
69             return None;
70         }
71
72         if self.cur_ty.is_ty_var() {
73             return None;
74         }
75
76         // Otherwise, deref if type is derefable:
77         let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(false, NoPreference) {
78             (AutoderefKind::Builtin, mt.ty)
79         } else {
80             match self.overloaded_deref_ty(self.cur_ty) {
81                 Some(ty) => (AutoderefKind::Overloaded, ty),
82                 _ => return None,
83             }
84         };
85
86         if new_ty.references_error() {
87             return None;
88         }
89
90         self.steps.push((self.cur_ty, kind));
91         debug!("autoderef stage #{:?} is {:?} from {:?}",
92                self.steps.len(),
93                new_ty,
94                (self.cur_ty, kind));
95         self.cur_ty = new_ty;
96
97         Some((self.cur_ty, self.steps.len()))
98     }
99 }
100
101 impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
102     fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
103         debug!("overloaded_deref_ty({:?})", ty);
104
105         let tcx = self.fcx.tcx();
106
107         // <cur_ty as Deref>
108         let trait_ref = TraitRef {
109             def_id: match tcx.lang_items.deref_trait() {
110                 Some(f) => f,
111                 None => return None,
112             },
113             substs: tcx.mk_substs_trait(self.cur_ty, &[]),
114         };
115
116         let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
117
118         let mut selcx = traits::SelectionContext::new(self.fcx);
119         let obligation = traits::Obligation::new(cause.clone(), trait_ref.to_predicate());
120         if !selcx.evaluate_obligation(&obligation) {
121             debug!("overloaded_deref_ty: cannot match obligation");
122             return None;
123         }
124
125         let normalized = traits::normalize_projection_type(&mut selcx,
126                                                            ty::ProjectionTy {
127                                                                trait_ref: trait_ref,
128                                                                item_name: Symbol::intern("Target"),
129                                                            },
130                                                            cause,
131                                                            0);
132
133         debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
134         self.obligations.extend(normalized.obligations);
135
136         Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
137     }
138
139     /// Returns the final type, generating an error if it is an
140     /// unresolved inference variable.
141     pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
142         self.fcx.structurally_resolved_type(self.span, self.cur_ty)
143     }
144
145     /// Returns the final type we ended up with, which may well be an
146     /// inference variable (we will resolve it first, if possible).
147     pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> {
148         self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
149     }
150
151     pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
152         where I: IntoIterator<Item = &'b hir::Expr>
153     {
154         let fcx = self.fcx;
155         fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, exprs));
156     }
157
158     pub fn finalize_as_infer_ok<'b, I>(self, pref: LvaluePreference, exprs: I)
159                                        -> InferOk<'tcx, ()>
160         where I: IntoIterator<Item = &'b hir::Expr>
161     {
162         let methods: Vec<_> = self.steps
163             .iter()
164             .map(|&(ty, kind)| {
165                 if let AutoderefKind::Overloaded = kind {
166                     self.fcx.try_overloaded_deref(self.span, None, ty, pref)
167                 } else {
168                     None
169                 }
170             })
171             .collect();
172
173         debug!("finalize({:?}) - {:?},{:?}",
174                pref,
175                methods,
176                self.obligations);
177
178         for expr in exprs {
179             debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
180             for (n, method) in methods.iter().enumerate() {
181                 if let &Some(method) = method {
182                     let method_call = MethodCall::autoderef(expr.id, n as u32);
183                     self.fcx.tables.borrow_mut().method_map.insert(method_call, method);
184                 }
185             }
186         }
187
188         InferOk {
189             value: (),
190             obligations: self.obligations
191         }
192     }
193 }
194
195 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
196     pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'gcx, 'tcx> {
197         Autoderef {
198             fcx: self,
199             steps: vec![],
200             cur_ty: self.resolve_type_vars_if_possible(&base_ty),
201             obligations: vec![],
202             at_start: true,
203             span: span,
204         }
205     }
206
207     pub fn try_overloaded_deref(&self,
208                                 span: Span,
209                                 base_expr: Option<&hir::Expr>,
210                                 base_ty: Ty<'tcx>,
211                                 lvalue_pref: LvaluePreference)
212                                 -> Option<MethodCallee<'tcx>> {
213         debug!("try_overloaded_deref({:?},{:?},{:?},{:?})",
214                span,
215                base_expr,
216                base_ty,
217                lvalue_pref);
218         // Try DerefMut first, if preferred.
219         let method = match (lvalue_pref, self.tcx.lang_items.deref_mut_trait()) {
220             (PreferMutLvalue, Some(trait_did)) => {
221                 self.lookup_method_in_trait(span,
222                                             base_expr,
223                                             Symbol::intern("deref_mut"),
224                                             trait_did,
225                                             base_ty,
226                                             None)
227             }
228             _ => None,
229         };
230
231         // Otherwise, fall back to Deref.
232         let method = match (method, self.tcx.lang_items.deref_trait()) {
233             (None, Some(trait_did)) => {
234                 self.lookup_method_in_trait(span,
235                                             base_expr,
236                                             Symbol::intern("deref"),
237                                             trait_did,
238                                             base_ty,
239                                             None)
240             }
241             (method, _) => method,
242         };
243
244         method
245     }
246 }