]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_const_eval/src/util/call_kind.rs
Auto merge of #103455 - BlackHoleFox:apple-sim-abi-consistency, r=davidtwco
[rust.git] / compiler / rustc_const_eval / src / util / 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
5 use rustc_hir::def_id::DefId;
6 use rustc_hir::lang_items;
7 use rustc_middle::ty::subst::SubstsRef;
8 use rustc_middle::ty::{self, AssocItemContainer, DefIdTree, Instance, ParamEnv, Ty, TyCtxt};
9 use rustc_span::symbol::Ident;
10 use rustc_span::{sym, DesugaringKind, Span};
11
12 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
13 pub 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
24 impl 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 => {
29                 tcx.lang_items().try_trait().unwrap()
30             }
31             Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(),
32         }
33     }
34 }
35
36 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
37 pub 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> },
48     /// A call to an operator trait, desugared from operator syntax (e.g. `a << b`)
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
60 pub 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> {
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         }
75     });
76
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     });
80
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     };
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.
92     let kind = if let Some(trait_id) = fn_call {
93         Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_substs.type_at(0) })
94     } else if let Some(trait_id) = operator {
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         };
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)
139             .and_then(|did| match tcx.type_of(did).kind() {
140                 ty::Adt(def, ..) => Some(def.did()),
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 }