about summary refs log tree commit diff
path: root/compiler/rustc_middle
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-04-16 13:12:53 +0000
committerbors <bors@rust-lang.org>2023-04-16 13:12:53 +0000
commit8a778ca1e35e4a8df95c00d800100d95e63e7722 (patch)
treef74a5282fe636aa9e51d238b2cb46a9092a5c38b /compiler/rustc_middle
parent1b50ea9abb65b33aac7285dbe36b37f9e33381a2 (diff)
parent38215fb77aca2dcd25277ff7b3093ea56e4e8ffb (diff)
downloadrust-8a778ca1e35e4a8df95c00d800100d95e63e7722.tar.gz
rust-8a778ca1e35e4a8df95c00d800100d95e63e7722.zip
Auto merge of #110405 - fee1-dead-contrib:rollup-9rkree6, r=fee1-dead
Rollup of 4 pull requests

Successful merges:

 - #110397 (Move some utils out of `rustc_const_eval`)
 - #110398 (use matches! macro in more places)
 - #110400 (more clippy fixes: clippy::{iter_cloned_collect, unwarp_or_else_defau…)
 - #110402 (Remove the loop in `Align::from_bytes`)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler/rustc_middle')
-rw-r--r--compiler/rustc_middle/src/lib.rs6
-rw-r--r--compiler/rustc_middle/src/util/call_kind.rs137
-rw-r--r--compiler/rustc_middle/src/util/find_self_call.rs36
-rw-r--r--compiler/rustc_middle/src/util/mod.rs7
4 files changed, 181 insertions, 5 deletions
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index b4edb02f6c4..1ddc08eac7d 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -99,13 +99,9 @@ pub mod mir;
 pub mod thir;
 pub mod traits;
 pub mod ty;
+pub mod util;
 mod values;
 
-pub mod util {
-    pub mod bug;
-    pub mod common;
-}
-
 // Allows macros to refer to this crate as `::rustc_middle`
 extern crate self as rustc_middle;
 
diff --git a/compiler/rustc_middle/src/util/call_kind.rs b/compiler/rustc_middle/src/util/call_kind.rs
new file mode 100644
index 00000000000..627c84c388c
--- /dev/null
+++ b/compiler/rustc_middle/src/util/call_kind.rs
@@ -0,0 +1,137 @@
+//! Common logic for borrowck use-after-move errors when moved into a `fn(self)`,
+//! as well as errors when attempting to call a non-const function in a const
+//! context.
+
+use crate::ty::subst::SubstsRef;
+use crate::ty::{AssocItemContainer, Instance, ParamEnv, Ty, TyCtxt};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{lang_items, LangItem};
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, DesugaringKind, Span};
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum CallDesugaringKind {
+    /// for _ in x {} calls x.into_iter()
+    ForLoopIntoIter,
+    /// x? calls x.branch()
+    QuestionBranch,
+    /// x? calls type_of(x)::from_residual()
+    QuestionFromResidual,
+    /// try { ..; x } calls type_of(x)::from_output(x)
+    TryBlockFromOutput,
+}
+
+impl CallDesugaringKind {
+    pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
+        match self {
+            Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
+            Self::QuestionBranch | Self::TryBlockFromOutput => {
+                tcx.require_lang_item(LangItem::Try, None)
+            }
+            Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(),
+        }
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum CallKind<'tcx> {
+    /// A normal method call of the form `receiver.foo(a, b, c)`
+    Normal {
+        self_arg: Option<Ident>,
+        desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>,
+        method_did: DefId,
+        method_substs: SubstsRef<'tcx>,
+    },
+    /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)`
+    FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> },
+    /// A call to an operator trait, desugared from operator syntax (e.g. `a << b`)
+    Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> },
+    DerefCoercion {
+        /// The `Span` of the `Target` associated type
+        /// in the `Deref` impl we are using.
+        deref_target: Span,
+        /// The type `T::Deref` we are dereferencing to
+        deref_target_ty: Ty<'tcx>,
+        self_ty: Ty<'tcx>,
+    },
+}
+
+pub fn call_kind<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ParamEnv<'tcx>,
+    method_did: DefId,
+    method_substs: SubstsRef<'tcx>,
+    fn_call_span: Span,
+    from_hir_call: bool,
+    self_arg: Option<Ident>,
+) -> CallKind<'tcx> {
+    let parent = tcx.opt_associated_item(method_did).and_then(|assoc| {
+        let container_id = assoc.container_id(tcx);
+        match assoc.container {
+            AssocItemContainer::ImplContainer => tcx.trait_id_of_impl(container_id),
+            AssocItemContainer::TraitContainer => Some(container_id),
+        }
+    });
+
+    let fn_call = parent.and_then(|p| {
+        lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
+    });
+
+    let operator = if !from_hir_call && let Some(p) = parent {
+        lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
+    } else {
+        None
+    };
+
+    let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
+
+    // Check for a 'special' use of 'self' -
+    // an FnOnce call, an operator (e.g. `<<`), or a
+    // deref coercion.
+    let kind = if let Some(trait_id) = fn_call {
+        Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_substs.type_at(0) })
+    } else if let Some(trait_id) = operator {
+        Some(CallKind::Operator { self_arg, trait_id, self_ty: method_substs.type_at(0) })
+    } else if is_deref {
+        let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
+            Instance::resolve(tcx, param_env, deref_target, method_substs).transpose()
+        });
+        if let Some(Ok(instance)) = deref_target {
+            let deref_target_ty = instance.ty(tcx, param_env);
+            Some(CallKind::DerefCoercion {
+                deref_target: tcx.def_span(instance.def_id()),
+                deref_target_ty,
+                self_ty: method_substs.type_at(0),
+            })
+        } else {
+            None
+        }
+    } else {
+        None
+    };
+
+    kind.unwrap_or_else(|| {
+        // This isn't a 'special' use of `self`
+        debug!(?method_did, ?fn_call_span);
+        let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn()
+            && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
+        {
+            Some((CallDesugaringKind::ForLoopIntoIter, method_substs.type_at(0)))
+        } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
+            if Some(method_did) == tcx.lang_items().branch_fn() {
+                Some((CallDesugaringKind::QuestionBranch, method_substs.type_at(0)))
+            } else if Some(method_did) == tcx.lang_items().from_residual_fn() {
+                Some((CallDesugaringKind::QuestionFromResidual, method_substs.type_at(0)))
+            } else {
+                None
+            }
+        } else if Some(method_did) == tcx.lang_items().from_output_fn()
+            && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
+        {
+            Some((CallDesugaringKind::TryBlockFromOutput, method_substs.type_at(0)))
+        } else {
+            None
+        };
+        CallKind::Normal { self_arg, desugaring, method_did, method_substs }
+    })
+}
diff --git a/compiler/rustc_middle/src/util/find_self_call.rs b/compiler/rustc_middle/src/util/find_self_call.rs
new file mode 100644
index 00000000000..0eab0adf07e
--- /dev/null
+++ b/compiler/rustc_middle/src/util/find_self_call.rs
@@ -0,0 +1,36 @@
+use crate::mir::*;
+use crate::ty::subst::SubstsRef;
+use crate::ty::{self, TyCtxt};
+use rustc_span::def_id::DefId;
+
+/// Checks if the specified `local` is used as the `self` parameter of a method call
+/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is
+/// returned.
+pub fn find_self_call<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'tcx>,
+    local: Local,
+    block: BasicBlock,
+) -> Option<(DefId, SubstsRef<'tcx>)> {
+    debug!("find_self_call(local={:?}): terminator={:?}", local, &body[block].terminator);
+    if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) =
+        &body[block].terminator
+    {
+        debug!("find_self_call: func={:?}", func);
+        if let Operand::Constant(box Constant { literal, .. }) = func {
+            if let ty::FnDef(def_id, substs) = *literal.ty().kind() {
+                if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
+                    tcx.opt_associated_item(def_id)
+                {
+                    debug!("find_self_call: args={:?}", args);
+                    if let [Operand::Move(self_place) | Operand::Copy(self_place), ..] = **args {
+                        if self_place.as_local() == Some(local) {
+                            return Some((def_id, substs));
+                        }
+                    }
+                }
+            }
+        }
+    }
+    None
+}
diff --git a/compiler/rustc_middle/src/util/mod.rs b/compiler/rustc_middle/src/util/mod.rs
new file mode 100644
index 00000000000..53b4257899b
--- /dev/null
+++ b/compiler/rustc_middle/src/util/mod.rs
@@ -0,0 +1,7 @@
+pub mod bug;
+pub mod call_kind;
+pub mod common;
+pub mod find_self_call;
+
+pub use call_kind::{call_kind, CallDesugaringKind, CallKind};
+pub use find_self_call::find_self_call;