diff options
| author | bors <bors@rust-lang.org> | 2025-08-05 01:53:59 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-08-05 01:53:59 +0000 |
| commit | 0f353363965ebf05e0757f7679c800b39c51a07e (patch) | |
| tree | ba4c09b6d1616f0505e88cf7d9c76f7b0ed45e5b /compiler/rustc_mir_build | |
| parent | 0060d5a2a8a86a31f6299311fe64b1d755a91c4f (diff) | |
| parent | 5c11681820a843da59d7b9c5e848f3e9e7fd0f62 (diff) | |
| download | rust-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.rs | 51 |
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 |
