Rollup merge of #144817 - WaffleLapkin:reject-referety, r=Urgau
Properly reject tail calls to `&FnPtr` or `&FnDef` Fixes rust-lang/rust#144795
This commit is contained in:
commit
eee8d775fe
7 changed files with 195 additions and 3 deletions
|
|
@ -1456,7 +1456,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the type and mutability of `*ty`.
|
||||
/// Returns the type of `*ty`.
|
||||
///
|
||||
/// The parameter `explicit` indicates if this is an *explicit* dereference.
|
||||
/// Some types -- notably raw ptrs -- can only be dereferenced explicitly.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
26
tests/ui/explicit-tail-calls/callee_is_ref.fixed
Normal file
26
tests/ui/explicit-tail-calls/callee_is_ref.fixed
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//@ run-rustfix
|
||||
#![feature(explicit_tail_calls)]
|
||||
#![expect(incomplete_features)]
|
||||
|
||||
fn f() {}
|
||||
|
||||
fn g() {
|
||||
become (*(&f))() //~ error: tail calls can only be performed with function definitions or pointers
|
||||
}
|
||||
|
||||
fn h() {
|
||||
let table = [f as fn()];
|
||||
if let Some(fun) = table.get(0) {
|
||||
become (*fun)(); //~ error: tail calls can only be performed with function definitions or pointers
|
||||
}
|
||||
}
|
||||
|
||||
fn i() {
|
||||
become (***Box::new(&mut &f))(); //~ error: tail calls can only be performed with function definitions or pointers
|
||||
}
|
||||
|
||||
fn main() {
|
||||
g();
|
||||
h();
|
||||
i();
|
||||
}
|
||||
26
tests/ui/explicit-tail-calls/callee_is_ref.rs
Normal file
26
tests/ui/explicit-tail-calls/callee_is_ref.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//@ run-rustfix
|
||||
#![feature(explicit_tail_calls)]
|
||||
#![expect(incomplete_features)]
|
||||
|
||||
fn f() {}
|
||||
|
||||
fn g() {
|
||||
become (&f)() //~ error: tail calls can only be performed with function definitions or pointers
|
||||
}
|
||||
|
||||
fn h() {
|
||||
let table = [f as fn()];
|
||||
if let Some(fun) = table.get(0) {
|
||||
become fun(); //~ error: tail calls can only be performed with function definitions or pointers
|
||||
}
|
||||
}
|
||||
|
||||
fn i() {
|
||||
become Box::new(&mut &f)(); //~ error: tail calls can only be performed with function definitions or pointers
|
||||
}
|
||||
|
||||
fn main() {
|
||||
g();
|
||||
h();
|
||||
i();
|
||||
}
|
||||
38
tests/ui/explicit-tail-calls/callee_is_ref.stderr
Normal file
38
tests/ui/explicit-tail-calls/callee_is_ref.stderr
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
error: tail calls can only be performed with function definitions or pointers
|
||||
--> $DIR/callee_is_ref.rs:8:12
|
||||
|
|
||||
LL | become (&f)()
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: callee has type `&fn() {f}`
|
||||
help: consider dereferencing the expression to get a function definition
|
||||
|
|
||||
LL | become (*(&f))()
|
||||
| ++ +
|
||||
|
||||
error: tail calls can only be performed with function definitions or pointers
|
||||
--> $DIR/callee_is_ref.rs:14:16
|
||||
|
|
||||
LL | become fun();
|
||||
| ^^^^^
|
||||
|
|
||||
= note: callee has type `&fn()`
|
||||
help: consider dereferencing the expression to get a function pointer
|
||||
|
|
||||
LL | become (*fun)();
|
||||
| ++ +
|
||||
|
||||
error: tail calls can only be performed with function definitions or pointers
|
||||
--> $DIR/callee_is_ref.rs:19:12
|
||||
|
|
||||
LL | become Box::new(&mut &f)();
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: callee has type `Box<&mut &fn() {f}>`
|
||||
help: consider dereferencing the expression to get a function definition
|
||||
|
|
||||
LL | become (***Box::new(&mut &f))();
|
||||
| ++++ +
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
29
tests/ui/explicit-tail-calls/callee_is_weird.rs
Normal file
29
tests/ui/explicit-tail-calls/callee_is_weird.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#![feature(explicit_tail_calls, exclusive_wrapper, fn_traits, unboxed_closures)]
|
||||
#![expect(incomplete_features)]
|
||||
|
||||
fn f() {}
|
||||
|
||||
fn g() {
|
||||
become std::sync::Exclusive::new(f)() //~ error: tail calls can only be performed with function definitions or pointers
|
||||
}
|
||||
|
||||
fn h() {
|
||||
become (&mut &std::sync::Exclusive::new(f))() //~ error: tail calls can only be performed with function definitions or pointers
|
||||
}
|
||||
|
||||
fn i() {
|
||||
struct J;
|
||||
|
||||
impl FnOnce<()> for J {
|
||||
type Output = ();
|
||||
extern "rust-call" fn call_once(self, (): ()) -> Self::Output {}
|
||||
}
|
||||
|
||||
become J(); //~ error: tail calls can only be performed with function definitions or pointers
|
||||
}
|
||||
|
||||
fn main() {
|
||||
g();
|
||||
h();
|
||||
i();
|
||||
}
|
||||
26
tests/ui/explicit-tail-calls/callee_is_weird.stderr
Normal file
26
tests/ui/explicit-tail-calls/callee_is_weird.stderr
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
error: tail calls can only be performed with function definitions or pointers
|
||||
--> $DIR/callee_is_weird.rs:7:12
|
||||
|
|
||||
LL | become std::sync::Exclusive::new(f)()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: callee has type `Exclusive<fn() {f}>`
|
||||
|
||||
error: tail calls can only be performed with function definitions or pointers
|
||||
--> $DIR/callee_is_weird.rs:11:12
|
||||
|
|
||||
LL | become (&mut &std::sync::Exclusive::new(f))()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: callee has type `Exclusive<fn() {f}>`
|
||||
|
||||
error: tail calls can only be performed with function definitions or pointers
|
||||
--> $DIR/callee_is_weird.rs:22:12
|
||||
|
|
||||
LL | become J();
|
||||
| ^^^
|
||||
|
|
||||
= note: callee has type `J`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue