]> git.proxmox.com Git - rustc.git/blame - src/librustc_typeck/check/autoderef.rs
New upstream version 1.12.0+dfsg1
[rustc.git] / src / librustc_typeck / check / autoderef.rs
CommitLineData
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
11use astconv::AstConv;
12
13use super::FnCtxt;
14
15use rustc::traits;
16use rustc::ty::{self, Ty, TraitRef};
17use rustc::ty::{ToPredicate, TypeFoldable};
18use rustc::ty::{MethodCall, MethodCallee};
19use rustc::ty::subst::Substs;
20use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
21use rustc::hir;
22
23use syntax_pos::Span;
24use syntax::parse::token;
25
26#[derive(Copy, Clone, Debug)]
27enum AutoderefKind {
28 Builtin,
29 Overloaded
30}
31
32pub 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
41impl<'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
92impl<'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 },
104 substs: tcx.mk_substs(Substs::new_trait(vec![], vec![], self.cur_ty))
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
165impl<'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}