]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_const_eval/src/util/call_kind.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / compiler / rustc_const_eval / src / util / call_kind.rs
CommitLineData
5099ac24
FG
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_id::DefId;
487cf647 6use rustc_hir::{lang_items, LangItem};
5099ac24
FG
7use rustc_middle::ty::subst::SubstsRef;
8use rustc_middle::ty::{self, AssocItemContainer, DefIdTree, Instance, ParamEnv, Ty, TyCtxt};
9use rustc_span::symbol::Ident;
10use rustc_span::{sym, DesugaringKind, Span};
11
12#[derive(Clone, Copy, PartialEq, Eq, Debug)]
13pub enum CallDesugaringKind {
14 /// for _ in x {} calls x.into_iter()
15 ForLoopIntoIter,
16 /// x? calls x.branch()
17 QuestionBranch,
18 /// x? calls type_of(x)::from_residual()
19 QuestionFromResidual,
20 /// try { ..; x } calls type_of(x)::from_output(x)
21 TryBlockFromOutput,
22}
23
24impl CallDesugaringKind {
25 pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
26 match self {
27 Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
28 Self::QuestionBranch | Self::TryBlockFromOutput => {
487cf647 29 tcx.require_lang_item(LangItem::Try, None)
5099ac24
FG
30 }
31 Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(),
32 }
33 }
34}
35
36#[derive(Clone, Copy, PartialEq, Eq, Debug)]
37pub enum CallKind<'tcx> {
38 /// A normal method call of the form `receiver.foo(a, b, c)`
39 Normal {
40 self_arg: Option<Ident>,
41 desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>,
42 /// Whether the self type of the method call has an `.as_ref()` method.
43 /// Used for better diagnostics.
44 is_option_or_result: bool,
45 },
46 /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)`
47 FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> },
5e7ed085 48 /// A call to an operator trait, desugared from operator syntax (e.g. `a << b`)
5099ac24
FG
49 Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> },
50 DerefCoercion {
51 /// The `Span` of the `Target` associated type
52 /// in the `Deref` impl we are using.
53 deref_target: Span,
54 /// The type `T::Deref` we are dereferencing to
55 deref_target_ty: Ty<'tcx>,
56 self_ty: Ty<'tcx>,
57 },
58}
59
60pub fn call_kind<'tcx>(
61 tcx: TyCtxt<'tcx>,
62 param_env: ParamEnv<'tcx>,
63 method_did: DefId,
64 method_substs: SubstsRef<'tcx>,
65 fn_call_span: Span,
66 from_hir_call: bool,
67 self_arg: Option<Ident>,
68) -> CallKind<'tcx> {
064997fb
FG
69 let parent = tcx.opt_associated_item(method_did).and_then(|assoc| {
70 let container_id = assoc.container_id(tcx);
71 match assoc.container {
72 AssocItemContainer::ImplContainer => tcx.trait_id_of_impl(container_id),
73 AssocItemContainer::TraitContainer => Some(container_id),
74 }
5099ac24
FG
75 });
76
487cf647
FG
77 let fn_call = parent.and_then(|p| {
78 lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
79 });
5099ac24 80
487cf647
FG
81 let operator = if !from_hir_call && let Some(p) = parent {
82 lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
83 } else {
84 None
85 };
5099ac24
FG
86
87 let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
88
89 // Check for a 'special' use of 'self' -
90 // an FnOnce call, an operator (e.g. `<<`), or a
91 // deref coercion.
487cf647 92 let kind = if let Some(trait_id) = fn_call {
5099ac24 93 Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_substs.type_at(0) })
487cf647 94 } else if let Some(trait_id) = operator {
5099ac24
FG
95 Some(CallKind::Operator { self_arg, trait_id, self_ty: method_substs.type_at(0) })
96 } else if is_deref {
97 let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
98 Instance::resolve(tcx, param_env, deref_target, method_substs).transpose()
99 });
100 if let Some(Ok(instance)) = deref_target {
101 let deref_target_ty = instance.ty(tcx, param_env);
102 Some(CallKind::DerefCoercion {
103 deref_target: tcx.def_span(instance.def_id()),
104 deref_target_ty,
105 self_ty: method_substs.type_at(0),
106 })
107 } else {
108 None
109 }
110 } else {
111 None
112 };
113
114 kind.unwrap_or_else(|| {
115 // This isn't a 'special' use of `self`
116 debug!(?method_did, ?fn_call_span);
117 let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn()
118 && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
119 {
120 Some((CallDesugaringKind::ForLoopIntoIter, method_substs.type_at(0)))
121 } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
122 if Some(method_did) == tcx.lang_items().branch_fn() {
123 Some((CallDesugaringKind::QuestionBranch, method_substs.type_at(0)))
124 } else if Some(method_did) == tcx.lang_items().from_residual_fn() {
125 Some((CallDesugaringKind::QuestionFromResidual, method_substs.type_at(0)))
126 } else {
127 None
128 }
129 } else if Some(method_did) == tcx.lang_items().from_output_fn()
130 && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
131 {
132 Some((CallDesugaringKind::TryBlockFromOutput, method_substs.type_at(0)))
133 } else {
134 None
135 };
04454e1e
FG
136 let parent_did = tcx.parent(method_did);
137 let parent_self_ty = (tcx.def_kind(parent_did) == rustc_hir::def::DefKind::Impl)
138 .then_some(parent_did)
5099ac24 139 .and_then(|did| match tcx.type_of(did).kind() {
5e7ed085 140 ty::Adt(def, ..) => Some(def.did()),
5099ac24
FG
141 _ => None,
142 });
143 let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
144 matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
145 });
146 CallKind::Normal { self_arg, desugaring, is_option_or_result }
147 })
148}