Skip to main content

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::attrs::lang_items::{self, LangItem};
6use rustc_hir::def::DefKind;
7use rustc_hir::def_id::DefId;
8use rustc_middle::ty::{
9    self, AssocContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv, Unnormalized,
10};
11use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, sym};
12use tracing::debug;
13
14use crate::traits::specialization_graph;
15
16#[derive(#[automatically_derived]
impl ::core::clone::Clone for CallDesugaringKind {
    #[inline]
    fn clone(&self) -> CallDesugaringKind { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CallDesugaringKind { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for CallDesugaringKind {
    #[inline]
    fn eq(&self, other: &CallDesugaringKind) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for CallDesugaringKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::fmt::Debug for CallDesugaringKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                CallDesugaringKind::ForLoopIntoIter => "ForLoopIntoIter",
                CallDesugaringKind::ForLoopIntoAsyncIter =>
                    "ForLoopIntoAsyncIter",
                CallDesugaringKind::ForLoopNext => "ForLoopNext",
                CallDesugaringKind::QuestionBranch => "QuestionBranch",
                CallDesugaringKind::QuestionFromResidual =>
                    "QuestionFromResidual",
                CallDesugaringKind::TryBlockFromOutput =>
                    "TryBlockFromOutput",
                CallDesugaringKind::Await => "Await",
            })
    }
}Debug)]
17pub enum CallDesugaringKind {
18    /// for _ in x {} calls x.into_iter()
19    ForLoopIntoIter,
20    /// for await _ in x {} calls x.into_async_iter()
21    ForLoopIntoAsyncIter,
22    /// for _ in x {} calls iter.next()
23    ForLoopNext,
24    /// x? calls x.branch()
25    QuestionBranch,
26    /// x? calls type_of(x)::from_residual()
27    QuestionFromResidual,
28    /// try { ..; x } calls type_of(x)::from_output(x)
29    TryBlockFromOutput,
30    /// `.await` calls `IntoFuture::into_future`
31    Await,
32}
33
34impl CallDesugaringKind {
35    pub fn name(&self) -> &'static str {
36        match self {
37            CallDesugaringKind::ForLoopIntoIter => "`for` loop",
38            CallDesugaringKind::ForLoopIntoAsyncIter => "`for await` loop",
39            CallDesugaringKind::ForLoopNext => "`for` loop",
40            CallDesugaringKind::QuestionBranch => "question mark operator",
41            CallDesugaringKind::QuestionFromResidual => "question mark operator",
42            CallDesugaringKind::TryBlockFromOutput => "try block",
43            CallDesugaringKind::Await => "`await`",
44        }
45    }
46
47    pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
48        match self {
49            Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
50            Self::ForLoopIntoAsyncIter => tcx.get_diagnostic_item(sym::IntoAsyncIterator).unwrap(),
51            Self::ForLoopNext => tcx.require_lang_item(LangItem::Iterator, DUMMY_SP),
52            Self::QuestionBranch | Self::TryBlockFromOutput => {
53                tcx.require_lang_item(LangItem::Try, DUMMY_SP)
54            }
55            Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(),
56            Self::Await => tcx.get_diagnostic_item(sym::IntoFuture).unwrap(),
57        }
58    }
59}
60
61#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for CallKind<'tcx> {
    #[inline]
    fn clone(&self) -> CallKind<'tcx> {
        let _: ::core::clone::AssertParamIsClone<Option<Ident>>;
        let _:
                ::core::clone::AssertParamIsClone<Option<(CallDesugaringKind,
                Ty<'tcx>)>>;
        let _: ::core::clone::AssertParamIsClone<DefId>;
        let _: ::core::clone::AssertParamIsClone<GenericArgsRef<'tcx>>;
        let _: ::core::clone::AssertParamIsClone<Ty<'tcx>>;
        let _: ::core::clone::AssertParamIsClone<Option<Ident>>;
        let _: ::core::clone::AssertParamIsClone<Ty<'tcx>>;
        let _: ::core::clone::AssertParamIsClone<Option<Span>>;
        let _: ::core::clone::AssertParamIsClone<Ty<'tcx>>;
        let _: ::core::clone::AssertParamIsClone<Ty<'tcx>>;
        *self
    }
}Clone, #[automatically_derived]
impl<'tcx> ::core::marker::Copy for CallKind<'tcx> { }Copy, #[automatically_derived]
impl<'tcx> ::core::cmp::PartialEq for CallKind<'tcx> {
    #[inline]
    fn eq(&self, other: &CallKind<'tcx>) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (CallKind::Normal {
                    self_arg: __self_0,
                    desugaring: __self_1,
                    method_did: __self_2,
                    method_args: __self_3 }, CallKind::Normal {
                    self_arg: __arg1_0,
                    desugaring: __arg1_1,
                    method_did: __arg1_2,
                    method_args: __arg1_3 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1 &&
                            __self_2 == __arg1_2 && __self_3 == __arg1_3,
                (CallKind::FnCall { fn_trait_id: __self_0, self_ty: __self_1
                    }, CallKind::FnCall {
                    fn_trait_id: __arg1_0, self_ty: __arg1_1 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                (CallKind::Operator {
                    self_arg: __self_0, trait_id: __self_1, self_ty: __self_2 },
                    CallKind::Operator {
                    self_arg: __arg1_0, trait_id: __arg1_1, self_ty: __arg1_2 })
                    =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1 &&
                        __self_2 == __arg1_2,
                (CallKind::DerefCoercion {
                    deref_target_span: __self_0,
                    deref_target_ty: __self_1,
                    self_ty: __self_2 }, CallKind::DerefCoercion {
                    deref_target_span: __arg1_0,
                    deref_target_ty: __arg1_1,
                    self_ty: __arg1_2 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1 &&
                        __self_2 == __arg1_2,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq, #[automatically_derived]
impl<'tcx> ::core::cmp::Eq for CallKind<'tcx> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Option<Ident>>;
        let _:
                ::core::cmp::AssertParamIsEq<Option<(CallDesugaringKind,
                Ty<'tcx>)>>;
        let _: ::core::cmp::AssertParamIsEq<DefId>;
        let _: ::core::cmp::AssertParamIsEq<GenericArgsRef<'tcx>>;
        let _: ::core::cmp::AssertParamIsEq<Ty<'tcx>>;
        let _: ::core::cmp::AssertParamIsEq<Option<Ident>>;
        let _: ::core::cmp::AssertParamIsEq<Ty<'tcx>>;
        let _: ::core::cmp::AssertParamIsEq<Option<Span>>;
        let _: ::core::cmp::AssertParamIsEq<Ty<'tcx>>;
        let _: ::core::cmp::AssertParamIsEq<Ty<'tcx>>;
    }
}Eq, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for CallKind<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            CallKind::Normal {
                self_arg: __self_0,
                desugaring: __self_1,
                method_did: __self_2,
                method_args: __self_3 } =>
                ::core::fmt::Formatter::debug_struct_field4_finish(f,
                    "Normal", "self_arg", __self_0, "desugaring", __self_1,
                    "method_did", __self_2, "method_args", &__self_3),
            CallKind::FnCall { fn_trait_id: __self_0, self_ty: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "FnCall", "fn_trait_id", __self_0, "self_ty", &__self_1),
            CallKind::Operator {
                self_arg: __self_0, trait_id: __self_1, self_ty: __self_2 } =>
                ::core::fmt::Formatter::debug_struct_field3_finish(f,
                    "Operator", "self_arg", __self_0, "trait_id", __self_1,
                    "self_ty", &__self_2),
            CallKind::DerefCoercion {
                deref_target_span: __self_0,
                deref_target_ty: __self_1,
                self_ty: __self_2 } =>
                ::core::fmt::Formatter::debug_struct_field3_finish(f,
                    "DerefCoercion", "deref_target_span", __self_0,
                    "deref_target_ty", __self_1, "self_ty", &__self_2),
        }
    }
}Debug)]
62pub enum CallKind<'tcx> {
63    /// A normal method call of the form `receiver.foo(a, b, c)`
64    Normal {
65        self_arg: Option<Ident>,
66        desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>,
67        method_did: DefId,
68        method_args: GenericArgsRef<'tcx>,
69    },
70    /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)`
71    FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> },
72    /// A call to an operator trait, desugared from operator syntax (e.g. `a << b`)
73    Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> },
74    DerefCoercion {
75        /// The `Span` of the `Target` associated type
76        /// in the `Deref` impl we are using.
77        deref_target_span: Option<Span>,
78        /// The type `T::Deref` we are dereferencing to
79        deref_target_ty: Ty<'tcx>,
80        self_ty: Ty<'tcx>,
81    },
82}
83
84pub fn call_kind<'tcx>(
85    tcx: TyCtxt<'tcx>,
86    typing_env: TypingEnv<'tcx>,
87    method_did: DefId,
88    method_args: GenericArgsRef<'tcx>,
89    fn_call_span: Span,
90    from_hir_call: bool,
91    self_arg: Option<Ident>,
92) -> CallKind<'tcx> {
93    let parent = tcx.opt_associated_item(method_did).and_then(|assoc| {
94        let container_id = assoc.container_id(tcx);
95        match assoc.container {
96            AssocContainer::InherentImpl => None,
97            AssocContainer::TraitImpl(_) => Some(tcx.impl_trait_id(container_id)),
98            AssocContainer::Trait => Some(container_id),
99        }
100    });
101
102    let fn_call = parent.filter(|&p| tcx.fn_trait_kind_from_def_id(p).is_some());
103
104    let operator = if !from_hir_call && let Some(p) = parent {
105        lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
106    } else {
107        None
108    };
109
110    // Check for a 'special' use of 'self' -
111    // an FnOnce call, an operator (e.g. `<<`), or a
112    // deref coercion.
113    if let Some(trait_id) = fn_call {
114        return CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_args.type_at(0) };
115    } else if let Some(trait_id) = operator {
116        return CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) };
117    } else if !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did) {
118        let deref_target_def_id =
119            tcx.get_diagnostic_item(sym::deref_target).expect("deref method but no deref target");
120        let deref_target_ty = tcx.normalize_erasing_regions(
121            typing_env,
122            Unnormalized::new(Ty::new_projection(
123                tcx,
124                ty::IsRigid::No,
125                deref_target_def_id,
126                method_args,
127            )),
128        );
129        let deref_target_span = if let Ok(Some(instance)) =
130            Instance::try_resolve(tcx, typing_env, method_did, method_args)
131            && let instance_parent_def_id = tcx.parent(instance.def_id())
132            && #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(instance_parent_def_id)
    {
    DefKind::Impl { .. } => true,
    _ => false,
}matches!(tcx.def_kind(instance_parent_def_id), DefKind::Impl { .. })
133            && let Ok(instance) =
134                specialization_graph::assoc_def(tcx, instance_parent_def_id, deref_target_def_id)
135            && instance.is_final()
136        {
137            Some(tcx.def_span(instance.item.def_id))
138        } else {
139            None
140        };
141        return CallKind::DerefCoercion {
142            deref_target_ty,
143            deref_target_span,
144            self_ty: method_args.type_at(0),
145        };
146    }
147
148    // This isn't a 'special' use of `self`
149    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs:149",
                        "rustc_trait_selection::error_reporting::traits::call_kind",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs"),
                        ::tracing_core::__macro_support::Option::Some(149u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_trait_selection::error_reporting::traits::call_kind"),
                        ::tracing_core::field::FieldSet::new(&[{
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("method_did")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("method_did");
                                            NAME.as_str()
                                        },
                                        {
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("fn_call_span")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("fn_call_span");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&method_did)
                                            as &dyn ::tracing::field::Value)),
                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&fn_call_span)
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(?method_did, ?fn_call_span);
150    let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter)
151        && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
152    {
153        Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0)))
154    } else if tcx.is_lang_item(method_did, LangItem::IntoAsyncIterIntoIter)
155        && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
156    {
157        Some((CallDesugaringKind::ForLoopIntoAsyncIter, method_args.type_at(0)))
158    } else if tcx.is_lang_item(method_did, LangItem::IteratorNext)
159        && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
160    {
161        Some((CallDesugaringKind::ForLoopNext, method_args.type_at(0)))
162    } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
163        if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) {
164            Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0)))
165        } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) {
166            Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0)))
167        } else {
168            None
169        }
170    } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput)
171        && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
172    {
173        Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0)))
174    } else if fn_call_span.is_desugaring(DesugaringKind::Await) {
175        Some((CallDesugaringKind::Await, method_args.type_at(0)))
176    } else {
177        None
178    };
179    CallKind::Normal { self_arg, desugaring, method_did, method_args }
180}