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.
13 use super::{FnCtxt, PlaceOp, Needs};
14 use super::method::MethodCallee;
16 use rustc::infer::InferOk;
17 use rustc::session::DiagnosticMessageId;
19 use rustc::ty::{self, Ty, TraitRef};
20 use rustc::ty::{ToPredicate, TypeFoldable};
21 use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
24 use syntax::symbol::Symbol;
28 #[derive(Copy, Clone, Debug)]
34 pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
35 fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
36 steps: Vec<(Ty<'tcx>, AutoderefKind)>,
38 obligations: Vec<traits::PredicateObligation<'tcx>>,
40 include_raw_pointers: bool,
44 impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
45 type Item = (Ty<'tcx>, usize);
47 fn next(&mut self) -> Option<Self::Item> {
48 let tcx = self.fcx.tcx;
50 debug!("autoderef: steps={:?}, cur_ty={:?}",
54 self.at_start = false;
55 debug!("autoderef stage #0 is {:?}", self.cur_ty);
56 return Some((self.cur_ty, 0));
59 if self.steps.len() >= *tcx.sess.recursion_limit.get() {
60 // We've reached the recursion limit, error gracefully.
61 let suggested_limit = *tcx.sess.recursion_limit.get() * 2;
62 let msg = format!("reached the recursion limit while auto-dereferencing {:?}",
64 let error_id = (DiagnosticMessageId::ErrorId(55), Some(self.span), msg.clone());
65 let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
67 struct_span_err!(tcx.sess,
70 "reached the recursion limit while auto-dereferencing {:?}",
72 .span_label(self.span, "deref recursion limit reached")
74 "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
81 if self.cur_ty.is_ty_var() {
85 // Otherwise, deref if type is derefable:
87 if let Some(mt) = self.cur_ty.builtin_deref(self.include_raw_pointers) {
88 (AutoderefKind::Builtin, mt.ty)
90 let ty = self.overloaded_deref_ty(self.cur_ty)?;
91 (AutoderefKind::Overloaded, ty)
94 if new_ty.references_error() {
98 self.steps.push((self.cur_ty, kind));
99 debug!("autoderef stage #{:?} is {:?} from {:?}",
102 (self.cur_ty, kind));
103 self.cur_ty = new_ty;
105 Some((self.cur_ty, self.steps.len()))
109 impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
110 fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
111 debug!("overloaded_deref_ty({:?})", ty);
113 let tcx = self.fcx.tcx();
116 let trait_ref = TraitRef {
117 def_id: tcx.lang_items().deref_trait()?,
118 substs: tcx.mk_substs_trait(self.cur_ty, &[]),
121 let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
123 let mut selcx = traits::SelectionContext::new(self.fcx);
124 let obligation = traits::Obligation::new(cause.clone(),
126 trait_ref.to_predicate());
127 if !selcx.evaluate_obligation(&obligation) {
128 debug!("overloaded_deref_ty: cannot match obligation");
132 let normalized = traits::normalize_projection_type(&mut selcx,
134 ty::ProjectionTy::from_ref_and_name(
137 Symbol::intern("Target"),
142 debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
143 self.obligations.extend(normalized.obligations);
145 Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
148 /// Returns the final type, generating an error if it is an
149 /// unresolved inference variable.
150 pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
151 self.fcx.structurally_resolved_type(self.span, self.cur_ty)
154 /// Returns the final type we ended up with, which may well be an
155 /// inference variable (we will resolve it first, if possible).
156 pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> {
157 self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
160 pub fn step_count(&self) -> usize {
164 /// Returns the adjustment steps.
165 pub fn adjust_steps(&self, needs: Needs)
166 -> Vec<Adjustment<'tcx>> {
167 self.fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(needs))
170 pub fn adjust_steps_as_infer_ok(&self, needs: Needs)
171 -> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
172 let mut obligations = vec![];
173 let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty)
174 .chain(iter::once(self.cur_ty));
175 let steps: Vec<_> = self.steps.iter().map(|&(source, kind)| {
176 if let AutoderefKind::Overloaded = kind {
177 self.fcx.try_overloaded_deref(self.span, source, needs)
178 .and_then(|InferOk { value: method, obligations: o }| {
179 obligations.extend(o);
180 if let ty::TyRef(region, mt) = method.sig.output().sty {
181 Some(OverloadedDeref {
192 }).zip(targets).map(|(autoderef, target)| {
194 kind: Adjust::Deref(autoderef),
205 /// also dereference through raw pointer types
206 /// e.g. assuming ptr_to_Foo is the type `*const Foo`
207 /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo]
208 /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo]
209 pub fn include_raw_pointers(mut self) -> Self {
210 self.include_raw_pointers = true;
214 pub fn finalize(self) {
216 fcx.register_predicates(self.into_obligations());
219 pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> {
224 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
225 pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'gcx, 'tcx> {
229 cur_ty: self.resolve_type_vars_if_possible(&base_ty),
232 include_raw_pointers: false,
237 pub fn try_overloaded_deref(&self,
241 -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
242 self.try_overloaded_place_op(span, base_ty, &[], needs, PlaceOp::Deref)