Skip to content

Commit 3530d82

Browse files
Rollup merge of #145270 - jakubadamw:issue-144985, r=WaffleLapkin
Fix an ICE observed with an explicit tail-call in a default trait method Right now, explicit tail-calls cannot be used in functions that hold the `#[track_caller]` attribute. This check is performed on a resolved concrete instance of a function, which would be absent for a tail-call performed from the body of a default trait method. This PR fixes the issue by checking for the relevant attribute in default trait methods separately, without expecting a specific resolved instance. Closes #144985.
2 parents 3ca43dc + ac5734a commit 3530d82

5 files changed

Lines changed: 69 additions & 21 deletions

File tree

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ declare_lint_pass! {
109109
SHADOWING_SUPERTRAIT_ITEMS,
110110
SINGLE_USE_LIFETIMES,
111111
STABLE_FEATURES,
112+
TAIL_CALL_TRACK_CALLER,
112113
TAIL_EXPR_DROP_ORDER,
113114
TEST_UNSTABLE_LINT,
114115
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,

compiler/rustc_mir_build/src/check_tail_calls.rs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ use rustc_errors::Applicability;
44
use rustc_hir::LangItem;
55
use rustc_hir::def::DefKind;
66
use rustc_hir::def_id::CRATE_DEF_ID;
7+
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
78
use rustc_middle::span_bug;
89
use rustc_middle::thir::visit::{self, Visitor};
910
use rustc_middle::thir::{BodyTy, Expr, ExprId, ExprKind, Thir};
1011
use rustc_middle::ty::{self, Ty, TyCtxt};
1112
use rustc_span::def_id::{DefId, LocalDefId};
12-
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
13+
use rustc_span::{ErrorGuaranteed, Span};
1314

1415
pub(crate) fn check_tail_calls(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<(), ErrorGuaranteed> {
1516
let (thir, expr) = tcx.thir_body(def)?;
@@ -21,7 +22,6 @@ pub(crate) fn check_tail_calls(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<(), E
2122
}
2223

2324
let is_closure = matches!(tcx.def_kind(def), DefKind::Closure);
24-
let caller_ty = tcx.type_of(def).skip_binder();
2525

2626
let mut visitor = TailCallCkVisitor {
2727
tcx,
@@ -30,7 +30,7 @@ pub(crate) fn check_tail_calls(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<(), E
3030
// FIXME(#132279): we're clearly in a body here.
3131
typing_env: ty::TypingEnv::non_body_analysis(tcx, def),
3232
is_closure,
33-
caller_ty,
33+
caller_def_id: def,
3434
};
3535

3636
visitor.visit_expr(&thir[expr]);
@@ -47,8 +47,8 @@ struct TailCallCkVisitor<'a, 'tcx> {
4747
/// The result of the checks, `Err(_)` if there was a problem with some
4848
/// tail call, `Ok(())` if all of them were fine.
4949
found_errors: Result<(), ErrorGuaranteed>,
50-
/// Type of the caller function.
51-
caller_ty: Ty<'tcx>,
50+
/// `LocalDefId` of the caller function.
51+
caller_def_id: LocalDefId,
5252
}
5353

5454
impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
@@ -148,11 +148,13 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
148148
// we should think what is the expected behavior here.
149149
// (we should probably just accept this by revealing opaques?)
150150
if caller_sig.inputs_and_output != callee_sig.inputs_and_output {
151+
let caller_ty = self.tcx.type_of(self.caller_def_id).skip_binder();
152+
151153
self.report_signature_mismatch(
152154
expr.span,
153155
self.tcx.liberate_late_bound_regions(
154156
CRATE_DEF_ID.to_def_id(),
155-
self.caller_ty.fn_sig(self.tcx),
157+
caller_ty.fn_sig(self.tcx),
156158
),
157159
self.tcx.liberate_late_bound_regions(CRATE_DEF_ID.to_def_id(), ty.fn_sig(self.tcx)),
158160
);
@@ -173,7 +175,7 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
173175
// coercing the function to an `fn()` pointer. (although in that case the tailcall is
174176
// basically useless -- the shim calls the actual function, so tailcalling the shim is
175177
// equivalent to calling the function)
176-
let caller_needs_location = self.needs_location(self.caller_ty);
178+
let caller_needs_location = self.caller_needs_location();
177179

178180
if caller_needs_location {
179181
self.report_track_caller_caller(expr.span);
@@ -189,19 +191,11 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
189191
}
190192
}
191193

192-
/// Returns true if function of type `ty` needs location argument
193-
/// (i.e. if a function is marked as `#[track_caller]`).
194-
///
195-
/// Panics if the function's instance can't be immediately resolved.
196-
fn needs_location(&self, ty: Ty<'tcx>) -> bool {
197-
if let &ty::FnDef(did, substs) = ty.kind() {
198-
let instance =
199-
ty::Instance::expect_resolve(self.tcx, self.typing_env, did, substs, DUMMY_SP);
200-
201-
instance.def.requires_caller_location(self.tcx)
202-
} else {
203-
false
204-
}
194+
/// Returns true if the caller function needs a location argument
195+
/// (i.e. if a function is marked as `#[track_caller]`)
196+
fn caller_needs_location(&self) -> bool {
197+
let flags = self.tcx.codegen_fn_attrs(self.caller_def_id).flags;
198+
flags.contains(CodegenFnAttrFlags::TRACK_CALLER)
205199
}
206200

207201
fn report_in_closure(&mut self, expr: &Expr<'_>) {

tests/ui/explicit-tail-calls/caller_is_track_caller.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,13 @@ fn c() {
1313
become a(); //~ error: a function marked with `#[track_caller]` cannot perform a tail-call
1414
}
1515

16+
trait Trait {
17+
fn d(&self);
18+
19+
#[track_caller]
20+
fn e(&self) {
21+
become self.d(); //~ error: a function marked with `#[track_caller]` cannot perform a tail-call
22+
}
23+
}
24+
1625
fn main() {}

tests/ui/explicit-tail-calls/caller_is_track_caller.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,11 @@ error: a function marked with `#[track_caller]` cannot perform a tail-call
1010
LL | become a();
1111
| ^^^^^^^^^^
1212

13-
error: aborting due to 2 previous errors
13+
error: a function marked with `#[track_caller]` cannot perform a tail-call
14+
--> $DIR/caller_is_track_caller.rs:21:9
15+
|
16+
LL | become self.d();
17+
| ^^^^^^^^^^^^^^^
18+
19+
error: aborting due to 3 previous errors
1420

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// A regression test for <https://github.com/rust-lang/rust/issues/144985>.
2+
// Previously, using `become` in a default trait method would lead to an ICE
3+
// in a path determining whether the method in question is marked as `#[track_caller]`.
4+
//
5+
//@ run-pass
6+
//@ ignore-backends: gcc
7+
8+
#![feature(explicit_tail_calls)]
9+
#![expect(incomplete_features)]
10+
11+
trait Trait {
12+
fn bar(&self) -> usize {
13+
123
14+
}
15+
16+
fn foo(&self) -> usize {
17+
#[allow(tail_call_track_caller)]
18+
become self.bar();
19+
}
20+
}
21+
22+
struct Struct;
23+
24+
impl Trait for Struct {}
25+
26+
struct OtherStruct;
27+
28+
impl Trait for OtherStruct {
29+
#[track_caller]
30+
fn bar(&self) -> usize {
31+
456
32+
}
33+
}
34+
35+
fn main() {
36+
assert_eq!(Struct.foo(), 123);
37+
assert_eq!(OtherStruct.foo(), 456);
38+
}

0 commit comments

Comments
 (0)