Skip to content

Commit e589c06

Browse files
committed
Switch #[track_caller] back to a no-op unless feature gate is enabled
This patch fixes a regression, in which `#[track_caller]`, which was previously a no-op, was changed to actually turn on the behavior. This should instead only happen behind the `closure_track_caller` feature gate. Also, add a warning for the user to understand how their code will compile depending on the feature gate being turned on or not. Fixes #104588
1 parent e221616 commit e589c06

File tree

8 files changed

+188
-24
lines changed

8 files changed

+188
-24
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+30-23
Original file line numberDiff line numberDiff line change
@@ -655,33 +655,40 @@ impl<'hir> LoweringContext<'_, 'hir> {
655655

656656
hir::ExprKind::Closure(c)
657657
};
658-
let parent_has_track_caller = self
659-
.attrs
660-
.values()
661-
.find(|attrs| attrs.into_iter().find(|attr| attr.has_name(sym::track_caller)).is_some())
662-
.is_some();
658+
663659
let unstable_span =
664660
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
665661

666-
let hir_id = if parent_has_track_caller {
667-
let generator_hir_id = self.lower_node_id(closure_node_id);
668-
self.lower_attrs(
669-
generator_hir_id,
670-
&[Attribute {
671-
kind: AttrKind::Normal(ptr::P(NormalAttr {
672-
item: AttrItem {
673-
path: Path::from_ident(Ident::new(sym::track_caller, span)),
674-
args: AttrArgs::Empty,
662+
let hir_id = if self.tcx.features().closure_track_caller {
663+
let parent_has_track_caller = self
664+
.attrs
665+
.values()
666+
.find(|attrs| {
667+
attrs.into_iter().find(|attr| attr.has_name(sym::track_caller)).is_some()
668+
})
669+
.is_some();
670+
if parent_has_track_caller {
671+
let generator_hir_id = self.lower_node_id(closure_node_id);
672+
self.lower_attrs(
673+
generator_hir_id,
674+
&[Attribute {
675+
kind: AttrKind::Normal(ptr::P(NormalAttr {
676+
item: AttrItem {
677+
path: Path::from_ident(Ident::new(sym::track_caller, span)),
678+
args: AttrArgs::Empty,
679+
tokens: None,
680+
},
675681
tokens: None,
676-
},
677-
tokens: None,
678-
})),
679-
id: self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id(),
680-
style: AttrStyle::Outer,
681-
span: unstable_span,
682-
}],
683-
);
684-
generator_hir_id
682+
})),
683+
id: self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id(),
684+
style: AttrStyle::Outer,
685+
span: unstable_span,
686+
}],
687+
);
688+
generator_hir_id
689+
} else {
690+
self.lower_node_id(closure_node_id)
691+
}
685692
} else {
686693
self.lower_node_id(closure_node_id)
687694
};

compiler/rustc_error_messages/locales/en-US/lint.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,8 @@ lint_builtin_mutable_transmutes =
350350
351351
lint_builtin_unstable_features = unstable feature
352352
353+
lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op, unless the `closure_track_caller` feature is enabled
354+
353355
lint_builtin_unreachable_pub = unreachable `pub` {$what}
354356
.suggestion = consider restricting its visibility
355357
.help = or consider exporting it for use by other crates

compiler/rustc_lint/src/builtin.rs

+44-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::{
2525
types::{transparent_newtype_field, CItemKind},
2626
EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
2727
};
28+
use hir::IsAsync;
2829
use rustc_ast::attr;
2930
use rustc_ast::tokenstream::{TokenStream, TokenTree};
3031
use rustc_ast::visit::{FnCtxt, FnKind};
@@ -40,7 +41,10 @@ use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, Gate
4041
use rustc_hir as hir;
4142
use rustc_hir::def::{DefKind, Res};
4243
use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
43-
use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, Node, PatKind, PredicateOrigin};
44+
use rustc_hir::intravisit::FnKind as HirFnKind;
45+
use rustc_hir::{
46+
Body, FnDecl, ForeignItemKind, GenericParamKind, HirId, Node, PatKind, PredicateOrigin,
47+
};
4448
use rustc_index::vec::Idx;
4549
use rustc_middle::lint::in_external_macro;
4650
use rustc_middle::ty::layout::{LayoutError, LayoutOf};
@@ -1337,6 +1341,45 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
13371341
}
13381342
}
13391343

1344+
declare_lint! {
1345+
/// `#[track_caller]` is a no-op without corresponding feature flag
1346+
UNGATED_ASYNC_FN_TRACK_CALLER,
1347+
Warn,
1348+
"enabling track_caller on an async fn is a no-op unless the closure_track_caller feature is enabled"
1349+
}
1350+
1351+
declare_lint_pass!(
1352+
/// Explains corresponding feature flag must be enabled for the `#[track_caller] attribute to
1353+
/// do anything
1354+
UngatedAsyncFnTrackCaller => [UNGATED_ASYNC_FN_TRACK_CALLER]
1355+
);
1356+
1357+
impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
1358+
fn check_fn(
1359+
&mut self,
1360+
cx: &LateContext<'_>,
1361+
fn_kind: HirFnKind<'_>,
1362+
_: &'tcx FnDecl<'_>,
1363+
_: &'tcx Body<'_>,
1364+
span: Span,
1365+
hir_id: HirId,
1366+
) {
1367+
if let HirFnKind::ItemFn(_, _, _) = fn_kind && fn_kind.asyncness() == IsAsync::Async && !cx.tcx.features().closure_track_caller {
1368+
// Now, check if the function has the `#[track_caller]` attribute
1369+
let attrs = cx.tcx.hir().attrs(hir_id);
1370+
let maybe_track_caller = attrs.iter().find(|attr| attr.has_name(sym::track_caller));
1371+
if let Some(attr) = maybe_track_caller {
1372+
cx.struct_span_lint(
1373+
UNGATED_ASYNC_FN_TRACK_CALLER,
1374+
span.with_hi(attr.span.hi()),
1375+
fluent::lint_ungated_async_fn_track_caller,
1376+
|lint| lint,
1377+
);
1378+
}
1379+
}
1380+
}
1381+
}
1382+
13401383
declare_lint! {
13411384
/// The `unreachable_pub` lint triggers for `pub` items not reachable from
13421385
/// the crate root.

compiler/rustc_lint/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ macro_rules! late_lint_mod_passes {
221221
// May Depend on constants elsewhere
222222
UnusedBrokenConst: UnusedBrokenConst,
223223
UnstableFeatures: UnstableFeatures,
224+
UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller,
224225
ArrayIntoIter: ArrayIntoIter::default(),
225226
DropTraitConstraints: DropTraitConstraints,
226227
TemporaryCStringAsPtr: TemporaryCStringAsPtr,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// run-pass
2+
// edition:2021
3+
// needs-unwind
4+
5+
6+
use std::future::Future;
7+
use std::panic;
8+
use std::sync::{Arc, Mutex};
9+
use std::task::{Context, Poll, Wake};
10+
use std::thread::{self, Thread};
11+
12+
/// A waker that wakes up the current thread when called.
13+
struct ThreadWaker(Thread);
14+
15+
impl Wake for ThreadWaker {
16+
fn wake(self: Arc<Self>) {
17+
self.0.unpark();
18+
}
19+
}
20+
21+
/// Run a future to completion on the current thread.
22+
fn block_on<T>(fut: impl Future<Output = T>) -> T {
23+
// Pin the future so it can be polled.
24+
let mut fut = Box::pin(fut);
25+
26+
// Create a new context to be passed to the future.
27+
let t = thread::current();
28+
let waker = Arc::new(ThreadWaker(t)).into();
29+
let mut cx = Context::from_waker(&waker);
30+
31+
// Run the future to completion.
32+
loop {
33+
match fut.as_mut().poll(&mut cx) {
34+
Poll::Ready(res) => return res,
35+
Poll::Pending => thread::park(),
36+
}
37+
}
38+
}
39+
40+
async fn bar() {
41+
panic!()
42+
}
43+
44+
async fn foo() {
45+
bar().await
46+
}
47+
48+
#[track_caller] //~ WARN `#[track_caller]` on async functions is a no-op, unless the `closure_track_caller` feature is enabled [ungated_async_fn_track_caller]
49+
async fn bar_track_caller() {
50+
panic!()
51+
}
52+
53+
async fn foo_track_caller() {
54+
bar_track_caller().await
55+
}
56+
57+
fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 {
58+
let loc = Arc::new(Mutex::new(None));
59+
60+
let hook = panic::take_hook();
61+
{
62+
let loc = loc.clone();
63+
panic::set_hook(Box::new(move |info| {
64+
*loc.lock().unwrap() = info.location().map(|loc| loc.line())
65+
}));
66+
}
67+
panic::catch_unwind(f).unwrap_err();
68+
panic::set_hook(hook);
69+
let x = loc.lock().unwrap().unwrap();
70+
x
71+
}
72+
73+
fn main() {
74+
assert_eq!(panicked_at(|| block_on(foo())), 41);
75+
// Since the `closure_track_caller` feature is not enabled, the
76+
// `track_caller annotation does nothing.
77+
assert_eq!(panicked_at(|| block_on(foo_track_caller())), 50);
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
warning: `#[track_caller]` on async functions is a no-op, unless the `closure_track_caller` feature is enabled
2+
--> $DIR/issue-104588-no-op-panic-track-caller.rs:48:16
3+
|
4+
LL | #[track_caller]
5+
| ________________^
6+
LL | | async fn bar_track_caller() {
7+
| |_
8+
|
9+
= note: `#[warn(ungated_async_fn_track_caller)]` on by default
10+
11+
warning: 1 warning emitted
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// check-pass
2+
// edition:2021
3+
4+
#[track_caller] //~ WARN `#[track_caller]` on async functions is a no-op, unless the `closure_track_caller` feature is enabled
5+
async fn foo() {}
6+
7+
fn main() {
8+
foo();
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
warning: `#[track_caller]` on async functions is a no-op, unless the `closure_track_caller` feature is enabled
2+
--> $DIR/issue-104588-no-op-track-caller.rs:4:16
3+
|
4+
LL | #[track_caller]
5+
| ________________^
6+
LL | | async fn foo() {}
7+
| |_
8+
|
9+
= note: `#[warn(ungated_async_fn_track_caller)]` on by default
10+
11+
warning: 1 warning emitted
12+

0 commit comments

Comments
 (0)