fix tail calls to #[track_caller] functions
This commit is contained in:
parent
07b7dc90ee
commit
85d1c89e0f
9 changed files with 134 additions and 8 deletions
|
|
@ -3622,6 +3622,7 @@ dependencies = [
|
|||
"rustc_hir",
|
||||
"rustc_incremental",
|
||||
"rustc_index",
|
||||
"rustc_lint_defs",
|
||||
"rustc_macros",
|
||||
"rustc_metadata",
|
||||
"rustc_middle",
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ rustc_hashes = { path = "../rustc_hashes" }
|
|||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_incremental = { path = "../rustc_incremental" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_lint_defs = { path = "../rustc_lint_defs" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_metadata = { path = "../rustc_metadata" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use rustc_ast as ast;
|
|||
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER;
|
||||
use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason};
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
|
||||
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
||||
|
|
@ -906,7 +907,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
fn_span,
|
||||
);
|
||||
|
||||
let instance = match instance.def {
|
||||
match instance.def {
|
||||
// We don't need AsyncDropGlueCtorShim here because it is not `noop func`,
|
||||
// it is `func returning noop future`
|
||||
ty::InstanceKind::DropGlue(_, None) => {
|
||||
|
|
@ -995,14 +996,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
intrinsic.name,
|
||||
);
|
||||
}
|
||||
instance
|
||||
(Some(instance), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => instance,
|
||||
};
|
||||
|
||||
(Some(instance), None)
|
||||
_ if kind == CallKind::Tail
|
||||
&& instance.def.requires_caller_location(bx.tcx()) =>
|
||||
{
|
||||
if let Some(hir_id) =
|
||||
terminator.source_info.scope.lint_root(&self.mir.source_scopes)
|
||||
{
|
||||
let msg = "tail calling a function marked with `#[track_caller]` has no special effect";
|
||||
bx.tcx().node_lint(TAIL_CALL_TRACK_CALLER, hir_id, |d| {
|
||||
_ = d.primary_message(msg).span(fn_span)
|
||||
});
|
||||
}
|
||||
|
||||
let instance = ty::Instance::resolve_for_fn_ptr(
|
||||
bx.tcx(),
|
||||
bx.typing_env(),
|
||||
def_id,
|
||||
generic_args,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(None, Some(bx.get_fn_addr(instance)))
|
||||
}
|
||||
_ => (Some(instance), None),
|
||||
}
|
||||
}
|
||||
ty::FnPtr(..) => (None, Some(callee.immediate())),
|
||||
_ => bug!("{} is not callable", callee.layout.ty),
|
||||
|
|
|
|||
|
|
@ -5100,3 +5100,36 @@ declare_lint! {
|
|||
report_in_deps: true,
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `tail_call_track_caller` lint detects usage of `become` attempting to tail call
|
||||
/// a function marked with `#[track_caller]`.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(explicit_tail_calls)]
|
||||
/// #![expect(incomplete_features)]
|
||||
///
|
||||
/// #[track_caller]
|
||||
/// fn f() {}
|
||||
///
|
||||
/// fn g() {
|
||||
/// become f();
|
||||
/// }
|
||||
///
|
||||
/// g();
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Due to implementation details of tail calls and `#[track_caller]` attribute, calls to
|
||||
/// functions marked with `#[track_caller]` cannot become tail calls. As such using `become`
|
||||
/// is no different than a normal call (except for changes in drop order).
|
||||
pub TAIL_CALL_TRACK_CALLER,
|
||||
Warn,
|
||||
"detects tail calls of functions marked with `#[track_caller]`",
|
||||
@feature_gate = explicit_tail_calls;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -788,7 +788,35 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
|
|||
// *Before* monomorphizing, record that we already handled this mention.
|
||||
self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty));
|
||||
let callee_ty = self.monomorphize(callee_ty);
|
||||
visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items)
|
||||
|
||||
// HACK(explicit_tail_calls): collect tail calls to `#[track_caller]` functions as indirect,
|
||||
// because we later call them as such, to prevent issues with ABI incompatibility.
|
||||
// Ideally we'd replace such tail calls with normal call + return, but this requires
|
||||
// post-mono MIR optimizations, which we don't yet have.
|
||||
let force_indirect_call =
|
||||
if matches!(terminator.kind, mir::TerminatorKind::TailCall { .. })
|
||||
&& let &ty::FnDef(def_id, args) = callee_ty.kind()
|
||||
&& let instance = ty::Instance::expect_resolve(
|
||||
self.tcx,
|
||||
ty::TypingEnv::fully_monomorphized(),
|
||||
def_id,
|
||||
args,
|
||||
source,
|
||||
)
|
||||
&& instance.def.requires_caller_location(self.tcx)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
visit_fn_use(
|
||||
self.tcx,
|
||||
callee_ty,
|
||||
!force_indirect_call,
|
||||
source,
|
||||
&mut self.used_items,
|
||||
)
|
||||
}
|
||||
mir::TerminatorKind::Drop { ref place, .. } => {
|
||||
let ty = place.ty(self.body, self.tcx).ty;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
//@ check-pass
|
||||
// FIXME(explicit_tail_calls): make this run-pass, once tail calls are properly implemented
|
||||
//@ run-pass
|
||||
//@ ignore-pass
|
||||
#![expect(incomplete_features)]
|
||||
#![feature(explicit_tail_calls)]
|
||||
|
||||
fn a(x: u32) -> u32 {
|
||||
become b(x);
|
||||
//~^ warning: tail calling a function marked with `#[track_caller]` has no special effect
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
|
|||
10
tests/ui/explicit-tail-calls/callee_is_track_caller.stderr
Normal file
10
tests/ui/explicit-tail-calls/callee_is_track_caller.stderr
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
warning: tail calling a function marked with `#[track_caller]` has no special effect
|
||||
--> $DIR/callee_is_track_caller.rs:7:12
|
||||
|
|
||||
LL | become b(x);
|
||||
| ^^^^
|
||||
|
|
||||
= note: `#[warn(tail_call_track_caller)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
//@ run-pass
|
||||
//@ ignore-pass
|
||||
#![expect(incomplete_features)]
|
||||
#![feature(explicit_tail_calls)]
|
||||
|
||||
fn c<T: Trait>() {
|
||||
become T::f();
|
||||
//~^ warning: tail calling a function marked with `#[track_caller]` has no special effect
|
||||
}
|
||||
|
||||
trait Trait {
|
||||
#[track_caller]
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
impl Trait for () {}
|
||||
|
||||
fn main() {
|
||||
c::<()>();
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
warning: tail calling a function marked with `#[track_caller]` has no special effect
|
||||
--> $DIR/callee_is_track_caller_polymorphic.rs:7:12
|
||||
|
|
||||
LL | become T::f();
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: `#[warn(tail_call_track_caller)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue