Skip to content

Commit 032f4e7

Browse files
committed
fix tail call checks wrt #[track_caller]
only check the caller + disallow caller having the attr.
1 parent 99768c8 commit 032f4e7

File tree

5 files changed

+89
-23
lines changed

5 files changed

+89
-23
lines changed

compiler/rustc_mir_build/src/check_tail_calls.rs

+28-23
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,24 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
131131
}
132132

133133
{
134+
// `#[track_caller]` affects the ABI of a function (by adding a location argument),
135+
// so a `track_caller` can only tail call other `track_caller` functions.
136+
//
137+
// The issue is however that we can't know if a function is `track_caller` or not at
138+
// this point (THIR can be polymorthic, we may have an unresolved trait function).
139+
// We could only allow functions that we *can* resolve and *are* `track_caller`,
140+
// but that would turn changing `track_caller`-ness into a breaking change,
141+
// which is probably undesirable.
142+
//
143+
// Also note that we don't check callee's `track_caller`-ness at all, mostly for the
144+
// reasons above, but also because we can always tailcall the shim we'd generate for
145+
// coercing the function to an `fn()` pointer. (although in that case the tailcall is
146+
// basically useless -- the shim calls the actual function, so tailcalling the shim is
147+
// equivalent to calling the function)
134148
let caller_needs_location = self.needs_location(self.caller_ty);
135-
let callee_needs_location = self.needs_location(ty);
136149

137-
if caller_needs_location != callee_needs_location {
138-
self.report_track_caller_mismatch(expr.span, caller_needs_location);
150+
if caller_needs_location {
151+
self.report_track_caller_caller(expr.span);
139152
}
140153
}
141154

@@ -149,7 +162,9 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
149162
}
150163

151164
/// Returns true if function of type `ty` needs location argument
152-
/// (i.e. if a function is marked as `#[track_caller]`)
165+
/// (i.e. if a function is marked as `#[track_caller]`).
166+
///
167+
/// Panics if the function's instance can't be immediately resolved.
153168
fn needs_location(&self, ty: Ty<'tcx>) -> bool {
154169
if let &ty::FnDef(did, substs) = ty.kind() {
155170
let instance =
@@ -292,25 +307,15 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
292307
self.found_errors = Err(err);
293308
}
294309

295-
fn report_track_caller_mismatch(&mut self, sp: Span, caller_needs_location: bool) {
296-
let err = match caller_needs_location {
297-
true => self
298-
.tcx
299-
.dcx()
300-
.struct_span_err(
301-
sp,
302-
"a function marked with `#[track_caller]` cannot tail-call one that is not",
303-
)
304-
.emit(),
305-
false => self
306-
.tcx
307-
.dcx()
308-
.struct_span_err(
309-
sp,
310-
"a function mot marked with `#[track_caller]` cannot tail-call one that is",
311-
)
312-
.emit(),
313-
};
310+
fn report_track_caller_caller(&mut self, sp: Span) {
311+
let err = self
312+
.tcx
313+
.dcx()
314+
.struct_span_err(
315+
sp,
316+
"a function marked with `#[track_caller]` cannot perform a tail-call",
317+
)
318+
.emit();
314319

315320
self.found_errors = Err(err);
316321
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ check-pass
2+
// regression test for <https://github.com/rust-lang/rust/issues/134336>
3+
#![expect(incomplete_features)]
4+
#![feature(explicit_tail_calls)]
5+
6+
trait Tr {
7+
fn f();
8+
}
9+
10+
fn g<T: Tr>() {
11+
become T::f();
12+
}
13+
14+
fn main() {}
15+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//@ check-pass
2+
// FIXME(explicit_tail_calls): make this run-pass, once tail calls are properly implemented
3+
#![expect(incomplete_features)]
4+
#![feature(explicit_tail_calls)]
5+
6+
fn a(x: u32) -> u32 {
7+
become b(x);
8+
}
9+
10+
#[track_caller]
11+
fn b(x: u32) -> u32 { x + 42 }
12+
13+
fn main() {
14+
assert_eq!(a(12), 54);
15+
}
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![expect(incomplete_features)]
2+
#![feature(explicit_tail_calls)]
3+
4+
#[track_caller]
5+
fn a() {
6+
become b(); //~ error: a function marked with `#[track_caller]` cannot perform a tail-call
7+
}
8+
9+
fn b() {}
10+
11+
#[track_caller]
12+
fn c() {
13+
become a(); //~ error: a function marked with `#[track_caller]` cannot perform a tail-call
14+
}
15+
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: a function marked with `#[track_caller]` cannot perform a tail-call
2+
--> $DIR/caller_is_track_caller.rs:6:5
3+
|
4+
LL | become b();
5+
| ^^^^^^^^^^
6+
7+
error: a function marked with `#[track_caller]` cannot perform a tail-call
8+
--> $DIR/caller_is_track_caller.rs:13:5
9+
|
10+
LL | become a();
11+
| ^^^^^^^^^^
12+
13+
error: aborting due to 2 previous errors
14+

0 commit comments

Comments
 (0)