rustc_hir_typeck/
autoderef.rs

1//! Some helper functions for `AutoDeref`.
2
3use std::iter;
4
5use itertools::Itertools;
6use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind};
7use rustc_infer::infer::InferOk;
8use rustc_infer::traits::PredicateObligations;
9use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
10use rustc_middle::ty::{self, Ty};
11use rustc_span::Span;
12
13use super::method::MethodCallee;
14use super::{FnCtxt, PlaceOp};
15
16impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17    pub(crate) fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> {
18        Autoderef::new(self, self.param_env, self.body_id, span, base_ty)
19    }
20
21    pub(crate) fn try_overloaded_deref(
22        &self,
23        span: Span,
24        base_ty: Ty<'tcx>,
25    ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
26        self.try_overloaded_place_op(span, base_ty, None, PlaceOp::Deref)
27    }
28
29    /// Returns the adjustment steps.
30    pub(crate) fn adjust_steps(&self, autoderef: &Autoderef<'a, 'tcx>) -> Vec<Adjustment<'tcx>> {
31        self.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(autoderef))
32    }
33
34    pub(crate) fn adjust_steps_as_infer_ok(
35        &self,
36        autoderef: &Autoderef<'a, 'tcx>,
37    ) -> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
38        let steps = autoderef.steps();
39        if steps.is_empty() {
40            return InferOk { obligations: PredicateObligations::new(), value: vec![] };
41        }
42
43        let mut obligations = PredicateObligations::new();
44        let targets =
45            steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false)));
46        let steps: Vec<_> = steps
47            .iter()
48            .map(|&(source, kind)| {
49                if let AutoderefKind::Overloaded = kind {
50                    self.try_overloaded_deref(autoderef.span(), source).and_then(
51                        |InferOk { value: method, obligations: o }| {
52                            obligations.extend(o);
53                            // FIXME: we should assert the sig is &T here... there's no reason for this to be fallible.
54                            if let ty::Ref(_, _, mutbl) = *method.sig.output().kind() {
55                                Some(OverloadedDeref { mutbl, span: autoderef.span() })
56                            } else {
57                                None
58                            }
59                        },
60                    )
61                } else {
62                    None
63                }
64            })
65            .zip_eq(targets)
66            .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target })
67            .collect();
68
69        InferOk { obligations, value: steps }
70    }
71}