1
1
# Implicit Caller Location
2
2
3
+ <!-- toc -->
4
+
3
5
Approved in [ RFC 2091] , this feature enables the accurate reporting of caller location during panics
4
- initiated from functions like ` Option::unwrap ` , ` Result::expect ` , and ` Index::index ` . This feature
5
- adds the [ ` #[track_caller] ` ] [ attr-reference ] attribute for functions, the
6
- [ ` caller_location ` ] [ intrinsic ] intrinsic, and the stabilization-friendly
6
+ initiated from functions like ` Option::unwrap ` , ` Result::expect ` , and ` Index::index ` . This feature
7
+ adds the [ ` #[track_caller] ` ] [ attr-reference ] attribute for functions, the
8
+ [ ` caller_location ` ] [ intrinsic ] intrinsic, and the stabilization-friendly
7
9
[ ` core::panic::Location::caller ` ] [ wrapper ] wrapper.
8
10
9
11
## Motivating Example
@@ -28,25 +30,25 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
28
30
As of 1.42, we get a much more helpful message:
29
31
30
32
```
31
- $ rustc +1.42.0 example.rs; example.exe
33
+ $ rustc +1.42.0 example.rs; example.exe
32
34
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', example.rs:3:5
33
35
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
34
36
```
35
37
36
38
These error messages are achieved through a combination of changes to ` panic! ` internals to make use
37
- of ` core::panic::Location::caller ` and a number of ` #[track_caller] ` annotations in the standard
39
+ of ` core::panic::Location::caller ` and a number of ` #[track_caller] ` annotations in the standard
38
40
library which propagate caller information.
39
41
40
42
## Reading Caller Location
41
43
42
44
Previously, ` panic! ` made use of the ` file!() ` , ` line!() ` , and ` column!() ` macros to construct a
43
45
[ ` Location ` ] pointing to where the panic occurred. These macros couldn't be given an overridden
44
- location, so functions which intentionally invoked ` panic! ` couldn't provide their own location,
46
+ location, so functions which intentionally invoked ` panic! ` couldn't provide their own location,
45
47
hiding the actual source of error.
46
48
47
- Internally, ` panic!() ` now calls [ ` core::panic::Location::caller() ` ] [ wrapper ] to find out where it
48
- was expanded. This function is itself annotated with ` #[track_caller] ` and wraps the
49
- [ ` caller_location ` ] [ intrinsic ] compiler intrinsic implemented by rustc. This intrinsic is easiest
49
+ Internally, ` panic!() ` now calls [ ` core::panic::Location::caller() ` ] [ wrapper ] to find out where it
50
+ was expanded. This function is itself annotated with ` #[track_caller] ` and wraps the
51
+ [ ` caller_location ` ] [ intrinsic ] compiler intrinsic implemented by rustc. This intrinsic is easiest
50
52
explained in terms of how it works in a ` const ` context.
51
53
52
54
## Caller Location in ` const `
@@ -56,20 +58,20 @@ to find the right location and allocating a const value to return.
56
58
57
59
### Finding the right ` Location `
58
60
59
- In a const context we "walk up the stack" from where the intrinsic is invoked, stopping when we
61
+ In a const context we "walk up the stack" from where the intrinsic is invoked, stopping when we
60
62
reach the first function call in the stack which does * not* have the attribute. This walk is in
61
63
[ ` InterpCx::find_closest_untracked_caller_location() ` ] [ const-find-closest ] .
62
64
63
- Starting at the bottom, we iterate up over stack [ ` Frame ` ] [ const-frame ] s in the
65
+ Starting at the bottom, we iterate up over stack [ ` Frame ` ] [ const-frame ] s in the
64
66
[ ` InterpCx::stack ` ] [ const-stack ] , calling
65
- [ ` InstanceDef::requires_caller_location ` ] [ requires-location ] on the
67
+ [ ` InstanceDef::requires_caller_location ` ] [ requires-location ] on the
66
68
[ ` Instance ` s from each ` Frame ` ] [ frame-instance ] . We stop once we find one that returns ` false ` and
67
69
return the span of the * previous* frame which was the "topmost" tracked function.
68
70
69
71
### Allocating a static ` Location `
70
72
71
- Once we have a ` Span ` , we need to allocate static memory for the ` Location ` , which is performed by
72
- the [ ` TyCtxt::const_caller_location() ` ] [ const-location-query ] query. Internally this calls
73
+ Once we have a ` Span ` , we need to allocate static memory for the ` Location ` , which is performed by
74
+ the [ ` TyCtxt::const_caller_location() ` ] [ const-location-query ] query. Internally this calls
73
75
[ ` InterpCx::alloc_caller_location() ` ] [ alloc-location ] and results in a unique
74
76
[ memory kind] [ location-memory-kind ] (` MemoryKind::CallerLocation ` ). The SSA codegen backend is able
75
77
to emit code for these same values, and we use this code there as well.
@@ -78,14 +80,14 @@ Once our `Location` has been allocated in static memory, our intrinsic returns a
78
80
79
81
## Generating code for ` #[track_caller] ` callees
80
82
81
- To generate efficient code for a tracked function and its callers, we need to provide the same
83
+ To generate efficient code for a tracked function and its callers, we need to provide the same
82
84
behavior from the intrinsic's point of view without having a stack to walk up at runtime. We invert
83
85
the approach: as we grow the stack down we pass an additional argument to calls of tracked functions
84
86
rather than walking up the stack when the intrinsic is called. That additional argument can be
85
87
returned wherever the caller location is queried.
86
88
87
89
The argument we append is of type ` &'static core::panic::Location<'static> ` . A reference was chosen
88
- to avoid unnecessary copying because a pointer is a third the size of
90
+ to avoid unnecessary copying because a pointer is a third the size of
89
91
` std::mem::size_of::<core::panic::Location>() == 24 ` at time of writing.
90
92
91
93
When generating a call to a function which is tracked, we pass the location argument the value of
@@ -151,12 +153,12 @@ probably the best we can do without modifying fully-stabilized type signatures.
151
153
152
154
> * Note:* We always emit a [ ` ReifyShim ` ] when taking a pointer to a tracked function. While the
153
155
> constraint here is imposed by codegen contexts, we don't know during MIR construction of the shim
154
- > whether we'll be called in a const context (safe to ignore shim) or in a codegen context (unsafe
156
+ > whether we'll be called in a const context (safe to ignore shim) or in a codegen context (unsafe
155
157
> to ignore shim). Even if we did know, the results from const and codegen contexts must agree.
156
158
157
159
## The Attribute
158
160
159
- The ` #[track_caller] ` attribute is checked alongside other codegen attributes to ensure the
161
+ The ` #[track_caller] ` attribute is checked alongside other codegen attributes to ensure the
160
162
function:
161
163
162
164
* has the ` "Rust" ` ABI (as opposed to e.g., ` "C" ` )
@@ -171,7 +173,7 @@ used in both const and codegen contexts to ensure correct propagation.
171
173
172
174
When applied to trait method implementations, the attribute works as it does for regular functions.
173
175
174
- When applied to a trait method prototype, the attribute applies to all implementations of the
176
+ When applied to a trait method prototype, the attribute applies to all implementations of the
175
177
method. When applied to a default trait method implementation, the attribute takes effect on
176
178
that implementation * and* any overrides.
177
179
@@ -203,14 +205,14 @@ trait TrackedFourWays {
203
205
assert_tracked! ();
204
206
}
205
207
206
- /// Overrides of this implementation are tracked (it is too).
208
+ /// Overrides of this implementation are tracked (it is too).
207
209
#[track_caller]
208
210
fn default_tracked_to_override () {
209
211
assert_tracked! ();
210
212
}
211
213
}
212
214
213
- /// This impl uses the default impl for `default_tracked` and provides its own for
215
+ /// This impl uses the default impl for `default_tracked` and provides its own for
214
216
/// `default_tracked_to_override`.
215
217
impl TrackedFourWays for () {
216
218
fn blanket_tracked () {
@@ -253,7 +255,7 @@ up on the tracking issue. During the course of implementing that, it was also di
253
255
implementation was possible without modifying the number of arguments in a function's MIR, which
254
256
would simplify later stages and unlock use in traits.
255
257
256
- Because the RFC's implementation strategy could not readily support traits, the semantics were not
258
+ Because the RFC's implementation strategy could not readily support traits, the semantics were not
257
259
originally specified. They have since been implemented following the path which seemed most correct
258
260
to the author and reviewers.
259
261
0 commit comments