mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Fix an ICE when using become in a default trait method
The logic determining whether the relevant function is marked as `#[track_caller]` only worked with functions that could be resolved to a concrete instance, which default trait methods cannot. We need to check the codegen attribute on the default method itself. Co-authored-by: Folkert de Vries <folkert@folkertdev.nl>
This commit is contained in:
committed by
Folkert de Vries
parent
ec2d669db8
commit
ac5734a779
@@ -109,6 +109,7 @@
|
||||
SHADOWING_SUPERTRAIT_ITEMS,
|
||||
SINGLE_USE_LIFETIMES,
|
||||
STABLE_FEATURES,
|
||||
TAIL_CALL_TRACK_CALLER,
|
||||
TAIL_EXPR_DROP_ORDER,
|
||||
TEST_UNSTABLE_LINT,
|
||||
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||
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)?;
|
||||
@@ -21,7 +22,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,
|
||||
@@ -30,7 +30,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]);
|
||||
@@ -47,8 +47,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> {
|
||||
@@ -148,11 +148,13 @@ fn check_tail_call(&mut self, call: &Expr<'_>, expr: &Expr<'_>) {
|
||||
// we should think what is the expected behavior here.
|
||||
// (we should probably just accept this by revealing opaques?)
|
||||
if caller_sig.inputs_and_output != callee_sig.inputs_and_output {
|
||||
let caller_ty = self.tcx.type_of(self.caller_def_id).skip_binder();
|
||||
|
||||
self.report_signature_mismatch(
|
||||
expr.span,
|
||||
self.tcx.liberate_late_bound_regions(
|
||||
CRATE_DEF_ID.to_def_id(),
|
||||
self.caller_ty.fn_sig(self.tcx),
|
||||
caller_ty.fn_sig(self.tcx),
|
||||
),
|
||||
self.tcx.liberate_late_bound_regions(CRATE_DEF_ID.to_def_id(), ty.fn_sig(self.tcx)),
|
||||
);
|
||||
@@ -173,7 +175,7 @@ fn check_tail_call(&mut self, call: &Expr<'_>, expr: &Expr<'_>) {
|
||||
// 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);
|
||||
@@ -189,19 +191,11 @@ fn check_tail_call(&mut self, call: &Expr<'_>, expr: &Expr<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 a location argument
|
||||
/// (i.e. if a function is marked as `#[track_caller]`)
|
||||
fn caller_needs_location(&self) -> bool {
|
||||
let flags = self.tcx.codegen_fn_attrs(self.caller_def_id).flags;
|
||||
flags.contains(CodegenFnAttrFlags::TRACK_CALLER)
|
||||
}
|
||||
|
||||
fn report_in_closure(&mut self, expr: &Expr<'_>) {
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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
|
||||
//@ ignore-backends: gcc
|
||||
|
||||
#![feature(explicit_tail_calls)]
|
||||
#![expect(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn bar(&self) -> usize {
|
||||
123
|
||||
}
|
||||
|
||||
fn foo(&self) -> usize {
|
||||
#[allow(tail_call_track_caller)]
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user