]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/autoderef.rs
Rollup merge of #42302 - GuillaumeGomez:new-error-codes-next, r=Susurrus
[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::from_ref_and_name(
128                                                                tcx,
129                                                                trait_ref,
130                                                                Symbol::intern("Target"),
131                                                            ),
132                                                            cause,
133                                                            0);
134
135         debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
136         self.obligations.extend(normalized.obligations);
137
138         Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
139     }
140
141     /// Returns the final type, generating an error if it is an
142     /// unresolved inference variable.
143     pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
144         self.fcx.structurally_resolved_type(self.span, self.cur_ty)
145     }
146
147     /// Returns the final type we ended up with, which may well be an
148     /// inference variable (we will resolve it first, if possible).
149     pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> {
150         self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
151     }
152
153     pub fn finalize(self, pref: LvaluePreference, expr: &hir::Expr) {
154         let fcx = self.fcx;
155         fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, &[expr]));
156     }
157
158     pub fn finalize_as_infer_ok<E>(self, pref: LvaluePreference, exprs: &[E])
159                                    -> InferOk<'tcx, ()>
160         where E: AsCoercionSite
161     {
162         let Autoderef { fcx, span, mut obligations, steps, .. } = self;
163         let methods: Vec<_> = steps
164             .iter()
165             .map(|&(ty, kind)| {
166                 if let AutoderefKind::Overloaded = kind {
167                     fcx.try_overloaded_deref(span, None, ty, pref)
168                         .map(|InferOk { value, obligations: o }| {
169                             obligations.extend(o);
170                             value
171                         })
172                 } else {
173                     None
174                 }
175             })
176             .collect();
177
178         debug!("finalize({:?}) - {:?},{:?}",
179                pref,
180                methods,
181                obligations);
182
183         for expr in exprs {
184             let expr = expr.as_coercion_site();
185             debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
186             for (n, method) in methods.iter().enumerate() {
187                 if let &Some(method) = method {
188                     let method_call = MethodCall::autoderef(expr.id, n as u32);
189                     fcx.tables.borrow_mut().method_map.insert(method_call, method);
190                 }
191             }
192         }
193
194         InferOk {
195             value: (),
196             obligations
197         }
198     }
199 }
200
201 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
202     pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'gcx, 'tcx> {
203         Autoderef {
204             fcx: self,
205             steps: vec![],
206             cur_ty: self.resolve_type_vars_if_possible(&base_ty),
207             obligations: vec![],
208             at_start: true,
209             span: span,
210         }
211     }
212
213     pub fn try_overloaded_deref(&self,
214                                 span: Span,
215                                 base_expr: Option<&hir::Expr>,
216                                 base_ty: Ty<'tcx>,
217                                 pref: LvaluePreference)
218                                 -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
219         let rcvr = base_expr.map(|base_expr| super::AdjustedRcvr {
220             rcvr_expr: base_expr, autoderefs: 0, unsize: false
221         });
222
223         self.try_overloaded_lvalue_op(span, rcvr, base_ty, &[], pref, LvalueOp::Deref)
224     }
225 }