]>
Commit | Line | Data |
---|---|---|
3157f602 XL |
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::traits; | |
16 | use rustc::ty::{self, Ty, TraitRef}; | |
17 | use rustc::ty::{ToPredicate, TypeFoldable}; | |
18 | use rustc::ty::{MethodCall, MethodCallee}; | |
19 | use rustc::ty::subst::Substs; | |
20 | use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue}; | |
21 | use rustc::hir; | |
22 | ||
23 | use syntax_pos::Span; | |
24 | use syntax::parse::token; | |
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, self.cur_ty); | |
49 | if self.at_start { | |
50 | self.at_start = false; | |
51 | debug!("autoderef stage #0 is {:?}", self.cur_ty); | |
52 | return Some((self.cur_ty, 0)); | |
53 | } | |
54 | ||
55 | if self.steps.len() == tcx.sess.recursion_limit.get() { | |
56 | // We've reached the recursion limit, error gracefully. | |
5bcae85e | 57 | struct_span_err!(tcx.sess, self.span, E0055, |
3157f602 | 58 | "reached the recursion limit while auto-dereferencing {:?}", |
5bcae85e SL |
59 | self.cur_ty) |
60 | .span_label(self.span, &format!("deref recursion limit reached")) | |
61 | .emit(); | |
3157f602 XL |
62 | return None; |
63 | } | |
64 | ||
65 | if self.cur_ty.is_ty_var() { | |
66 | return None; | |
67 | } | |
68 | ||
69 | // Otherwise, deref if type is derefable: | |
70 | let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(false, NoPreference) { | |
71 | (AutoderefKind::Builtin, mt.ty) | |
72 | } else { | |
73 | match self.overloaded_deref_ty(self.cur_ty) { | |
74 | Some(ty) => (AutoderefKind::Overloaded, ty), | |
75 | _ => return None | |
76 | } | |
77 | }; | |
78 | ||
79 | if new_ty.references_error() { | |
80 | return None; | |
81 | } | |
82 | ||
83 | self.steps.push((self.cur_ty, kind)); | |
84 | debug!("autoderef stage #{:?} is {:?} from {:?}", self.steps.len(), | |
85 | new_ty, (self.cur_ty, kind)); | |
86 | self.cur_ty = new_ty; | |
87 | ||
88 | Some((self.cur_ty, self.steps.len())) | |
89 | } | |
90 | } | |
91 | ||
92 | impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { | |
93 | fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { | |
94 | debug!("overloaded_deref_ty({:?})", ty); | |
95 | ||
96 | let tcx = self.fcx.tcx(); | |
97 | ||
98 | // <cur_ty as Deref> | |
99 | let trait_ref = TraitRef { | |
100 | def_id: match tcx.lang_items.deref_trait() { | |
101 | Some(f) => f, | |
102 | None => return None | |
103 | }, | |
9e0c209e | 104 | substs: Substs::new_trait(tcx, self.cur_ty, &[]) |
3157f602 XL |
105 | }; |
106 | ||
107 | let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id); | |
108 | ||
109 | let mut selcx = traits::SelectionContext::new(self.fcx); | |
110 | let obligation = traits::Obligation::new(cause.clone(), trait_ref.to_predicate()); | |
111 | if !selcx.evaluate_obligation(&obligation) { | |
112 | debug!("overloaded_deref_ty: cannot match obligation"); | |
113 | return None; | |
114 | } | |
115 | ||
116 | let normalized = traits::normalize_projection_type( | |
117 | &mut selcx, | |
118 | ty::ProjectionTy { | |
119 | trait_ref: trait_ref, | |
120 | item_name: token::intern("Target") | |
121 | }, | |
122 | cause, | |
123 | 0 | |
124 | ); | |
125 | ||
126 | debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized); | |
127 | self.obligations.extend(normalized.obligations); | |
128 | ||
129 | Some(self.fcx.resolve_type_vars_if_possible(&normalized.value)) | |
130 | } | |
131 | ||
132 | pub fn unambiguous_final_ty(&self) -> Ty<'tcx> { | |
133 | self.fcx.structurally_resolved_type(self.span, self.cur_ty) | |
134 | } | |
135 | ||
136 | pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I) | |
137 | where I: IntoIterator<Item=&'b hir::Expr> | |
138 | { | |
139 | let methods : Vec<_> = self.steps.iter().map(|&(ty, kind)| { | |
140 | if let AutoderefKind::Overloaded = kind { | |
141 | self.fcx.try_overloaded_deref(self.span, None, ty, pref) | |
142 | } else { | |
143 | None | |
144 | } | |
145 | }).collect(); | |
146 | ||
147 | debug!("finalize({:?}) - {:?},{:?}", pref, methods, self.obligations); | |
148 | ||
149 | for expr in exprs { | |
150 | debug!("finalize - finalizing #{} - {:?}", expr.id, expr); | |
151 | for (n, method) in methods.iter().enumerate() { | |
152 | if let &Some(method) = method { | |
153 | let method_call = MethodCall::autoderef(expr.id, n as u32); | |
154 | self.fcx.tables.borrow_mut().method_map.insert(method_call, method); | |
155 | } | |
156 | } | |
157 | } | |
158 | ||
159 | for obligation in self.obligations { | |
160 | self.fcx.register_predicate(obligation); | |
161 | } | |
162 | } | |
163 | } | |
164 | ||
165 | impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { | |
166 | pub fn autoderef(&'a self, | |
167 | span: Span, | |
168 | base_ty: Ty<'tcx>) | |
169 | -> Autoderef<'a, 'gcx, 'tcx> | |
170 | { | |
171 | Autoderef { | |
172 | fcx: self, | |
173 | steps: vec![], | |
174 | cur_ty: self.resolve_type_vars_if_possible(&base_ty), | |
175 | obligations: vec![], | |
176 | at_start: true, | |
177 | span: span | |
178 | } | |
179 | } | |
180 | ||
181 | pub fn try_overloaded_deref(&self, | |
182 | span: Span, | |
183 | base_expr: Option<&hir::Expr>, | |
184 | base_ty: Ty<'tcx>, | |
185 | lvalue_pref: LvaluePreference) | |
186 | -> Option<MethodCallee<'tcx>> | |
187 | { | |
188 | debug!("try_overloaded_deref({:?},{:?},{:?},{:?})", | |
189 | span, base_expr, base_ty, lvalue_pref); | |
190 | // Try DerefMut first, if preferred. | |
191 | let method = match (lvalue_pref, self.tcx.lang_items.deref_mut_trait()) { | |
192 | (PreferMutLvalue, Some(trait_did)) => { | |
193 | self.lookup_method_in_trait(span, base_expr, | |
194 | token::intern("deref_mut"), trait_did, | |
195 | base_ty, None) | |
196 | } | |
197 | _ => None | |
198 | }; | |
199 | ||
200 | // Otherwise, fall back to Deref. | |
201 | let method = match (method, self.tcx.lang_items.deref_trait()) { | |
202 | (None, Some(trait_did)) => { | |
203 | self.lookup_method_in_trait(span, base_expr, | |
204 | token::intern("deref"), trait_did, | |
205 | base_ty, None) | |
206 | } | |
207 | (method, _) => method | |
208 | }; | |
209 | ||
210 | method | |
211 | } | |
212 | } |