about summary refs log tree commit diff
path: root/compiler/rustc_mir_build
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-08-05 01:53:59 +0000
committerbors <bors@rust-lang.org>2025-08-05 01:53:59 +0000
commit0f353363965ebf05e0757f7679c800b39c51a07e (patch)
treeba4c09b6d1616f0505e88cf7d9c76f7b0ed45e5b /compiler/rustc_mir_build
parent0060d5a2a8a86a31f6299311fe64b1d755a91c4f (diff)
parent5c11681820a843da59d7b9c5e848f3e9e7fd0f62 (diff)
downloadrust-0f353363965ebf05e0757f7679c800b39c51a07e.tar.gz
rust-0f353363965ebf05e0757f7679c800b39c51a07e.zip
Auto merge of #144934 - samueltardieu:rollup-25jvb9g, r=samueltardieu
Rollup of 17 pull requests

Successful merges:

 - rust-lang/rust#144467 (rustdoc template font links only emit `crossorigin` when needed)
 - rust-lang/rust#144548 (Rehome 21 `tests/ui/issues/` tests to other subdirectories under `tests/ui/`)
 - rust-lang/rust#144741 (fix: Error on illegal `[const]`s inside blocks within legal positions)
 - rust-lang/rust#144776 (`Printer` cleanups)
 - rust-lang/rust#144779 (Implement debugging output of the bootstrap Step graph into a DOT file)
 - rust-lang/rust#144813 (Add a tidy check to prevent adding UI tests directly under `tests/ui/`)
 - rust-lang/rust#144817 (Properly reject tail calls to `&FnPtr` or `&FnDef`)
 - rust-lang/rust#144852 (Rename `rust_panic_without_hook` to `resume_unwind` )
 - rust-lang/rust#144866 (Remove `SHOULD_EMIT_LINTS` in favor of `should_emit`)
 - rust-lang/rust#144867 (Use `as_array` in PartialEq for arrays)
 - rust-lang/rust#144872 (Document Poisoning in `LazyCell` and `LazyLock`)
 - rust-lang/rust#144877 (coverage: Various small cleanups)
 - rust-lang/rust#144887 (`rust-analyzer` subtree update)
 - rust-lang/rust#144890 (Add `InterpCx::project_fields`)
 - rust-lang/rust#144894 (Delete `tests/ui/threads-sendsync/tcp-stress.rs`)
 - rust-lang/rust#144905 (rustc-dev-guide subtree update)
 - rust-lang/rust#144920 (Dont print arg span in MIR dump for tail call)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler/rustc_mir_build')
-rw-r--r--compiler/rustc_mir_build/src/check_tail_calls.rs51
1 files changed, 49 insertions, 2 deletions
diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs
index 6ed100899d8..b4c8b20e50f 100644
--- a/compiler/rustc_mir_build/src/check_tail_calls.rs
+++ b/compiler/rustc_mir_build/src/check_tail_calls.rs
@@ -95,9 +95,15 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
             // So we have to check for them in this weird way...
             let parent = self.tcx.parent(did);
             if self.tcx.fn_trait_kind_from_def_id(parent).is_some()
-                && args.first().and_then(|arg| arg.as_type()).is_some_and(Ty::is_closure)
+                && let Some(this) = args.first()
+                && let Some(this) = this.as_type()
             {
-                self.report_calling_closure(&self.thir[fun], args[1].as_type().unwrap(), expr);
+                if this.is_closure() {
+                    self.report_calling_closure(&self.thir[fun], args[1].as_type().unwrap(), expr);
+                } else {
+                    // This can happen when tail calling `Box` that wraps a function
+                    self.report_nonfn_callee(fn_span, self.thir[fun].span, this);
+                }
 
                 // Tail calling is likely to cause unrelated errors (ABI, argument mismatches),
                 // skip them, producing an error about calling a closure is enough.
@@ -109,6 +115,13 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
             }
         }
 
+        let (ty::FnDef(..) | ty::FnPtr(..)) = ty.kind() else {
+            self.report_nonfn_callee(fn_span, self.thir[fun].span, ty);
+
+            // `fn_sig` below panics otherwise
+            return;
+        };
+
         // Erase regions since tail calls don't care about lifetimes
         let callee_sig =
             self.tcx.normalize_erasing_late_bound_regions(self.typing_env, ty.fn_sig(self.tcx));
@@ -294,6 +307,40 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
         self.found_errors = Err(err);
     }
 
+    fn report_nonfn_callee(&mut self, call_sp: Span, fun_sp: Span, ty: Ty<'_>) {
+        let mut err = self
+            .tcx
+            .dcx()
+            .struct_span_err(
+                call_sp,
+                "tail calls can only be performed with function definitions or pointers",
+            )
+            .with_note(format!("callee has type `{ty}`"));
+
+        let mut ty = ty;
+        let mut refs = 0;
+        while ty.is_box() || ty.is_ref() {
+            ty = ty.builtin_deref(false).unwrap();
+            refs += 1;
+        }
+
+        if refs > 0 && ty.is_fn() {
+            let thing = if ty.is_fn_ptr() { "pointer" } else { "definition" };
+
+            let derefs =
+                std::iter::once('(').chain(std::iter::repeat_n('*', refs)).collect::<String>();
+
+            err.multipart_suggestion(
+                format!("consider dereferencing the expression to get a function {thing}"),
+                vec![(fun_sp.shrink_to_lo(), derefs), (fun_sp.shrink_to_hi(), ")".to_owned())],
+                Applicability::MachineApplicable,
+            );
+        }
+
+        let err = err.emit();
+        self.found_errors = Err(err);
+    }
+
     fn report_abi_mismatch(&mut self, sp: Span, caller_abi: ExternAbi, callee_abi: ExternAbi) {
         let err = self
             .tcx