From 814a530db0a3f91821095a830fa321fdd5a41d17 Mon Sep 17 00:00:00 2001 From: Adam Perry Date: Tue, 14 Jan 2020 21:39:16 -0800 Subject: [PATCH] Document #[track_caller]. --- src/attributes.md | 2 + src/attributes/codegen.md | 111 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/src/attributes.md b/src/attributes.md index d03dfa87c..d00653073 100644 --- a/src/attributes.md +++ b/src/attributes.md @@ -221,6 +221,7 @@ The following is an index of all built-in attributes. - [`cold`] — Hint that a function is unlikely to be called. - [`no_builtins`] — Disables use of certain built-in functions. - [`target_feature`] — Configure platform-specific code generation. + - [`track_caller`] - Pass the parent call location to `std::panic::Location::caller()`. - Documentation - `doc` — Specifies documentation. See [The Rustdoc Book] for more information. [Doc comments] are transformed into `doc` attributes. @@ -294,6 +295,7 @@ The following is an index of all built-in attributes. [`should_panic`]: attributes/testing.md#the-should_panic-attribute [`target_feature`]: attributes/codegen.md#the-target_feature-attribute [`test`]: attributes/testing.md#the-test-attribute +[`track_caller`]: attributes/codegen.md#the-track_caller-attribute [`type_length_limit`]: attributes/limits.md#the-type_length_limit-attribute [`used`]: abi.md#the-used-attribute [`warn`]: attributes/diagnostics.md#lint-check-attributes diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index ebcef8c28..3713f3acd 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -143,6 +143,113 @@ feature detection on the x86 platforms. > may be enabled or disabled for an entire crate with the > [`-C target-feature`] flag. +## The `track_caller` attribute + +The `track_caller` attribute may be applied to any function with [`"Rust"` ABI][rust-abi]. When +applied to functions and methods in trait declarations, the attribute applies to all +implementations. If the trait provides a default implementation with the attribute, then the +attribute also applies to override implementations. + +When applied to a function in an `extern` block the attribute must also be applied to any linked +implementations, otherwise undefined behavior results. When applied to a function which is made +available to an `extern` block, the declaration in the `extern` block must also have the attribute, +otherwise undefined behavior results. + +### Behavior + +Applying the attribute to a function `f` allows code within `f` to get a hint of the [`Location`] of +the "topmost" tracked call that led to `f`'s invocation. At the point of observation, an +implementation behaves as if it walks up the stack from `f`'s frame to find the nearest frame of an +*unattributed* function `outer`, and it returns the [`Location`] of the tracked call in `outer`. + +```rust +#[track_caller] +fn f() { + println!("{}", std::panic::Location::caller()); +} +``` + +> Note: `core` provides [`core::panic::Location::caller`] for observing caller locations. It wraps +> the [`core::intrinsics::caller_location`] intrinsic implemented by `rustc`. + +> Note: because the resulting `Location` is a hint, an implementation may halt its walk up the stack +> early. See [Limitations](#limitations) for important caveats. + +#### Examples + +When `f` is called directly by `calls_f`, code in `f` observes its callsite within `calls_f`: + +```rust +# #[track_caller] +# fn f() { +# println!("{}", std::panic::Location::caller()); +# } +fn calls_f() { + f(); // <-- f() prints this location +} +``` + +When `f` is called by another attributed function `g` which is in turn called by `calls_g`, code in +both `f` and `g` observes `g`'s callsite within `calls_g`: + +```rust +# #[track_caller] +# fn f() { +# println!("{}", std::panic::Location::caller()); +# } +#[track_caller] +fn g() { + println!("{}", std::panic::Location::caller()); + f(); +} + +fn calls_g() { + g(); // <-- g() prints this location twice, once itself and once from f() +} +``` + +When `g` is called by another attributed function `h` which is in turn called by `calls_h`, all code +in `f`, `g`, and `h` observes `h`'s callsite within `calls_h`: + +```rust +# #[track_caller] +# fn f() { +# println!("{}", std::panic::Location::caller()); +# } +# #[track_caller] +# fn g() { +# println!("{}", std::panic::Location::caller()); +# f(); +# } +#[track_caller] +fn h() { + println!("{}", std::panic::Location::caller()); + g(); +} + +fn calls_h() { + h(); // <-- prints this location three times, once itself, once from g(), once from f() +} +``` + +And so on. + +### Limitations + +This information is a hint and implementations are not required to preserve it. + +In particular, coercing a function with `#[track_caller]` to a function pointer creates a shim which +appears to observers to have been called at the attributed function's definition site, losing actual +caller information across virtual calls. A common example of this coercion is the creation of a +trait object whose methods are attributed. + +> Note: The aforementioned shim for function pointers is necessary because `rustc` implements +> `track_caller` in a codegen context by appending an implicit parameter to the function ABI, but +> this would be unsound for an indirect call because the parameter is not a part of the function's +> type and a given function pointer type may or may not refer to a function with the attribute. The +> creation of a shim hides the implicit parameter from callers of the function pointer, preserving +> soundness. + [_MetaListNameValueStr_]: ../attributes.md#meta-item-attribute-syntax [`-C target-cpu`]: ../../rustc/codegen-options/index.html#target-cpu [`-C target-feature`]: ../../rustc/codegen-options/index.html#target-feature @@ -155,3 +262,7 @@ feature detection on the x86 platforms. [trait]: ../items/traits.md [undefined behavior]: ../behavior-considered-undefined.md [unsafe function]: ../unsafe-functions.md +[rust-abi]: ../items/external-blocks.md#abi +[`core::intrinsics::caller_location`]: ../../core/intrinsics/fn.caller_location.html +[`core::panic::Location::caller`]: ../../core/panic/struct.Location.html#method.caller +[`Location`]: ../../core/panic/struct.Location.html