| //! Some helper functions for `AutoDeref` |
| use super::method::MethodCallee; |
| use super::{FnCtxt, PlaceOp}; |
| |
| use rustc_infer::infer::InferOk; |
| use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; |
| use rustc_middle::ty::{self, Ty}; |
| use rustc_span::Span; |
| use rustc_trait_selection::autoderef::{Autoderef, AutoderefKind}; |
| |
| use std::iter; |
| |
| impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
| pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> { |
| Autoderef::new(self, self.param_env, self.body_id, span, base_ty) |
| } |
| |
| pub fn try_overloaded_deref( |
| &self, |
| span: Span, |
| base_ty: Ty<'tcx>, |
| ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { |
| self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref) |
| } |
| |
| /// Returns the adjustment steps. |
| pub fn adjust_steps(&self, autoderef: &Autoderef<'a, 'tcx>) -> Vec<Adjustment<'tcx>> { |
| self.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(autoderef)) |
| } |
| |
| pub fn adjust_steps_as_infer_ok( |
| &self, |
| autoderef: &Autoderef<'a, 'tcx>, |
| ) -> InferOk<'tcx, Vec<Adjustment<'tcx>>> { |
| let mut obligations = vec![]; |
| let steps = autoderef.steps(); |
| let targets = |
| steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false))); |
| let steps: Vec<_> = steps |
| .iter() |
| .map(|&(source, kind)| { |
| if let AutoderefKind::Overloaded = kind { |
| self.try_overloaded_deref(autoderef.span(), source).and_then( |
| |InferOk { value: method, obligations: o }| { |
| obligations.extend(o); |
| if let ty::Ref(region, _, mutbl) = method.sig.output().kind { |
| Some(OverloadedDeref { region, mutbl }) |
| } else { |
| None |
| } |
| }, |
| ) |
| } else { |
| None |
| } |
| }) |
| .zip(targets) |
| .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) |
| .collect(); |
| |
| InferOk { obligations, value: steps } |
| } |
| } |