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.
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.
15 use rustc::infer::InferOk;
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};
24 use syntax::symbol::Symbol;
26 #[derive(Copy, Clone, Debug)]
32 pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
33 fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
34 steps: Vec<(Ty<'tcx>, AutoderefKind)>,
36 obligations: Vec<traits::PredicateObligation<'tcx>>,
41 impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
42 type Item = (Ty<'tcx>, usize);
44 fn next(&mut self) -> Option<Self::Item> {
45 let tcx = self.fcx.tcx;
47 debug!("autoderef: steps={:?}, cur_ty={:?}",
51 self.at_start = false;
52 debug!("autoderef stage #0 is {:?}", self.cur_ty);
53 return Some((self.cur_ty, 0));
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,
62 "reached the recursion limit while auto-dereferencing {:?}",
64 .span_label(self.span, &format!("deref recursion limit reached"))
66 "consider adding a `#[recursion_limit=\"{}\"]` attribute to your crate",
72 if self.cur_ty.is_ty_var() {
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)
80 match self.overloaded_deref_ty(self.cur_ty) {
81 Some(ty) => (AutoderefKind::Overloaded, ty),
86 if new_ty.references_error() {
90 self.steps.push((self.cur_ty, kind));
91 debug!("autoderef stage #{:?} is {:?} from {:?}",
97 Some((self.cur_ty, self.steps.len()))
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);
105 let tcx = self.fcx.tcx();
108 let trait_ref = TraitRef {
109 def_id: match tcx.lang_items.deref_trait() {
113 substs: tcx.mk_substs_trait(self.cur_ty, &[]),
116 let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
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");
125 let normalized = traits::normalize_projection_type(&mut selcx,
127 trait_ref: trait_ref,
128 item_name: Symbol::intern("Target"),
133 debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
134 self.obligations.extend(normalized.obligations);
136 Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
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)
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)
151 pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
152 where I: IntoIterator<Item = &'b hir::Expr>
155 fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, exprs));
158 pub fn finalize_as_infer_ok<'b, I>(self, pref: LvaluePreference, exprs: I)
160 where I: IntoIterator<Item = &'b hir::Expr>
162 let methods: Vec<_> = self.steps
165 if let AutoderefKind::Overloaded = kind {
166 self.fcx.try_overloaded_deref(self.span, None, ty, pref)
173 debug!("finalize({:?}) - {:?},{:?}",
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);
190 obligations: self.obligations
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> {
200 cur_ty: self.resolve_type_vars_if_possible(&base_ty),
207 pub fn try_overloaded_deref(&self,
209 base_expr: Option<&hir::Expr>,
211 lvalue_pref: LvaluePreference)
212 -> Option<MethodCallee<'tcx>> {
213 debug!("try_overloaded_deref({:?},{:?},{:?},{:?})",
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,
223 Symbol::intern("deref_mut"),
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,
236 Symbol::intern("deref"),
241 (method, _) => method,