Skip to content

Fix an ICE observed with an explicit tail-call in a default trait method #145270

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 13 additions & 19 deletions compiler/rustc_mir_build/src/check_tail_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::Applicability;
use rustc_hir::LangItem;
use rustc_hir::def::DefKind;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::span_bug;
use rustc_middle::thir::visit::{self, Visitor};
use rustc_middle::thir::{BodyTy, Expr, ExprId, ExprKind, Thir};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use rustc_span::{ErrorGuaranteed, Span};

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

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

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

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

impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
Expand Down Expand Up @@ -167,7 +167,7 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
// coercing the function to an `fn()` pointer. (although in that case the tailcall is
// basically useless -- the shim calls the actual function, so tailcalling the shim is
// equivalent to calling the function)
let caller_needs_location = self.needs_location(self.caller_ty);
let caller_needs_location = self.caller_needs_location();

if caller_needs_location {
self.report_track_caller_caller(expr.span);
Expand All @@ -183,19 +183,13 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
}
}

/// Returns true if function of type `ty` needs location argument
/// (i.e. if a function is marked as `#[track_caller]`).
///
/// Panics if the function's instance can't be immediately resolved.
fn needs_location(&self, ty: Ty<'tcx>) -> bool {
if let &ty::FnDef(did, substs) = ty.kind() {
let instance =
ty::Instance::expect_resolve(self.tcx, self.typing_env, did, substs, DUMMY_SP);

instance.def.requires_caller_location(self.tcx)
} else {
false
}
/// Returns true if the caller function needs location argument
/// (i.e. if a function is marked as `#[track_caller]`)
fn caller_needs_location(&self) -> bool {
self.tcx
.codegen_fn_attrs(self.caller_def_id)
.flags
.contains(CodegenFnAttrFlags::TRACK_CALLER)
}

fn report_in_closure(&mut self, expr: &Expr<'_>) {
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/explicit-tail-calls/caller_is_track_caller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,13 @@ fn c() {
become a(); //~ error: a function marked with `#[track_caller]` cannot perform a tail-call
}

trait Trait {
fn d(&self);

#[track_caller]
fn e(&self) {
become self.d(); //~ error: a function marked with `#[track_caller]` cannot perform a tail-call
}
}

fn main() {}
8 changes: 7 additions & 1 deletion tests/ui/explicit-tail-calls/caller_is_track_caller.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,11 @@ error: a function marked with `#[track_caller]` cannot perform a tail-call
LL | become a();
| ^^^^^^^^^^

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

error: aborting due to 3 previous errors

36 changes: 36 additions & 0 deletions tests/ui/explicit-tail-calls/default-trait-method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// A regression test for <https://github.com/rust-lang/rust/issues/144985>.
// Previously, using `become` in a default trait method would lead to an ICE
// in a path determining whether the method in question is marked as `#[track_caller]`.
//
//@ run-pass

#![feature(explicit_tail_calls)]
#![expect(incomplete_features)]

trait Trait {
fn bar(&self) -> usize {
123
}

fn foo(&self) -> usize {
become self.bar();
}
}

struct Struct;

impl Trait for Struct {}

struct OtherStruct;

impl Trait for OtherStruct {
#[track_caller]
fn bar(&self) -> usize {
456
}
}

fn main() {
assert_eq!(Struct.foo(), 123);
assert_eq!(OtherStruct.foo(), 456);
}
Loading