diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f4bfec24e3b1b..08243203d1b19 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use rustc_ast::node_id::NodeMap; use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::*; +use rustc_attr_ir::find_attr; use rustc_attr_ir::lang_items::LangItem; use rustc_attr_ir::target::Target; use rustc_errors::msg; @@ -411,6 +412,7 @@ impl<'hir> LoweringContext<'_, 'hir> { |this| { this.with_new_scopes(e.span, |this| this.lower_block_expr(block)) }, + None, ) }); let Some(move_expr_state) = move_expr_state else { @@ -866,6 +868,7 @@ impl<'hir> LoweringContext<'_, 'hir> { desugaring_kind: hir::CoroutineDesugaring, coroutine_source: hir::CoroutineSource, body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, + captured_caller_location: Option, ) -> hir::ExprKind<'hir> { let closure_def_id = self.local_def_id(closure_node_id); let coroutine_kind = hir::CoroutineKind::Desugared(desugaring_kind, coroutine_source); @@ -957,11 +960,49 @@ impl<'hir> LoweringContext<'_, 'hir> { kind: hir::ClosureKind::Coroutine(coroutine_kind), constness: hir::Constness::NotConst, explicit_captures, + captured_caller_location, })) } + /// Checks whether `#[track_caller]` annotation on a coroutine function + /// or coroutine closure exists, and should affect the generated coroutine inside. + /// Currently used only for coroutine fns, not coroutine closures. + /// + /// FIXME(closure_track_caller): Change coroutine closures to use this. + pub(super) fn should_track_caller_in_coroutine( + &self, + hir_id: HirId, + node_id: NodeId, + span: Span, + is_in_trait_impl: bool, + ) -> bool { + if !self.tcx.features().async_fn_track_caller() { + return false; + } + if let Some(attrs) = self.curr_owner.attrs.get(&hir_id.local_id) + && find_attr!(*attrs, TrackCaller(_)) + { + // The coroutine function itself is annotated with #[track_caller] + return true; + } + if is_in_trait_impl { + // Check if we need to "inherit" #[track_caller] from the trait definition. + let Some(trait_item_def_id) = + self.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id()) + else { + self.dcx().span_delayed_bug(span, "could not resolve trait item being implemented"); + return false; + }; + return find_attr!(self.tcx, trait_item_def_id, TrackCaller(_)); + } + false + } + /// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to /// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled. + /// Currently only used for coroutine closures, not coroutine fns. + /// + /// FIXME(closure_track_caller): Remove this function. pub(super) fn maybe_forward_track_caller(&mut self, outer_hir_id: HirId, inner_hir_id: HirId) { if self.tcx.features().async_fn_track_caller() && let Some(attrs) = self.curr_owner.attrs.get(&outer_hir_id.local_id) diff --git a/compiler/rustc_ast_lowering/src/expr/closure.rs b/compiler/rustc_ast_lowering/src/expr/closure.rs index c72956545b7c8..c9a08505823c3 100644 --- a/compiler/rustc_ast_lowering/src/expr/closure.rs +++ b/compiler/rustc_ast_lowering/src/expr/closure.rs @@ -219,6 +219,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind: closure_kind, constness: self.lower_constness(attrs, constness), explicit_captures, + captured_caller_location: None, }); (hir::ExprKind::Closure(c), move_expr_state) @@ -315,8 +316,12 @@ impl<'hir> LoweringContext<'_, 'hir> { body.span, coroutine_marker, hir::CoroutineSource::Closure, + false, ); + // FIXME(closure_track_caller): Currently, coroutine closures, + // unlike coroutine fns, have #[track_caller] track the poller instead of + // the caller of the closure. this.maybe_forward_track_caller(closure_hir_id, expr.hir_id); (parameters, expr) @@ -358,6 +363,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring), constness: self.lower_constness(attrs, constness), explicit_captures, + captured_caller_location: None, }); hir::ExprKind::Closure(c) } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 6ec5903c5fb3b..52b2ad1cf7be3 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -2,7 +2,7 @@ use rustc_abi::ExternAbi; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; use rustc_attr_ir::target::Target; -use rustc_attr_ir::{AttributeKind, EiiImplResolution, find_attr}; +use rustc_attr_ir::{AttributeKind, EiiImplResolution, LangItem, find_attr}; use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{self as hir, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin}; @@ -268,12 +268,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let body_id = this.lower_maybe_coroutine_body( *fn_sig_span, span, + id, hir_id, decl, coroutine_marker, body.as_deref(), attrs, contract.as_deref(), + false, ); let itctx = ImplTraitContext::Universal; @@ -867,12 +869,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let body_id = self.lower_maybe_coroutine_body( sig.span, i.span, + i.id, hir_id, &sig.decl, sig.header.coroutine_marker, Some(body), attrs, contract.as_deref(), + false, ); let (generics, sig) = self.lower_method_sig( generics, @@ -1077,12 +1081,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let body_id = self.lower_maybe_coroutine_body( sig.span, i.span, + i.id, hir_id, &sig.decl, sig.header.coroutine_marker, body.as_deref(), attrs, contract.as_deref(), + is_in_trait_impl, ); let (generics, sig) = self.lower_method_sig( generics, @@ -1271,12 +1277,14 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, fn_decl_span: Span, span: Span, + node_id: NodeId, fn_id: hir::HirId, decl: &FnDecl, coroutine_marker: Option, body: Option<&Block>, attrs: &'hir [rustc_attr_ir::Attribute], contract: Option<&FnContract>, + is_in_trait_impl: bool, ) -> hir::BodyId { let Some(body) = body else { // Functions without a body are an error, except if this is an intrinsic. For those we @@ -1311,20 +1319,15 @@ impl<'hir> LoweringContext<'_, 'hir> { }; // FIXME(contracts): Support contracts on async fn. self.lower_body(|this| { - let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( + this.lower_coroutine_body_with_moved_arguments( decl, |this| this.lower_block_expr(body), fn_decl_span, body.span, coroutine_marker, hir::CoroutineSource::Fn, - ); - - // FIXME(async_fn_track_caller): Can this be moved above? - let hir_id = expr.hir_id; - this.maybe_forward_track_caller(fn_id, hir_id); - - (parameters, expr) + this.should_track_caller_in_coroutine(fn_id, node_id, span, is_in_trait_impl), + ) }) } @@ -1340,6 +1343,7 @@ impl<'hir> LoweringContext<'_, 'hir> { body_span: Span, coroutine_marker: CoroutineMarker, coroutine_source: hir::CoroutineSource, + track_caller: bool, ) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>) { let mut parameters: Vec> = Vec::new(); let mut statements: Vec> = Vec::new(); @@ -1472,6 +1476,42 @@ impl<'hir> LoweringContext<'_, 'hir> { parameters.push(new_parameter); } + let (caller_location_init_stmt, caller_location_hir_id) = track_caller + .then(|| { + let ident = Ident::with_dummy_span(sym::__captured_caller_location); + let span = self.mark_span_with_reason( + DesugaringKind::CoroutineFnTrackCaller, + DUMMY_SP, + Some([sym::core_intrinsics].into()), + ); + + // Get the caller location inside the function/closure body, but outside the coroutine. + let (outer_pat, outer_pat_hir_id) = self.pat_ident(span, ident); + let outer_expr = self.expr_call_lang_item_fn(span, LangItem::CallerLocation, &[]); + let outer_let_stmt = self.stmt_let_pat( + None, + span, + Some(outer_expr), + outer_pat, + hir::LocalSource::AsyncFn, + ); + + // Capture the stored caller location in the coroutine. + let (inner_pat, _inner_pat_hir_id) = self.pat_ident(span, ident); + let inner_expr = self.expr_ident(span, ident, outer_pat_hir_id); + let inner_let_stmt = self.stmt_let_pat( + None, + span, + Some(inner_expr), + inner_pat, + hir::LocalSource::AsyncFn, + ); + statements.push(inner_let_stmt); + + (outer_let_stmt, outer_pat_hir_id) + }) + .unzip(); + let mkbody = |this: &mut LoweringContext<'_, 'hir>| { // Create a block from the user's function body: let user_body = lower_body(this); @@ -1505,7 +1545,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let closure_id = coroutine_marker.closure_id; - let coroutine_expr = self.make_desugared_coroutine_expr( + let coroutine_expr_kind = self.make_desugared_coroutine_expr( // The default capture mode here is by-ref. Later on during upvar analysis, // we will force the captured arguments to by-move, but for async closures, // we want to make sure that we avoid unnecessarily moving captures, or else @@ -1518,15 +1558,28 @@ impl<'hir> LoweringContext<'_, 'hir> { desugaring_kind, coroutine_source, mkbody, + caller_location_hir_id, ); - - let expr = hir::Expr { + let coroutine_expr = hir::Expr { hir_id: self.lower_node_id(closure_id), - kind: coroutine_expr, + kind: coroutine_expr_kind, span: self.lower_span(body_span), }; - (self.arena.alloc_from_iter(parameters), expr) + let body_expr = match caller_location_init_stmt { + Some(init_stmt) => { + let body_block = self.block_all( + DUMMY_SP, + self.arena.alloc_from_iter([init_stmt]), + Some(self.arena.alloc(coroutine_expr)), + ); + let body_expr_kind = hir::ExprKind::Block(body_block, None); + hir::Expr { hir_id: self.next_id(), kind: body_expr_kind, span: DUMMY_SP } + } + None => coroutine_expr, + }; + + (self.arena.alloc_from_iter(parameters), body_expr) } fn lower_method_sig( diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index 60890f7799290..acebb90f9a6c2 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -469,6 +469,10 @@ language_item_table! { // Experimental lang item for `Reflection and comptime`(https://goals.rust-lang.org/2025h2/reflection-and-comptime.html) FnPtr, sym::FnPtr, fn_ptr, Target::Struct, GenericRequirement::None; + + // Used in the desugaring of #[track_caller] on coroutine functions + // FIXME(closure_track_caller): Also use this in coroutine closures. + CallerLocation, sym::caller_location, caller_location, Target::Fn, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 7f46b19f7568f..0c43622eb1c66 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -325,7 +325,15 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ .collect::, Ty<'tcx>)>>(); assert!(fx.caller_location.is_none()); - if fx.instance.def.requires_caller_location(fx.tcx) { + if let Some(coro_info) = fx.mir.coroutine.as_deref() + && let Some(captured_caller_location_idx) = coro_info.captured_caller_location + { + assert!( + !fx.instance.def.requires_caller_location(fx.tcx), + "should have only one source of truth for caller_location" + ); + fx.caller_location = Some(CallerLocation::Captured(captured_caller_location_idx)); + } else if fx.instance.def.requires_caller_location(fx.tcx) { // Store caller location for `#[track_caller]`. let arg_abi = arg_abis_iter.next().unwrap(); let param = cvalue_for_param(fx, None, None, arg_abi, &mut block_params_iter).unwrap(); @@ -333,7 +341,7 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ !param.is_underaligned_pointee, "caller location argument should not be underaligned", ); - fx.caller_location = Some(param.value); + fx.caller_location = Some(CallerLocation::Direct(param.value)); } assert_eq!(arg_abis_iter.next(), None, "ArgAbi left behind for {:?}", fx.fn_abi); @@ -557,7 +565,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( // Pass the caller location for `#[track_caller]`. if instance.is_some_and(|inst| inst.def.requires_caller_location(fx.tcx)) { - let caller_location = fx.get_caller_location(source_info); + let caller_location = fx.codegen_caller_location(source_info); args.push(CallArgument { value: caller_location, is_owned: false }); } @@ -811,7 +819,7 @@ pub(crate) fn codegen_drop<'tcx>( if drop_instance.def.requires_caller_location(fx.tcx) { // Pass the caller location for `#[track_caller]`. - let caller_location = fx.get_caller_location(source_info); + let caller_location = fx.codegen_caller_location(source_info); call_args.extend(adjust_arg_for_abi( fx, caller_location, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index e752189130290..747d70dd80f0c 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -391,7 +391,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { AssertKind::BoundsCheck { len, index } => { let len = codegen_operand(fx, len).load_scalar(fx); let index = codegen_operand(fx, index).load_scalar(fx); - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -404,7 +404,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { AssertKind::MisalignedPointerDereference { required, found } => { let required = codegen_operand(fx, required).load_scalar(fx); let found = codegen_operand(fx, found).load_scalar(fx); - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -415,7 +415,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ); } AssertKind::NullPointerDereference => { - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -426,7 +426,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ) } AssertKind::NullReferenceConstructed => { - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -438,7 +438,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { } AssertKind::InvalidEnumConstruction(source) => { let source = codegen_operand(fx, source).load_scalar(fx); - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -449,7 +449,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ) } _ => { - let location = fx.get_caller_location(source_info).load_scalar(fx); + let location = fx.codegen_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 30bd2f28af53f..b636a5aebb600 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -1,6 +1,7 @@ use cranelift_codegen::isa::TargetFrontendConfig; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; use rustc_abi::{Float, Integer, Primitive}; +use rustc_ast::Movability; use rustc_index::IndexVec; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::layout::{ @@ -288,8 +289,9 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) block_map: IndexVec, pub(crate) local_map: IndexVec>, - /// When `#[track_caller]` is used, the implicit caller location is stored in this variable. - pub(crate) caller_location: Option>, + /// Where to access the caller location (for the parent-most body if there's inlining) + /// when there's `#[track_caller]`. + pub(crate) caller_location: Option>, /// During cleanup the exception pointer will be stored in this variable. pub(crate) exception_slot: Variable, @@ -300,6 +302,16 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) inline_asm_index: u32, } +#[derive(Clone, Copy)] +pub(crate) enum CallerLocation<'tcx> { + /// Typical case: This function has `#[track_caller]`. + /// We track the caller location argument directly. + Direct(CValue<'tcx>), + /// Used if we're in a desugared coroutine inside a coroutine fn. + /// We track the field index inside `Self` that stores the caller location. + Captured(FieldIdx), +} + impl<'tcx> LayoutOfHelpers<'tcx> for FunctionCx<'_, '_, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { @@ -420,11 +432,42 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { } } - pub(crate) fn get_caller_location(&mut self, source_info: mir::SourceInfo) -> CValue<'tcx> { - self.mir.caller_location_span(source_info, self.caller_location, self.tcx, |span| { - let const_loc = self.tcx.span_as_caller_location(span); - crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty()) - }) + pub(crate) fn codegen_caller_location(&mut self, source_info: mir::SourceInfo) -> CValue<'tcx> { + let self_caller_location = self.caller_location; + let caller_location = self.mir.caller_location_span( + source_info, + || self_caller_location, + self.tcx, + |span| { + let const_loc = self.tcx.span_as_caller_location(span); + CallerLocation::Direct(crate::constant::codegen_const_value( + self, + const_loc, + self.tcx.caller_location_ty(), + )) + }, + ); + match caller_location { + CallerLocation::Direct(value) => value, + CallerLocation::Captured(idx) => { + let base = self.local_map[Local::arg(0)]; + // base is a `Pin<&mut Self>` or `&mut Self`, and we want the value of + // the `idx` field inside `Self`. + let is_pinned = self + .mir + .coroutine + .as_ref() + .expect("captured caller_location should only be in coroutines") + .coroutine_kind + .movability() + == Movability::Static; + let coro_ref = + if is_pinned { base.place_field(self, FieldIdx::from_usize(0)) } else { base }; // `&mut Self` + let coro_place = coro_ref.place_deref(self); // `Self` + let location_place = coro_place.place_field(self, idx); // `self.idx` + location_place.to_cvalue(self) + } + } } } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index cf1c1f027e7f5..a64bc2702d418 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -812,7 +812,7 @@ fn codegen_regular_intrinsic_call<'tcx>( sym::caller_location => { intrinsic_args!(fx, args => (); intrinsic); - let caller_location = fx.get_caller_location(source_info); + let caller_location = fx.codegen_caller_location(source_info); ret.write_cvalue(fx, caller_location); } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 8fa044a45de22..199892ae60bd7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -24,11 +24,10 @@ use tracing::{debug, info}; use super::operand::OperandRef; use super::operand::OperandValue::{self, Immediate, Pair, Ref, ZeroSized}; use super::place::{PlaceRef, PlaceValue}; -use super::{CachedLlbb, FunctionCx, LocalRef}; +use super::{CachedLlbb, CallerLocation, FunctionCx, IntrinsicResult, LocalRef}; use crate::base::{self, is_call_from_compiler_builtins_to_upstream_monomorphization}; use crate::common::{self, IntPredicate}; use crate::diagnostics::CompilerBuiltinsCannotCall; -use crate::mir::IntrinsicResult; use crate::traits::*; use crate::{MemFlags, meth}; @@ -795,7 +794,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(bx, terminator.source_info); // Get the location information. - let location = self.get_caller_location(bx, terminator.source_info).immediate(); + let location = self.codegen_caller_location(bx, terminator.source_info).immediate(); // Put together the arguments to the panic entry point. let (lang_item, args) = match msg { @@ -1441,7 +1440,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir_args + 1, "#[track_caller] fn's must have 1 more argument in their ABI than in their MIR: {instance:?} {fn_span:?} {fn_abi:?}", ); - let location = self.get_caller_location(bx, source_info); + let location = self.codegen_caller_location(bx, source_info); debug!( "codegen_call_terminator({:?}): location={:?} (fn_span {:?})", terminator, location, fn_span @@ -2178,15 +2177,47 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { tuple.layout.fields.count() } - pub(super) fn get_caller_location( + pub(super) fn codegen_caller_location( &mut self, bx: &mut Bx, source_info: mir::SourceInfo, ) -> OperandRef<'tcx, Bx::Value> { - self.mir.caller_location_span(source_info, self.caller_location, bx.tcx(), |span: Span| { - let const_loc = bx.tcx().span_as_caller_location(span); - OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty()) - }) + let caller_location = self.mir.caller_location_span( + source_info, + || self.caller_location, + bx.tcx(), + |span: Span| { + let const_loc = bx.tcx().span_as_caller_location(span); + // We don't inline `#[track_caller] async fn` into anything, + // so in this case, we don't need to load the caller location from + // an upvar in the coroutine. + CallerLocation::Direct(OperandRef::from_const( + bx, + const_loc, + bx.tcx().caller_location_ty(), + )) + }, + ); + match caller_location { + CallerLocation::Direct(operand_ref) => operand_ref, + CallerLocation::Captured(idx) => { + let base = self.codegen_consume(bx, mir::Local::arg(0).into()); + // base is a `Pin<&mut Self>` or `&mut Self`, and we want the value of + // the `idx` field inside `Self`. + let is_pinned = self + .mir + .coroutine + .as_ref() + .expect("captured caller_location should only be in coroutines") + .coroutine_kind + .movability() + == ast::Movability::Static; + let coro_ref = if is_pinned { base.extract_field(self, bx, 0) } else { base }; // `&mut Self` + let coro_place = coro_ref.deref(bx.cx()); // `Self` + let location_place = coro_place.project_field(bx, idx.as_usize()); // `self.idx` + bx.load_operand(location_place) + } + } } fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> { diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index e6b70ce2ca77b..096e66e367a38 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -158,7 +158,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } sym::caller_location => { - let location = self.get_caller_location(bx, source_info); + let location = self.codegen_caller_location(bx, source_info); location.val } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index aefa8356536dc..f8c9182aee205 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -1,5 +1,6 @@ use std::iter; +use rustc_abi::FieldIdx; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -125,8 +126,19 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// This is `None` if no variable debuginfo/names are needed. per_local_var_debug_info: Option>, - /// Caller location propagated if this function has `#[track_caller]`. - caller_location: Option>, + /// Where to access the caller location (for the parent-most body if there's inlining) + /// when there's `#[track_caller]`. + caller_location: Option>, +} + +#[derive(Clone, Copy)] +enum CallerLocation<'tcx, Value> { + /// Typical case: This function has `#[track_caller]`. + /// We track the caller location argument directly. + Direct(OperandRef<'tcx, Value>), + /// Used if we're in a desugared coroutine inside a coroutine fn. + /// We track the field index inside `Self` that stores the caller location. + Captured(FieldIdx), } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -634,7 +646,15 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx.cached_llbbs[mir::START_BLOCK] = CachedLlbb::Some(bx.llbb()); } - if fx.instance.def.requires_caller_location(bx.tcx()) { + if let Some(coro_info) = mir.coroutine.as_deref() + && let Some(captured_caller_location_idx) = coro_info.captured_caller_location + { + assert!( + !fx.instance.def.requires_caller_location(bx.tcx()), + "should have only one source of truth for caller_location" + ); + fx.caller_location = Some(CallerLocation::Captured(captured_caller_location_idx)); + } else if fx.instance.def.requires_caller_location(bx.tcx()) { let mir_args = if let Some(num_untupled) = num_untupled { // Subtract off the tupled argument that gets 'expanded' args.len() - 1 + num_untupled @@ -654,11 +674,11 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( _ => bug!("caller location must be PassMode::Direct, found {:?}", arg.mode), } - fx.caller_location = Some(OperandRef { + fx.caller_location = Some(CallerLocation::Direct(OperandRef { val: OperandValue::Immediate(bx.get_param(llarg_idx)), layout: arg.layout, move_annotation: None, - }); + })); } args diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 3f7731ce95583..e2426d847359b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -17,15 +17,15 @@ use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::{self, FieldInfo, ScalarInt, Ty, TyCtxt}; use rustc_middle::{mir, throw_machine_stop}; -use rustc_span::{Span, Symbol, bug, span_bug, sym}; +use rustc_span::{Symbol, bug, span_bug, sym}; use rustc_target::callconv::FnAbi; use tracing::debug; use super::error::*; use crate::diagnostics::{LongRunning, LongRunningWarn}; use crate::interpret::{ - self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, + self, AllocId, AllocInit, AllocRange, CallerLocation, ConstAllocation, CtfeProvenance, FnArg, + Frame, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, RetagMode, Scalar, compile_time_machine, ensure_monomorphic_enough, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_format, throw_unsup, throw_unsup_format, type_implements_dyn_trait, @@ -212,18 +212,41 @@ impl interpret::MayLeak for ! { } impl<'tcx> CompileTimeInterpCx<'tcx> { - fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { - let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); - let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); - - use rustc_span::RemapPathScopeComponents; - ( - Symbol::intern( - &caller.file.name.display(RemapPathScopeComponents::DIAGNOSTICS).to_string_lossy(), - ), - u32::try_from(caller.line).unwrap(), - u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(), - ) + fn caller_location_triple(&self) -> InterpResult<'tcx, (Symbol, u32, u32)> { + interp_ok(match self.caller_location()? { + CallerLocation::Direct(span) => { + let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); + let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); + + use rustc_span::RemapPathScopeComponents; + ( + Symbol::intern( + &caller + .file + .name + .display(RemapPathScopeComponents::DIAGNOSTICS) + .to_string_lossy(), + ), + u32::try_from(caller.line).unwrap(), + u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(), + ) + } + CallerLocation::Memory(place) => { + // `place` refers to a `Location`. Read its fields. + let filename = + Symbol::intern(self.read_str(&self.deref_pointer(&self.project_field( + &self.project_field(&place, FieldIdx::from_usize(0))?, + FieldIdx::from_usize(0), + )?)?)?); + let line = self + .read_scalar(&self.project_field(&place, FieldIdx::from_usize(1))?)? + .to_u32()?; + let col = self + .read_scalar(&self.project_field(&place, FieldIdx::from_usize(2))?)? + .to_u32()?; + (filename, line, col) + } + }) } /// "Intercept" a function call, because we have something special to do for it. @@ -253,8 +276,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { } let msg = Symbol::intern(self.read_str(&msg_place)?); - let span = self.find_closest_untracked_caller_location(); - let (file, line, col) = self.location_triple_for_span(span); + let (file, line, col) = self.caller_location_triple()?; return Err(ConstEvalErrKind::Panic { msg, file, line, col }).into(); } else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) { // For panic_fmt, call const_panic_fmt instead. @@ -463,8 +485,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> { let msg = Symbol::intern(msg); - let span = ecx.find_closest_untracked_caller_location(); - let (file, line, col) = ecx.location_triple_for_span(span); + let (file, line, col) = ecx.caller_location_triple()?; Err(ConstEvalErrKind::Panic { msg, file, line, col }).into() } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index c7615a216312e..99e811c663f05 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -10,7 +10,7 @@ use rustc_attr_ir::find_attr; use rustc_hir::def_id::DefId; use rustc_middle::mir; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; -use rustc_middle::ty::{self, AdtDef, FieldDef, Instance, Ty, VariantDef}; +use rustc_middle::ty::{self, AdtDef, FieldDef, Instance, InstanceKind, Ty, VariantDef}; use rustc_span::{bug, span_bug}; use rustc_target::callconv::{ArgAbi, FnAbi}; use tracing::field::Empty; @@ -456,12 +456,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, M::Provenance>], with_caller_location: bool, + callee_receives_caller_location: bool, destination: &PlaceTy<'tcx, M::Provenance>, mut cont: ReturnContinuation, ) -> InterpResult<'tcx> { let _trace = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty); let def_id = instance.def_id(); + assert!(!with_caller_location || callee_receives_caller_location); + let caller_location = + if callee_receives_caller_location { Some(self.caller_location()?) } else { None }; + // The first order of business is to figure out the callee signature. // However, that requires the list of variadic arguments. // We use the *caller* information to determine where to split the list of arguments, @@ -542,6 +547,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .collect::>() ); + self.frame_mut().track_caller_arg = caller_location; + // Determine whether there is a special VaList argument. This is always the // last argument, and since arguments start at index 1 that's `arg_count`. let va_list_arg = callee_fn_abi.c_variadic.then(|| mir::Local::from_usize(body.arg_count)); @@ -563,7 +570,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // The "where they come from" part is easy, we expect the caller to do any special handling // that might be required here (e.g. for untupling). // If `with_caller_location` is set we pretend there is an extra argument (that - // we will not pass; our `caller_location` intrinsic implementation walks the stack instead). + // we will not pass normally; our `caller_location` intrinsic implementation handles + // that separately). assert_eq!( args.len() + if with_caller_location { 1 } else { 0 }, caller_fn_abi.args.len(), @@ -847,12 +855,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Cow::from(args) }; + // Figure out if the callee receives a caller location argument. + // In the case where we are calling the fallback body of + // a `#[rustc_intrinsic] #[track_caller] fn`, we pretend that + // the caller doesn't pass a caller location argument (for checking ABI), + // but the caller receives a caller location argument from thin air anyway. + let callee_receives_caller_location = with_caller_location + || (instance.def.requires_caller_location(*self.tcx) + && matches!(instance.def, InstanceKind::Item(def_id) if self.tcx.intrinsic(def_id).is_some())); + self.init_stack_frame( instance, body, caller_fn_abi, &args, with_caller_location, + callee_receives_caller_location, destination, ReturnContinuation::Goto { ret: target, unwind }, ) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index a74b323c395e6..f5a9a3de6b615 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -2,7 +2,8 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; use either::{Left, Right}; -use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; +use rustc_abi::{Align, FieldIdx, HasDataLayout, Size, TargetDataLayout}; +use rustc_ast::ast::Movability; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_middle::mir; @@ -18,12 +19,12 @@ use rustc_middle::ty::{ use rustc_span::{Span, span_bug}; use rustc_structures::Limit; use rustc_target::callconv::FnAbi; -use tracing::{debug, trace}; +use tracing::trace; use super::{ - Frame, FrameInfo, GlobalId, InterpErrorKind, InterpResult, MPlaceTy, Machine, MemPlaceMeta, - Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval, interp_ok, - throw_inval, throw_ub, throw_ub_format, + CallerLocation, Frame, FrameInfo, GlobalId, InterpErrorKind, InterpResult, MPlaceTy, Machine, + MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, + err_inval, interp_ok, throw_inval, throw_ub, throw_ub_format, }; use crate::{enter_trace_span, util}; @@ -376,49 +377,90 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a - /// frame which is not `#[track_caller]`. This matches the `caller_location` intrinsic, + /// Grabs the implicit caller location argument, if there is one, + /// or falls back to the "current" span. This matches the `caller_location` intrinsic, /// and is primarily intended for the panic machinery. - pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { - for frame in self.stack().iter().rev() { - debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); - - // Assert that the frame we look at is actually executing code currently - // (`loc` is `Right` when we are unwinding and the frame does not require cleanup). - let loc = frame.loc.left().unwrap(); - - // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all - // (such as `box`). Use the normal span by default. - let mut source_info = *frame.body.source_info(loc); - - // If this is a `Call` terminator, use the `fn_span` instead. - let block = &frame.body.basic_blocks[loc.block]; - if loc.statement_index == block.statements.len() { - debug!( - "find_closest_untracked_caller_location: got terminator {:?} ({:?})", - block.terminator(), - block.terminator().kind, + pub(crate) fn caller_location( + &self, + ) -> InterpResult<'tcx, CallerLocation<'tcx, M::Provenance>> { + let frame = self.frame(); + + // Assert that the frame we look at is actually executing code currently + // (`loc` is `Right` when we are unwinding and the frame does not require cleanup). + let loc = frame.loc.left().unwrap(); + + assert_eq!( + frame.track_caller_arg.is_some(), + frame.instance.def.requires_caller_location(*self.tcx) + ); + + // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all + // (such as `box`). Use the normal span by default. + let mut source_info = *frame.body.source_info(loc); + + // If this is a `Call` terminator, use the `fn_span` instead. + let block = &frame.body.basic_blocks[loc.block]; + if loc.statement_index == block.statements.len() + && let mir::TerminatorKind::Call { fn_span, .. } = block.terminator().kind + { + source_info.span = fn_span; + } + + // The caller location of the top-level function, among the functions inlined + // into this frame. + // We need to compute this lazily, as this computation can cause UB for + // `#[track_caller] async fn`. Computing it eagerly would cause detection of UB + // in the case where a non-`#[track_caller]` function is inlined into a + // `#[track_caller] async fn`, the coroutine contains a dangling `&Location`, + // and the `&Location` was not used before inlining. + // (Note that it's fine to not use the `&Location` after inlining even if it's + // used before inlining. This just means that optimization causes us to miss UB.) + // Importantly, we don't actually dereference the `&Location` here, + // as this is not required to propagate it further as an implicit + // caller location argument. + let top_level_caller_location = || { + if let Some(coroutine) = &frame.body.coroutine + && let Some(field_idx) = coroutine.captured_caller_location + { + assert!( + !frame.instance.def.requires_caller_location(*self.tcx), + "should have only one source of truth for caller_location" ); - if let mir::TerminatorKind::Call { fn_span, .. } = block.terminator().kind { - source_info.span = fn_span; - } - } - let caller_location = if frame.instance.def.requires_caller_location(*self.tcx) { - // We use `Err(())` as indication that we should continue up the call stack since - // this is a `#[track_caller]` function. - Some(Err(())) + let is_pinned = coroutine.coroutine_kind.movability() == Movability::Static; + + Some(try { + // `Pin<&mut Self>` or `&mut Self` + let base_op = self.local_to_op(mir::Local::arg(0), None)?; + // `&mut Self` + let coro_ref_op = if is_pinned { + self.project_field(&base_op, FieldIdx::from_usize(0))? + } else { + base_op + }; + // `Self` + let coro_place = self.deref_pointer(&coro_ref_op)?; + // `&Location` + let location_ref_place = self.project_field(&coro_place, field_idx)?; + // `Location` + let location_place = self.deref_pointer(&location_ref_place)?; + CallerLocation::Memory(location_place) + }) + } else if frame.instance.def.requires_caller_location(*self.tcx) { + Some(interp_ok( + frame + .track_caller_arg + .clone() + .expect("should have received track_caller argument"), + )) } else { None - }; - if let Ok(span) = - frame.body.caller_location_span(source_info, caller_location, *self.tcx, Ok) - { - return span; } - } + }; - span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found") + frame.body.caller_location_span(source_info, top_level_caller_location, *self.tcx, |span| { + interp_ok(CallerLocation::Direct(span)) + }) } /// Returns the actual dynamic size and alignment of the place at the given type. diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index b8b372ad1dab1..05bd56f9a7031 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -25,7 +25,7 @@ use super::{ PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance, Scalar, err_ub_format, err_unsup_format, interp_ok, throw_inval, throw_ub, throw_ub_format, }; -use crate::interpret::{MPlaceTy, Writeable}; +use crate::interpret::{CallerLocation, MPlaceTy, Writeable}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum MulAddType { @@ -293,10 +293,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } sym::caller_location => { - let span = self.find_closest_untracked_caller_location(); - let val = self.tcx.span_as_caller_location(span); - let val = - self.const_val_to_op(val, self.tcx.caller_location_ty(), Some(dest.layout))?; + let val = match self.caller_location()? { + CallerLocation::Direct(span) => self.const_val_to_op( + self.tcx.span_as_caller_location(span), + self.tcx.caller_location_ty(), + Some(dest.layout), + )?, + CallerLocation::Memory(place) => self + .mplace_to_imm_ptr( + &place, + Some( + self.tcx.erase_and_anonymize_regions(self.tcx.caller_location_ty()), + ), + )? + .into(), + }; self.copy_op(&val, dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 1eceff75e092b..e833ff662293e 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -40,6 +40,7 @@ pub use self::operator::AtomicRmwOp; pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable}; use self::place::{MemPlace, Place}; pub use self::projection::{OffsetMode, Projectable}; +pub(crate) use self::stack::CallerLocation; pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation}; pub use self::util::EnteredTraceSpan; pub(crate) use self::util::{ diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 8a731e25d8f4b..91a84b561c27e 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -96,6 +96,9 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { /// frame is popped. pub(super) va_list: Vec>, + /// The implicit caller location argument that is passed by this function's caller, if any. + pub(super) track_caller_arg: Option>, + /// The span of the `tracing` crate is stored here. /// When the guard is dropped, the span is exited. This gives us /// a full stack trace on all tracing statements. @@ -112,6 +115,18 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { pub(super) loc: Either, } +/// Information necessary to figure out the caller_location +#[derive(Clone)] +pub(crate) enum CallerLocation<'tcx, Prov: Provenance> { + /// Simple case: This span was created when we called a `#[track_caller]` frame + /// (other than an async fn) from a normal frame. We know the span from just inspecting + /// the stack at the point of this call. + Direct(Span), + /// A place containing a `std::panic::Location`. This is used for `#[track_caller] async fn`, + /// which captures a `&std::panic::Location` in the coroutine. + Memory(MPlaceTy<'tcx, Prov>), +} + /// Where and how to continue when returning/unwinding from the current function. #[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these pub enum ReturnContinuation { @@ -255,13 +270,14 @@ impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> { Frame { body: self.body, instance: self.instance, + extra, return_cont: self.return_cont, return_place: self.return_place, locals: self.locals, va_list: self.va_list, - loc: self.loc, - extra, + track_caller_arg: self.track_caller_arg, tracing_span: self.tracing_span, + loc: self.loc, } } } @@ -397,6 +413,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return_place: return_place.clone(), locals, va_list: vec![], + track_caller_arg: None, instance, tracing_span: SpanGuard::new(), extra: (), diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 4efef90581080..416207371e51c 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -25,7 +25,7 @@ pub(crate) fn type_implements_dyn_trait<'tcx, M: Machine<'tcx>>( let ty::Dynamic(preds, _) = trait_ty.kind() else { span_bug!( - ecx.find_closest_untracked_caller_location(), + ecx.cur_span(), "Invalid type provided to type_implements_predicates. U must be dyn Trait, got {trait_ty}." ); }; diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 981813f13c97f..ccf88b6df765c 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -4,6 +4,7 @@ #![feature(deref_patterns)] #![feature(slice_ptr_get)] #![feature(trait_alias)] +#![feature(try_blocks)] #![feature(unqualified_local_imports)] #![feature(yeet_expr)] #![warn(unqualified_local_imports)] diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 4f4ed83be06a3..33ac0e2e15ff8 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1453,6 +1453,12 @@ pub struct Closure<'hir> { pub fn_arg_span: Option, pub kind: ClosureKind, pub explicit_captures: &'hir [ExplicitCapture], + /// If this is a coroutine that was desugared from a coroutine fn that's annotated + /// with `#[track_caller]`, then we track the binding that contains the caller location + /// outside the coroutine. + /// + /// FIXME(closure_track_caller): Also do this for coroutine closures + pub captured_caller_location: Option, } /// A HIR local that must be captured by value even if ordinary closure capture diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index ad96fe492e6e7..e79dd042e2022 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -913,6 +913,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) kind: _, constness: _, explicit_captures: _, + captured_caller_location: _, }) => { walk_list!(visitor, visit_generic_param, bound_generic_params); try_visit!(visitor.visit_fn(FnKind::Closure, fn_decl, body, *span, def_id)); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index d8c57bfbdb56b..9329c64d18807 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1680,6 +1680,7 @@ impl<'a> State<'a> { kind: _, def_id: _, explicit_captures: _, + captured_caller_location: _, }) => { self.print_closure_binder(binder, bound_generic_params); self.print_constness(constness); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 19a6d2a31c4e3..e69dce03f621e 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -165,6 +165,13 @@ pub struct CoroutineInfo<'tcx> { /// If this is a coroutine then record the type of source expression that caused this coroutine /// to be created. pub coroutine_kind: CoroutineKind, + + /// If this is a desugared coroutine that was desugared from a coroutine fn + /// that's annotated with `#[track_caller]`, then we record the field in the coroutine + /// that we can read the caller's `Location`. + /// + /// FIXME(closure_track_caller): Also use this for coroutine closures + pub captured_caller_location: Option, } impl<'tcx> CoroutineInfo<'tcx> { @@ -182,6 +189,7 @@ impl<'tcx> CoroutineInfo<'tcx> { coroutine_drop_async: None, coroutine_drop_proxy_async: None, coroutine_layout: None, + captured_caller_location: None, } } } @@ -682,12 +690,20 @@ impl<'tcx> Body<'tcx> { /// For a `Location` in this scope, determine what the "caller location" at that point is. This /// is interesting because of inlining: the `#[track_caller]` attribute of inlined functions - /// must be honored. Falls back to the `tracked_caller` value for `#[track_caller]` functions, - /// or the function's scope. + /// must be honored. + /// + /// Callers may pass `Some` in the `caller_location` argument to provide a different + /// return value to be used instead of `from_span(span_of_topmost_source)`. This is useful + /// if this `Body` is itself `#[track_caller]` + /// + /// This function does not handle the behavior of coroutines desugared from + /// `#[track_caller] async fn`, treating it as a non-`#[track_caller]` function. + /// Callers are responsible for handling this case, although we currently do not + /// inline such coroutines into anything. pub fn caller_location_span( &self, mut source_info: SourceInfo, - caller_location: Option, + caller_location: impl FnOnce() -> Option, tcx: TyCtxt<'tcx>, from_span: impl FnOnce(Span) -> T, ) -> T { @@ -711,7 +727,7 @@ impl<'tcx> Body<'tcx> { } // No inlined `SourceScope`s, or all of them were `#[track_caller]`. - caller_location.unwrap_or_else(|| from_span(source_info.span)) + caller_location().unwrap_or_else(|| from_span(source_info.span)) } #[track_caller] diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 1a00293f8ffc6..66941ebedc30d 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -303,6 +303,18 @@ impl<'tcx> InstanceKind<'tcx> { } } + /// Checks if calling `self` requires passing an implicit caller location argument. + /// + /// Note that this returns `false` on the coroutine of a `#[track_caller] async fn`, + /// since calling the coroutine doesn't require any extra arguments, even if + /// the coroutine does "override" the caller location. + /// + /// In Miri, when reporting the stack trace of UB or other errors, + /// frames where this function returns true are skipped by default, + /// and `#[track_caller]` on an `async fn` is ignored for the generated coroutine. + /// This is because resolving the caller (as opposed to the poller) of a coroutine + /// requires reading the memory of the running program, and this memory can potentially + /// be corrupted by misbehaving user code. pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool { match *self { InstanceKind::Item(def_id) diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 93114b59ed647..a3c8a99ee2f06 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -885,6 +885,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => return, }; + let captured_caller_location = + self.tcx.hir_node_by_def_id(self.def_id).expect_closure().captured_caller_location; + // In analyze_closure() in upvar.rs we gathered a list of upvars used by an // indexed closure and we stored in a map called closure_min_captures in TypeckResults // with the closure's DefId. Here, we run through that vec of UpvarIds for @@ -914,7 +917,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mutability = captured_place.mutability; let mut projs = closure_env_projs.clone(); - projs.push(ProjectionElem::Field(FieldIdx::new(i), ty)); + let field_idx = FieldIdx::new(i); + projs.push(ProjectionElem::Field(field_idx, ty)); match capture { ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {} ty::UpvarCapture::ByRef(..) => { @@ -934,6 +938,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { argument_index: None, }); + if captured_caller_location + .is_some_and(|hir_id| captured_place.get_root_variable() == hir_id) + { + let coroutine_info = self + .coroutine + .as_deref_mut() + .expect("caller location should only be captured in a coroutine"); + assert!( + coroutine_info.captured_caller_location.is_none(), + "should only have one captured_caller_location" + ); + coroutine_info.captured_caller_location = Some(field_idx); + } + let capture = Capture { captured_place, use_place, mutability }; (var_id, capture) }) diff --git a/compiler/rustc_mir_transform/src/check_inline.rs b/compiler/rustc_mir_transform/src/check_inline.rs index bea0d5c81eb2f..4c1df8353ec9b 100644 --- a/compiler/rustc_mir_transform/src/check_inline.rs +++ b/compiler/rustc_mir_transform/src/check_inline.rs @@ -73,6 +73,17 @@ pub(super) fn is_inline_valid_on_body<'tcx>( _: TyCtxt<'tcx>, body: &Body<'tcx>, ) -> Result<(), &'static str> { + // We currently do not support inlining a callee which is the coroutine + // desugared from `#[track_caller] async fn`. + // This is because figuring out the caller_location of such coroutines + // requires accessing the argument of the `poll()` call. And inlining + // would cause us to lose track of where that argument is. + if let Some(coroutine) = &body.coroutine + && coroutine.captured_caller_location.is_some() + { + return Err("can't inline coroutines from `#[track_caller] async fn`"); + } + if body .basic_blocks .iter() diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index a081bd1b65e92..22a4d120e0641 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1240,6 +1240,8 @@ pub enum DesugaringKind { source: bool, }, RangeExpr, + // `#[track_caller]` applied to a coroutine fn or coroutine closure + CoroutineFnTrackCaller, } impl DesugaringKind { @@ -1262,6 +1264,9 @@ impl DesugaringKind { "expression that expanded into a format string literal" } DesugaringKind::RangeExpr => "range expression", + DesugaringKind::CoroutineFnTrackCaller => { + "`#[track_caller]` on coroutine fn or coroutine closure" + } } } @@ -1282,6 +1287,7 @@ impl DesugaringKind { DesugaringKind::PatTyRange => value == "PatTyRange", DesugaringKind::FormatLiteral { .. } => value == "FormatLiteral", DesugaringKind::RangeExpr => value == "RangeExpr", + DesugaringKind::CoroutineFnTrackCaller => value == "CoroutineFnTrackCaller", } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a003f20e3512d..252f3fa31ee50 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -370,6 +370,7 @@ symbols! { __H, __S, __awaitee, + __captured_caller_location, __ensures_checker, __ret, __try_var, @@ -753,6 +754,7 @@ symbols! { copysignf64, copysignf128, core, + core_intrinsics, core_panic, core_panic_2015_macro, core_panic_macro, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index f44da9840b8e5..93d1e799c5f98 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -612,6 +612,7 @@ pub const fn assert_mem_uninitialized_valid(); #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] +#[lang = "caller_location"] pub const fn caller_location() -> &'static crate::panic::Location<'static>; /// Moves a value out of scope without running drop glue. diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index b0ff200c0614b..fbbf99c7f3d87 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -429,6 +429,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; + let callee_receives_caller_location = f.def.requires_caller_location(*this.tcx); // This will also show proper errors if there is any ABI mismatch. this.init_stack_frame( f, @@ -436,6 +437,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { caller_fn_abi, &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::>(), /*with_caller_location*/ false, + callee_receives_caller_location, &dest.into(), cont, ) diff --git a/src/tools/miri/tests/pass/async-panic-track-caller.rs b/src/tools/miri/tests/pass/async-panic-track-caller.rs index 062e07cffc8b9..ef346594499a8 100644 --- a/src/tools/miri/tests/pass/async-panic-track-caller.rs +++ b/src/tools/miri/tests/pass/async-panic-track-caller.rs @@ -1,20 +1,26 @@ // This test is duplicated (with changes) at // tests/ui/async-await/track-caller/panic-track-caller.rs -//@ edition:2021 -//@ revisions: afn cls afn_cls nofeat -//@ run-native +//@ edition:2024 +//@ revisions: nofeat afn cls afn_cls +// +// +// // // // // Padding comment so that the line numbers are the same as panic-track-caller.rs -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, coroutines, coroutine_trait, gen_blocks)] #![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] #![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] #![allow(unused)] use std::future::Future; -use std::panic; +use std::ops::Coroutine; +use std::panic::{self, Location}; +use std::pin::pin; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering::Relaxed; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll, Wake}; use std::thread::{self, Thread}; @@ -47,7 +53,10 @@ fn block_on(fut: impl Future) -> T { } } +static LINE: AtomicU32 = AtomicU32::new(0); + async fn bar() { + LINE.store(Location::caller().line(), Relaxed); panic!() } @@ -59,6 +68,7 @@ async fn foo() { #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] #[track_caller] async fn bar_track_caller() { + LINE.store(Location::caller().line(), Relaxed); panic!() } @@ -73,6 +83,7 @@ impl Foo { #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] #[track_caller] async fn bar_assoc() { + LINE.store(Location::caller().line(), Relaxed); panic!(); } } @@ -88,6 +99,7 @@ async fn foo_assoc() { async fn foo_closure() { let closure = #[track_caller] async || { + LINE.store(Location::caller().line(), Relaxed); panic!(); }; let future = closure(); @@ -100,6 +112,7 @@ async fn foo_closure() { async fn foo_block() { let future = #[track_caller] async { + LINE.store(Location::caller().line(), Relaxed); panic!(); }; future.await; @@ -108,6 +121,7 @@ async fn foo_block() { #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] #[track_caller] async fn bar_manual_poll() { + LINE.store(Location::caller().line(), Relaxed); panic!(); } @@ -119,6 +133,118 @@ fn foo_manual_poll() { assert_eq!(res, std::task::Poll::Ready(())); } +trait Trait { + async fn bar_trait_attr_nowhere(); + #[track_caller] + async fn bar_trait_attr_in_trait(); + async fn bar_trait_attr_in_impl(); + #[track_caller] + async fn bar_trait_attr_in_both(); + + #[track_caller] + fn bar_rpit_in_trait() -> impl Future; + #[track_caller] + async fn bar_rpit_in_impl(); +} +impl Trait for Foo { + async fn bar_trait_attr_nowhere() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + async fn bar_trait_attr_in_trait() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] + #[track_caller] + async fn bar_trait_attr_in_impl() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] + #[track_caller] + async fn bar_trait_attr_in_both() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + + async fn bar_rpit_in_trait() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + fn bar_rpit_in_impl() -> impl Future { + async { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + } +} + +async fn foo_trait_attr_nowhere() { + let future = Foo::bar_trait_attr_nowhere(); + future.await; +} +async fn foo_trait_attr_in_trait() { + let future = Foo::bar_trait_attr_in_trait(); + future.await; +} +async fn foo_trait_attr_in_impl() { + let future = Foo::bar_trait_attr_in_impl(); + future.await; +} +async fn foo_trait_attr_in_both() { + let future = Foo::bar_trait_attr_in_both(); + future.await; +} + +async fn foo_rpit_in_trait() { + let future = Foo::bar_rpit_in_trait(); + future.await; +} +async fn foo_rpit_in_impl() { + let future = Foo::bar_rpit_in_impl(); + future.await; +} + +#[track_caller] +gen fn bar_gen_fn() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); +} + +fn foo_gen_fn() { + let mut iter = bar_gen_fn(); + let _ = iter.next(); +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] +fn foo_gen_block() { + let mut iter = #[track_caller] + gen { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + yield (); + }; + let _ = iter.next(); +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] +fn foo_coroutine() { + let coro = #[track_caller] + #[coroutine] + || { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + yield (); + }; + let coro = std::pin::pin!(coro); + let _ = coro.resume(()); +} + fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { let loc = Arc::new(Mutex::new(None)); @@ -135,36 +261,119 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { x } -// FIXME(async_fn_track_caller): Currently, #[track_caller] on an async function -// uses the location where the future is awaited or polled. -// The correct behavior as per T-lang is to use the location where the function is called. fn main() { - assert_eq!(panicked_at(|| block_on(foo())), 51); + assert_eq!(panicked_at(|| block_on(foo())), 60); + assert_eq!(LINE.load(Relaxed), 59); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 67); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 76); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 76); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 72); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 62); + assert_eq!(LINE.load(Relaxed), 71); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 82); + assert_eq!(panicked_at(|| block_on(foo_assoc())), 92); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 92); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 87); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); + assert_eq!(LINE.load(Relaxed), 86); + // FIXME(closure_track_caller): Currently, #[track_caller] on an async closure + // uses the location where the future is awaited or polled. + // It should be changed to use the location where the closure is called, + // so the behavior matches that of `async fn`. + #[cfg(afn_cls)] + assert_eq!(panicked_at(|| block_on(foo_closure())), 106); + #[cfg(afn_cls)] + assert_eq!(LINE.load(Relaxed), 106); // FIXME(closure_track_caller): if closure_track_caller is enabled, but // async_fn_track_caller is disabled, then #[track_caller] on async closures // silently do nothing. Either it should function, or we should emit a warning. // See #161961 #[cfg(cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 91); - #[cfg(afn_cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 94); + assert_eq!(panicked_at(|| block_on(foo_closure())), 103); + #[cfg(cls)] + assert_eq!(LINE.load(Relaxed), 102); #[cfg(any(cls, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_block())), 105); + assert_eq!(panicked_at(|| block_on(foo_block())), 118); + #[cfg(any(cls, afn_cls))] + assert_eq!(LINE.load(Relaxed), 118); + + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| foo_manual_poll()), 129); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 129); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| foo_manual_poll()), 125); + #[cfg(any(cls, nofeat))] + assert_eq!(LINE.load(Relaxed), 124); + + assert_eq!(panicked_at(|| block_on(foo_trait_attr_nowhere())), 152); + assert_eq!(LINE.load(Relaxed), 151); + + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_trait())), 188); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 188); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_trait())), 156); + #[cfg(any(cls, nofeat))] + assert_eq!(LINE.load(Relaxed), 155); + + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_impl())), 192); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 192); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_impl())), 162); + #[cfg(any(cls, nofeat))] + assert_eq!(LINE.load(Relaxed), 161); + + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_both())), 196); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 196); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_both())), 168); + #[cfg(any(cls, nofeat))] + assert_eq!(LINE.load(Relaxed), 167); + + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| block_on(foo_rpit_in_trait())), 201); + #[cfg(any(afn, afn_cls))] + assert_eq!(LINE.load(Relaxed), 201); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_rpit_in_trait())), 173); + #[cfg(any(cls, nofeat))] + assert_eq!(LINE.load(Relaxed), 172); + assert_eq!(panicked_at(|| block_on(foo_rpit_in_impl())), 178); + assert_eq!(LINE.load(Relaxed), 177); + + // FIXME(gen_blocks): Decide if this behavior is correct. + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| foo_gen_fn()), 216); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| foo_manual_poll()), 118); + assert_eq!(LINE.load(Relaxed), 216); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| foo_gen_fn()), 212); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| foo_manual_poll()), 111); + assert_eq!(LINE.load(Relaxed), 211); + + #[cfg(any(cls, afn_cls))] + assert_eq!(panicked_at(|| foo_gen_block()), 230); + #[cfg(any(cls, afn_cls))] + assert_eq!(LINE.load(Relaxed), 230); + + // FIXME(coroutines): This behavior is inconsistent with async blocks. + #[cfg(any(cls, afn_cls))] + assert_eq!(panicked_at(|| foo_coroutine()), 245); + #[cfg(any(cls, afn_cls))] + assert_eq!(LINE.load(Relaxed), 245); } diff --git a/tests/ui-fulldeps/rustc_public/check_track_caller.rs b/tests/ui-fulldeps/rustc_public/check_track_caller.rs index a9d396a479fa5..d7c3a7f11704a 100644 --- a/tests/ui-fulldeps/rustc_public/check_track_caller.rs +++ b/tests/ui-fulldeps/rustc_public/check_track_caller.rs @@ -105,6 +105,8 @@ fn test_caller_location_propagation(items: &[rustc_public::CrateItem]) { } /// Find the first call to a `#[track_caller]` function in the body and resolve its location. +/// +/// FIXME(async_fn_track_caller): What if the Location is stored inside a coroutine upvar? fn resolve_tracked_call_location(body: &Body, inherited: Option) -> MirConst { for bb in &body.blocks { if let TerminatorKind::Call { func, .. } = &bb.terminator.kind { diff --git a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr index fb774290bde4e..1f913f0426235 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr @@ -1,5 +1,5 @@ error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:11:13 + --> $DIR/async-closure-gate.rs:12:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:19:13 + --> $DIR/async-closure-gate.rs:20:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:25:13 + --> $DIR/async-closure-gate.rs:26:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:32:17 + --> $DIR/async-closure-gate.rs:33:17 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:40:9 + --> $DIR/async-closure-gate.rs:41:9 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:49:13 + --> $DIR/async-closure-gate.rs:50:13 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -58,6 +58,26 @@ LL | #[track_caller] = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 6 previous errors +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:62:13 + | +LL | let _ = #[track_caller] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:70:13 + | +LL | let _ = #[track_caller] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr b/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr index 5fe7a2fb7d36a..cf7af1c34da02 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr @@ -1,5 +1,5 @@ error: `#[track_caller]` on async functions is a no-op - --> $DIR/async-closure-gate.rs:16:1 + --> $DIR/async-closure-gate.rs:17:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | | } = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date note: the lint level is defined here - --> $DIR/async-closure-gate.rs:6:9 + --> $DIR/async-closure-gate.rs:7:9 | LL | #![deny(ungated_async_fn_track_caller)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr index fb774290bde4e..1f913f0426235 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr @@ -1,5 +1,5 @@ error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:11:13 + --> $DIR/async-closure-gate.rs:12:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:19:13 + --> $DIR/async-closure-gate.rs:20:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:25:13 + --> $DIR/async-closure-gate.rs:26:13 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:32:17 + --> $DIR/async-closure-gate.rs:33:17 | LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | let _ = #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:40:9 + --> $DIR/async-closure-gate.rs:41:9 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | #[track_caller] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:49:13 + --> $DIR/async-closure-gate.rs:50:13 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -58,6 +58,26 @@ LL | #[track_caller] = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 6 previous errors +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:62:13 + | +LL | let _ = #[track_caller] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:70:13 + | +LL | let _ = #[track_caller] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.rs b/tests/ui/async-await/track-caller/async-closure-gate.rs index 13d3a5787ca9d..a35c38249ef2d 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.rs +++ b/tests/ui/async-await/track-caller/async-closure-gate.rs @@ -1,8 +1,9 @@ -//@ edition:2021 +//@ edition:2024 //@ revisions: afn cls afn_cls nofeat //@[afn_cls] check-pass -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, coroutines, gen_blocks)] +#![allow(incomplete_features)] #![deny(ungated_async_fn_track_caller)] #![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] #![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] @@ -52,3 +53,24 @@ fn foo5() { }; }; } + +// FIXME(gen_blocks): #[track_caller] is apparently not properly linted here? +#[track_caller] +gen fn foo6() {} + +fn foo7() { + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + gen { + yield (); + }; +} + +fn foo8() { + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + #[coroutine] + || { + yield (); + }; +} diff --git a/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.cls.stderr b/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.cls.stderr new file mode 100644 index 0000000000000..24d2cf644b025 --- /dev/null +++ b/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.cls.stderr @@ -0,0 +1,83 @@ +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:68:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_track_caller() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!() +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(ungated_async_fn_track_caller)]` on by default + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:83:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:121:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:158:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:164:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted + diff --git a/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.cls_opt.stderr b/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.cls_opt.stderr new file mode 100644 index 0000000000000..24d2cf644b025 --- /dev/null +++ b/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.cls_opt.stderr @@ -0,0 +1,83 @@ +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:68:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_track_caller() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!() +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(ungated_async_fn_track_caller)]` on by default + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:83:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:121:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:158:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:164:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted + diff --git a/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.nofeat.stderr b/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.nofeat.stderr new file mode 100644 index 0000000000000..24d2cf644b025 --- /dev/null +++ b/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.nofeat.stderr @@ -0,0 +1,83 @@ +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:68:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_track_caller() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!() +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(ungated_async_fn_track_caller)]` on by default + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:83:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:121:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:158:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:164:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted + diff --git a/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.nofeat_opt.stderr b/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.nofeat_opt.stderr new file mode 100644 index 0000000000000..24d2cf644b025 --- /dev/null +++ b/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.nofeat_opt.stderr @@ -0,0 +1,83 @@ +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:68:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_track_caller() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!() +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(ungated_async_fn_track_caller)]` on by default + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:83:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:121:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:158:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller-no-unwind.rs:164:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | // panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted + diff --git a/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.rs b/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.rs new file mode 100644 index 0000000000000..a0190d59e4cb4 --- /dev/null +++ b/tests/ui/async-await/track-caller/panic-track-caller-no-unwind.rs @@ -0,0 +1,339 @@ +// This test is duplicated (with changes) at +// src/tools/miri/tests/pass/async-panic-track-caller.rs + +//@ run-pass +//@ edition:2024 +//@ revisions: nofeat afn cls afn_cls nofeat_opt afn_opt cls_opt afn_cls_opt +//@[nofeat_opt] compile-flags: -O +//@[afn_opt] compile-flags: -O +//@[cls_opt] compile-flags: -O +//@[afn_cls_opt] compile-flags: -O +// +// gate-test-async_fn_track_caller +#![feature(stmt_expr_attributes, coroutines, coroutine_trait, gen_blocks)] +#![cfg_attr(any(afn, afn_cls, afn_opt, afn_cls_opt), feature(async_fn_track_caller))] +#![cfg_attr(any(cls, afn_cls, cls_opt, afn_cls_opt), feature(closure_track_caller))] +#![allow(unused)] + +use std::future::Future; +use std::ops::Coroutine; +use std::panic::{self, Location}; +use std::pin::pin; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering::Relaxed; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll, Wake}; +use std::thread::{self, Thread}; + +/// A waker that wakes up the current thread when called. +struct ThreadWaker(Thread); + +impl Wake for ThreadWaker { + fn wake(self: Arc) { + self.0.unpark(); + } +} + +/// Run a future to completion on the current thread. +fn block_on(fut: impl Future) -> T { + // Pin the future so it can be polled. + let mut fut = Box::pin(fut); + + // Create a new context to be passed to the future. + let t = thread::current(); + let waker = Arc::new(ThreadWaker(t)).into(); + let mut cx = Context::from_waker(&waker); + + // Run the future to completion. + loop { + match fut.as_mut().poll(&mut cx) { + Poll::Ready(res) => return res, + Poll::Pending => thread::park(), + } + } +} + +static LINE: AtomicU32 = AtomicU32::new(0); + +async fn bar() { + LINE.store(Location::caller().line(), Relaxed); + // panic!() +} + +async fn foo() { + let future = bar(); + future.await; +} + +#[track_caller] +//[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op +async fn bar_track_caller() { + LINE.store(Location::caller().line(), Relaxed); + // panic!() +} + +async fn foo_track_caller() { + let future = bar_track_caller(); + future.await; +} + +struct Foo; + +impl Foo { + #[track_caller] + //[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op + async fn bar_assoc() { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + } +} + +async fn foo_assoc() { + let future = Foo::bar_assoc(); + future.await; +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] +async fn foo_closure() { + let closure = #[track_caller] + async || { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + }; + let future = closure(); + future.await; +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] +async fn foo_block() { + let future = #[track_caller] + async { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + }; + future.await; +} + +#[track_caller] +//[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op +async fn bar_manual_poll() { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); +} + +fn foo_manual_poll() { + let future = bar_manual_poll(); + let future = std::pin::pin!(future); + let mut cx = std::task::Context::from_waker(std::task::Waker::noop()); + let res = future.poll(&mut cx); + assert_eq!(res, std::task::Poll::Ready(())); +} + +trait Trait { + async fn bar_trait_attr_nowhere(); + #[track_caller] + async fn bar_trait_attr_in_trait(); + async fn bar_trait_attr_in_impl(); + #[track_caller] + async fn bar_trait_attr_in_both(); + + #[track_caller] + fn bar_rpit_in_trait() -> impl Future; + #[track_caller] + async fn bar_rpit_in_impl(); +} +impl Trait for Foo { + async fn bar_trait_attr_nowhere() { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + } + async fn bar_trait_attr_in_trait() { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + } + #[track_caller] + //[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op + async fn bar_trait_attr_in_impl() { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + } + #[track_caller] + //[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op + async fn bar_trait_attr_in_both() { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + } + + async fn bar_rpit_in_trait() { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + } + fn bar_rpit_in_impl() -> impl Future { + async { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + } + } +} + +async fn foo_trait_attr_nowhere() { + let future = Foo::bar_trait_attr_nowhere(); + future.await; +} +async fn foo_trait_attr_in_trait() { + let future = Foo::bar_trait_attr_in_trait(); + future.await; +} +async fn foo_trait_attr_in_impl() { + let future = Foo::bar_trait_attr_in_impl(); + future.await; +} +async fn foo_trait_attr_in_both() { + let future = Foo::bar_trait_attr_in_both(); + future.await; +} + +async fn foo_rpit_in_trait() { + let future = Foo::bar_rpit_in_trait(); + future.await; +} +async fn foo_rpit_in_impl() { + let future = Foo::bar_rpit_in_impl(); + future.await; +} + +#[track_caller] +gen fn bar_gen_fn() { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); +} + +fn foo_gen_fn() { + let mut iter = bar_gen_fn(); + let _ = iter.next(); +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] +fn foo_gen_block() { + let mut iter = #[track_caller] + gen { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + yield (); + }; + let _ = iter.next(); +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] +fn foo_coroutine() { + let coro = #[track_caller] + #[coroutine] + || { + LINE.store(Location::caller().line(), Relaxed); + // panic!(); + yield (); + }; + let coro = std::pin::pin!(coro); + let _ = coro.resume(()); +} + +fn main() { + block_on(foo()); + assert_eq!(LINE.load(Relaxed), 59); + + block_on(foo_track_caller()); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 76); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 71); + + block_on(foo_assoc()); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 92); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 86); + + // FIXME(closure_track_caller): Currently, #[track_caller] on an async closure + // uses the location where the future is awaited or polled. + // It should be changed to use the location where the closure is called, + // so the behavior matches that of `async fn`. + #[cfg(any(afn_cls, afn_cls_opt))] + block_on(foo_closure()); + #[cfg(any(afn_cls, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 106); + // FIXME(closure_track_caller): if closure_track_caller is enabled, but + // async_fn_track_caller is disabled, then #[track_caller] on async closures + // silently do nothing. Either it should function, or we should emit a warning. + // See #161961 + #[cfg(any(cls, cls_opt))] + block_on(foo_closure()); + #[cfg(any(cls, cls_opt))] + assert_eq!(LINE.load(Relaxed), 102); + + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + block_on(foo_block()); + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 118); + + foo_manual_poll(); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 129); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 124); + + block_on(foo_trait_attr_nowhere()); + assert_eq!(LINE.load(Relaxed), 151); + + block_on(foo_trait_attr_in_trait()); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 188); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 155); + + block_on(foo_trait_attr_in_impl()); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 192); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 161); + + block_on(foo_trait_attr_in_both()); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 196); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 167); + + block_on(foo_rpit_in_trait()); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 201); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 172); + + block_on(foo_rpit_in_impl()); + assert_eq!(LINE.load(Relaxed), 177); + + // FIXME(gen_blocks): Decide if this behavior is correct. + foo_gen_fn(); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 216); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 211); + + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + foo_gen_block(); + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 230); + + // FIXME(coroutines): This behavior is inconsistent with async blocks. + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + foo_coroutine(); + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 245); +} diff --git a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr index 5611e53f50a40..bd56982b0582c 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr @@ -1,10 +1,11 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:59:1 + --> $DIR/panic-track-caller.rs:68:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_track_caller() { +LL | | LINE.store(Location::caller().line(), Relaxed); LL | | panic!() LL | | } | |_- this function will not propagate the caller location @@ -15,12 +16,13 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:73:5 + --> $DIR/panic-track-caller.rs:83:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); LL | | panic!(); LL | | } | |_____- this function will not propagate the caller location @@ -30,12 +32,13 @@ LL | | } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:108:1 + --> $DIR/panic-track-caller.rs:121:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); LL | | panic!(); LL | | } | |_- this function will not propagate the caller location @@ -44,5 +47,37 @@ LL | | } = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: 3 warnings emitted +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:158:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:164:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted diff --git a/tests/ui/async-await/track-caller/panic-track-caller.cls_opt.stderr b/tests/ui/async-await/track-caller/panic-track-caller.cls_opt.stderr new file mode 100644 index 0000000000000..bd56982b0582c --- /dev/null +++ b/tests/ui/async-await/track-caller/panic-track-caller.cls_opt.stderr @@ -0,0 +1,83 @@ +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:68:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_track_caller() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!() +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(ungated_async_fn_track_caller)]` on by default + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:83:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:121:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:158:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:164:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted + diff --git a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr index 5611e53f50a40..bd56982b0582c 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr @@ -1,10 +1,11 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:59:1 + --> $DIR/panic-track-caller.rs:68:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_track_caller() { +LL | | LINE.store(Location::caller().line(), Relaxed); LL | | panic!() LL | | } | |_- this function will not propagate the caller location @@ -15,12 +16,13 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:73:5 + --> $DIR/panic-track-caller.rs:83:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); LL | | panic!(); LL | | } | |_____- this function will not propagate the caller location @@ -30,12 +32,13 @@ LL | | } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:108:1 + --> $DIR/panic-track-caller.rs:121:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); LL | | panic!(); LL | | } | |_- this function will not propagate the caller location @@ -44,5 +47,37 @@ LL | | } = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: 3 warnings emitted +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:158:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:164:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted diff --git a/tests/ui/async-await/track-caller/panic-track-caller.nofeat_opt.stderr b/tests/ui/async-await/track-caller/panic-track-caller.nofeat_opt.stderr new file mode 100644 index 0000000000000..bd56982b0582c --- /dev/null +++ b/tests/ui/async-await/track-caller/panic-track-caller.nofeat_opt.stderr @@ -0,0 +1,83 @@ +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:68:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_track_caller() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!() +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(ungated_async_fn_track_caller)]` on by default + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:83:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_assoc() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:121:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_manual_poll() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:158:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_impl() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:164:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_trait_attr_in_both() { +LL | | LINE.store(Location::caller().line(), Relaxed); +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 5 warnings emitted + diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index 294150500170d..4e8accc2c0222 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -1,20 +1,26 @@ // This test is duplicated (with changes) at // src/tools/miri/tests/pass/async-panic-track-caller.rs -// FIXME: catch_unwind is broken in gcc. Will be fixed in the next rustc_codegen_gcc sync. -//@ ignore-backends: gcc //@ run-pass -//@ edition:2021 -//@ revisions: afn cls afn_cls nofeat +//@ edition:2024 +//@ revisions: nofeat afn cls afn_cls nofeat_opt afn_opt cls_opt afn_cls_opt +//@[nofeat_opt] compile-flags: -O +//@[afn_opt] compile-flags: -O +//@[cls_opt] compile-flags: -O +//@[afn_cls_opt] compile-flags: -O //@ needs-unwind // gate-test-async_fn_track_caller -#![feature(stmt_expr_attributes)] -#![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] -#![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] +#![feature(stmt_expr_attributes, coroutines, coroutine_trait, gen_blocks)] +#![cfg_attr(any(afn, afn_cls, afn_opt, afn_cls_opt), feature(async_fn_track_caller))] +#![cfg_attr(any(cls, afn_cls, cls_opt, afn_cls_opt), feature(closure_track_caller))] #![allow(unused)] use std::future::Future; -use std::panic; +use std::ops::Coroutine; +use std::panic::{self, Location}; +use std::pin::pin; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering::Relaxed; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll, Wake}; use std::thread::{self, Thread}; @@ -47,7 +53,10 @@ fn block_on(fut: impl Future) -> T { } } +static LINE: AtomicU32 = AtomicU32::new(0); + async fn bar() { + LINE.store(Location::caller().line(), Relaxed); panic!() } @@ -57,8 +66,9 @@ async fn foo() { } #[track_caller] -//[cls,nofeat]~^ WARN `#[track_caller]` on async functions is a no-op +//[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op async fn bar_track_caller() { + LINE.store(Location::caller().line(), Relaxed); panic!() } @@ -71,8 +81,9 @@ struct Foo; impl Foo { #[track_caller] - //[cls,nofeat]~^ WARN `#[track_caller]` on async functions is a no-op + //[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op async fn bar_assoc() { + LINE.store(Location::caller().line(), Relaxed); panic!(); } } @@ -84,10 +95,11 @@ async fn foo_assoc() { // Since compilation is expected to fail for this fn when `closure_track_caller` // is disabled, we test that separately in `async-closure-gate.rs` -#[cfg(any(cls, afn_cls))] +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] async fn foo_closure() { let closure = #[track_caller] async || { + LINE.store(Location::caller().line(), Relaxed); panic!(); }; let future = closure(); @@ -96,18 +108,20 @@ async fn foo_closure() { // Since compilation is expected to fail for this fn when `closure_track_caller` // is disabled, we test that separately in `async-closure-gate.rs` -#[cfg(any(cls, afn_cls))] +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] async fn foo_block() { let future = #[track_caller] async { + LINE.store(Location::caller().line(), Relaxed); panic!(); }; future.await; } #[track_caller] -//[cls,nofeat]~^ WARN `#[track_caller]` on async functions is a no-op +//[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op async fn bar_manual_poll() { + LINE.store(Location::caller().line(), Relaxed); panic!(); } @@ -119,6 +133,118 @@ fn foo_manual_poll() { assert_eq!(res, std::task::Poll::Ready(())); } +trait Trait { + async fn bar_trait_attr_nowhere(); + #[track_caller] + async fn bar_trait_attr_in_trait(); + async fn bar_trait_attr_in_impl(); + #[track_caller] + async fn bar_trait_attr_in_both(); + + #[track_caller] + fn bar_rpit_in_trait() -> impl Future; + #[track_caller] + async fn bar_rpit_in_impl(); +} +impl Trait for Foo { + async fn bar_trait_attr_nowhere() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + async fn bar_trait_attr_in_trait() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + #[track_caller] + //[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op + async fn bar_trait_attr_in_impl() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + #[track_caller] + //[nofeat,cls,nofeat_opt,cls_opt]~^ WARN `#[track_caller]` on async functions is a no-op + async fn bar_trait_attr_in_both() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + + async fn bar_rpit_in_trait() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + fn bar_rpit_in_impl() -> impl Future { + async { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + } + } +} + +async fn foo_trait_attr_nowhere() { + let future = Foo::bar_trait_attr_nowhere(); + future.await; +} +async fn foo_trait_attr_in_trait() { + let future = Foo::bar_trait_attr_in_trait(); + future.await; +} +async fn foo_trait_attr_in_impl() { + let future = Foo::bar_trait_attr_in_impl(); + future.await; +} +async fn foo_trait_attr_in_both() { + let future = Foo::bar_trait_attr_in_both(); + future.await; +} + +async fn foo_rpit_in_trait() { + let future = Foo::bar_rpit_in_trait(); + future.await; +} +async fn foo_rpit_in_impl() { + let future = Foo::bar_rpit_in_impl(); + future.await; +} + +#[track_caller] +gen fn bar_gen_fn() { + LINE.store(Location::caller().line(), Relaxed); + panic!(); +} + +fn foo_gen_fn() { + let mut iter = bar_gen_fn(); + let _ = iter.next(); +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] +fn foo_gen_block() { + let mut iter = #[track_caller] + gen { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + yield (); + }; + let _ = iter.next(); +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] +fn foo_coroutine() { + let coro = #[track_caller] + #[coroutine] + || { + LINE.store(Location::caller().line(), Relaxed); + panic!(); + yield (); + }; + let coro = std::pin::pin!(coro); + let _ = coro.resume(()); +} + fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { let loc = Arc::new(Mutex::new(None)); @@ -135,36 +261,119 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { x } -// FIXME(async_fn_track_caller): Currently, #[track_caller] on an async function -// uses the location where the future is awaited or polled. -// The correct behavior as per T-lang is to use the location where the function is called. fn main() { - assert_eq!(panicked_at(|| block_on(foo())), 51); + assert_eq!(panicked_at(|| block_on(foo())), 60); + assert_eq!(LINE.load(Relaxed), 59); - #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 67); - #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 62); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 76); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 76); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 72); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 71); - #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 82); - #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 92); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 92); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 87); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 86); + // FIXME(closure_track_caller): Currently, #[track_caller] on an async closure + // uses the location where the future is awaited or polled. + // It should be changed to use the location where the closure is called, + // so the behavior matches that of `async fn`. + #[cfg(any(afn_cls, afn_cls_opt))] + assert_eq!(panicked_at(|| block_on(foo_closure())), 106); + #[cfg(any(afn_cls, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 106); // FIXME(closure_track_caller): if closure_track_caller is enabled, but // async_fn_track_caller is disabled, then #[track_caller] on async closures // silently do nothing. Either it should function, or we should emit a warning. // See #161961 - #[cfg(cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 91); - #[cfg(afn_cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 94); - - #[cfg(any(cls, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_block())), 105); - - #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| foo_manual_poll()), 118); - #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| foo_manual_poll()), 111); + #[cfg(any(cls, cls_opt))] + assert_eq!(panicked_at(|| block_on(foo_closure())), 103); + #[cfg(any(cls, cls_opt))] + assert_eq!(LINE.load(Relaxed), 102); + + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| block_on(foo_block())), 118); + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 118); + + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| foo_manual_poll()), 129); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 129); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(panicked_at(|| foo_manual_poll()), 125); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 124); + + assert_eq!(panicked_at(|| block_on(foo_trait_attr_nowhere())), 152); + assert_eq!(LINE.load(Relaxed), 151); + + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_trait())), 188); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 188); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_trait())), 156); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 155); + + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_impl())), 192); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 192); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_impl())), 162); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 161); + + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_both())), 196); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 196); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(panicked_at(|| block_on(foo_trait_attr_in_both())), 168); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 167); + + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| block_on(foo_rpit_in_trait())), 201); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 201); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(panicked_at(|| block_on(foo_rpit_in_trait())), 173); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 172); + + assert_eq!(panicked_at(|| block_on(foo_rpit_in_impl())), 178); + assert_eq!(LINE.load(Relaxed), 177); + + // FIXME(gen_blocks): Decide if this behavior is correct. + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| foo_gen_fn()), 216); + #[cfg(any(afn, afn_cls, afn_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 216); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(panicked_at(|| foo_gen_fn()), 212); + #[cfg(any(cls, nofeat, cls_opt, nofeat_opt))] + assert_eq!(LINE.load(Relaxed), 211); + + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| foo_gen_block()), 230); + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 230); + + // FIXME(coroutines): This behavior is inconsistent with async blocks. + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(panicked_at(|| foo_coroutine()), 245); + #[cfg(any(cls, afn_cls, cls_opt, afn_cls_opt))] + assert_eq!(LINE.load(Relaxed), 245); }