rustc_trait_selection/error_reporting/traits/
call_kind.rs

1//! Common logic for borrowck use-after-move errors when moved into a `fn(self)`,
2//! as well as errors when attempting to call a non-const function in a const
3//! context.
4
5use rustc_hir::def::DefKind;
6use rustc_hir::def_id::DefId;
7use rustc_hir::{LangItem, lang_items};
8use rustc_middle::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv};
9use rustc_span::{DesugaringKind, Ident, Span, sym};
10use tracing::debug;
11
12use crate::traits::specialization_graph;
13
14#[derive(Clone, Copy, PartialEq, Eq, Debug)]
15pub enum CallDesugaringKind {
16    /// for _ in x {} calls x.into_iter()
17    ForLoopIntoIter,
18    /// for _ in x {} calls iter.next()
19    ForLoopNext,
20    /// x? calls x.branch()
21    QuestionBranch,
22    /// x? calls type_of(x)::from_residual()
23    QuestionFromResidual,
24    /// try { ..; x } calls type_of(x)::from_output(x)
25    TryBlockFromOutput,
26    /// `.await` calls `IntoFuture::into_future`
27    Await,
28}
29
30impl CallDesugaringKind {
31    pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
32        match self {
33            Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
34            Self::ForLoopNext => tcx.require_lang_item(LangItem::Iterator, None),
35            Self::QuestionBranch | Self::TryBlockFromOutput => {
36                tcx.require_lang_item(LangItem::Try, None)
37            }
38            Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(),
39            Self::Await => tcx.get_diagnostic_item(sym::IntoFuture).unwrap(),
40        }
41    }
42}
43
44#[derive(Clone, Copy, PartialEq, Eq, Debug)]
45pub enum CallKind<'tcx> {
46    /// A normal method call of the form `receiver.foo(a, b, c)`
47    Normal {
48        self_arg: Option<Ident>,
49        desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>,
50        method_did: DefId,
51        method_args: GenericArgsRef<'tcx>,
52    },
53    /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)`
54    FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> },
55    /// A call to an operator trait, desugared from operator syntax (e.g. `a << b`)
56    Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> },
57    DerefCoercion {
58        /// The `Span` of the `Target` associated type
59        /// in the `Deref` impl we are using.
60        deref_target_span: Option<Span>,
61        /// The type `T::Deref` we are dereferencing to
62        deref_target_ty: Ty<'tcx>,
63        self_ty: Ty<'tcx>,
64    },
65}
66
67pub fn call_kind<'tcx>(
68    tcx: TyCtxt<'tcx>,
69    typing_env: TypingEnv<'tcx>,
70    method_did: DefId,
71    method_args: GenericArgsRef<'tcx>,
72    fn_call_span: Span,
73    from_hir_call: bool,
74    self_arg: Option<Ident>,
75) -> CallKind<'tcx> {
76    let parent = tcx.opt_associated_item(method_did).and_then(|assoc| {
77        let container_id = assoc.container_id(tcx);
78        match assoc.container {
79            AssocItemContainer::Impl => tcx.trait_id_of_impl(container_id),
80            AssocItemContainer::Trait => Some(container_id),
81        }
82    });
83
84    let fn_call = parent.and_then(|p| {
85        lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
86    });
87
88    let operator = if !from_hir_call && let Some(p) = parent {
89        lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
90    } else {
91        None
92    };
93
94    // Check for a 'special' use of 'self' -
95    // an FnOnce call, an operator (e.g. `<<`), or a
96    // deref coercion.
97    if let Some(trait_id) = fn_call {
98        return CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_args.type_at(0) };
99    } else if let Some(trait_id) = operator {
100        return CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) };
101    } else if !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did) {
102        let deref_target_def_id =
103            tcx.get_diagnostic_item(sym::deref_target).expect("deref method but no deref target");
104        let deref_target_ty = tcx.normalize_erasing_regions(
105            typing_env,
106            Ty::new_projection(tcx, deref_target_def_id, method_args),
107        );
108        let deref_target_span = if let Ok(Some(instance)) =
109            Instance::try_resolve(tcx, typing_env, method_did, method_args)
110            && let instance_parent_def_id = tcx.parent(instance.def_id())
111            && matches!(tcx.def_kind(instance_parent_def_id), DefKind::Impl { .. })
112            && let Ok(instance) =
113                specialization_graph::assoc_def(tcx, instance_parent_def_id, deref_target_def_id)
114            && instance.is_final()
115        {
116            Some(tcx.def_span(instance.item.def_id))
117        } else {
118            None
119        };
120        return CallKind::DerefCoercion {
121            deref_target_ty,
122            deref_target_span,
123            self_ty: method_args.type_at(0),
124        };
125    }
126
127    // This isn't a 'special' use of `self`
128    debug!(?method_did, ?fn_call_span);
129    let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter)
130        && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
131    {
132        Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0)))
133    } else if tcx.is_lang_item(method_did, LangItem::IteratorNext)
134        && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
135    {
136        Some((CallDesugaringKind::ForLoopNext, method_args.type_at(0)))
137    } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
138        if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) {
139            Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0)))
140        } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) {
141            Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0)))
142        } else {
143            None
144        }
145    } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput)
146        && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
147    {
148        Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0)))
149    } else if fn_call_span.is_desugaring(DesugaringKind::Await) {
150        Some((CallDesugaringKind::Await, method_args.type_at(0)))
151    } else {
152        None
153    };
154    CallKind::Normal { self_arg, desugaring, method_did, method_args }
155}