1
1
#![ feature( core_intrinsics) ]
2
+ #![ feature( stmt_expr_attributes) ]
3
+ #![ feature( closure_track_caller) ]
4
+ #![ feature( generator_trait) ]
5
+ #![ feature( generators) ]
2
6
7
+ use std:: ops:: { Generator , GeneratorState } ;
3
8
use std:: panic:: Location ;
9
+ use std:: pin:: Pin ;
10
+
11
+ type Loc = & ' static Location < ' static > ;
4
12
5
13
#[ track_caller]
6
- fn tracked ( ) -> & ' static Location < ' static > {
14
+ fn tracked ( ) -> Loc {
7
15
Location :: caller ( ) // most importantly, we never get line 7
8
16
}
9
17
10
- fn nested_intrinsic ( ) -> & ' static Location < ' static > {
18
+ fn nested_intrinsic ( ) -> Loc {
11
19
Location :: caller ( )
12
20
}
13
21
14
- fn nested_tracked ( ) -> & ' static Location < ' static > {
22
+ fn nested_tracked ( ) -> Loc {
15
23
tracked ( )
16
24
}
17
25
@@ -21,6 +29,44 @@ macro_rules! caller_location_from_macro {
21
29
} ;
22
30
}
23
31
32
+ fn test_basic ( ) {
33
+ let location = Location :: caller ( ) ;
34
+ let expected_line = line ! ( ) - 1 ;
35
+ assert_eq ! ( location. file( ) , file!( ) ) ;
36
+ assert_eq ! ( location. line( ) , expected_line) ;
37
+ assert_eq ! ( location. column( ) , 20 ) ;
38
+
39
+ let tracked = tracked ( ) ;
40
+ let expected_line = line ! ( ) - 1 ;
41
+ assert_eq ! ( tracked. file( ) , file!( ) ) ;
42
+ assert_eq ! ( tracked. line( ) , expected_line) ;
43
+ assert_eq ! ( tracked. column( ) , 19 ) ;
44
+
45
+ let nested = nested_intrinsic ( ) ;
46
+ assert_eq ! ( nested. file( ) , file!( ) ) ;
47
+ assert_eq ! ( nested. line( ) , 19 ) ;
48
+ assert_eq ! ( nested. column( ) , 5 ) ;
49
+
50
+ let contained = nested_tracked ( ) ;
51
+ assert_eq ! ( contained. file( ) , file!( ) ) ;
52
+ assert_eq ! ( contained. line( ) , 23 ) ;
53
+ assert_eq ! ( contained. column( ) , 5 ) ;
54
+
55
+ // `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
56
+ // i.e. point to where the macro was invoked, instead of the macro itself.
57
+ let inmacro = caller_location_from_macro ! ( ) ;
58
+ let expected_line = line ! ( ) - 1 ;
59
+ assert_eq ! ( inmacro. file( ) , file!( ) ) ;
60
+ assert_eq ! ( inmacro. line( ) , expected_line) ;
61
+ assert_eq ! ( inmacro. column( ) , 19 ) ;
62
+
63
+ let intrinsic = core:: intrinsics:: caller_location ( ) ;
64
+ let expected_line = line ! ( ) - 1 ;
65
+ assert_eq ! ( intrinsic. file( ) , file!( ) ) ;
66
+ assert_eq ! ( intrinsic. line( ) , expected_line) ;
67
+ assert_eq ! ( intrinsic. column( ) , 21 ) ;
68
+ }
69
+
24
70
fn test_fn_ptr ( ) {
25
71
fn pass_to_ptr_call < T > ( f : fn ( T ) , x : T ) {
26
72
f ( x) ;
@@ -87,44 +133,144 @@ fn test_trait_obj2() {
87
133
assert_eq ! ( loc. line( ) , expected_line) ;
88
134
}
89
135
90
- fn main ( ) {
91
- let location = Location :: caller ( ) ;
92
- let expected_line = line ! ( ) - 1 ;
93
- assert_eq ! ( location. file( ) , file!( ) ) ;
94
- assert_eq ! ( location. line( ) , expected_line) ;
95
- assert_eq ! ( location. column( ) , 20 ) ;
136
+ fn test_closure ( ) {
137
+ #[ track_caller]
138
+ fn mono_invoke_fn < F : Fn ( & ' static str , bool ) -> ( & ' static str , bool , Loc ) > (
139
+ val : & F ,
140
+ ) -> ( & ' static str , bool , Loc ) {
141
+ val ( "from_mono" , false )
142
+ }
96
143
97
- let tracked = tracked ( ) ;
98
- let expected_line = line ! ( ) - 1 ;
99
- assert_eq ! ( tracked. file( ) , file!( ) ) ;
100
- assert_eq ! ( tracked. line( ) , expected_line) ;
101
- assert_eq ! ( tracked. column( ) , 19 ) ;
144
+ #[ track_caller]
145
+ fn mono_invoke_fn_once < F : FnOnce ( & ' static str , bool ) -> ( & ' static str , bool , Loc ) > (
146
+ val : F ,
147
+ ) -> ( & ' static str , bool , Loc ) {
148
+ val ( "from_mono" , false )
149
+ }
102
150
103
- let nested = nested_intrinsic ( ) ;
104
- assert_eq ! ( nested. file( ) , file!( ) ) ;
105
- assert_eq ! ( nested. line( ) , 11 ) ;
106
- assert_eq ! ( nested. column( ) , 5 ) ;
151
+ #[ track_caller]
152
+ fn dyn_invoke_fn_mut (
153
+ val : & mut dyn FnMut ( & ' static str , bool ) -> ( & ' static str , bool , Loc ) ,
154
+ ) -> ( & ' static str , bool , Loc ) {
155
+ val ( "from_dyn" , false )
156
+ }
107
157
108
- let contained = nested_tracked ( ) ;
109
- assert_eq ! ( contained. file( ) , file!( ) ) ;
110
- assert_eq ! ( contained. line( ) , 15 ) ;
111
- assert_eq ! ( contained. column( ) , 5 ) ;
158
+ #[ track_caller]
159
+ fn dyn_invoke_fn_once (
160
+ val : Box < dyn FnOnce ( & ' static str , bool ) -> ( & ' static str , bool , Loc ) > ,
161
+ ) -> ( & ' static str , bool , Loc ) {
162
+ val ( "from_dyn" , false )
163
+ }
112
164
113
- // `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
114
- // i.e. point to where the macro was invoked, instead of the macro itself.
115
- let inmacro = caller_location_from_macro ! ( ) ;
116
- let expected_line = line ! ( ) - 1 ;
117
- assert_eq ! ( inmacro. file( ) , file!( ) ) ;
118
- assert_eq ! ( inmacro. line( ) , expected_line) ;
119
- assert_eq ! ( inmacro. column( ) , 19 ) ;
165
+ let mut track_closure = #[ track_caller]
166
+ |first: & ' static str , second: bool | ( first, second, Location :: caller( ) ) ;
167
+ let ( first_arg, first_bool, first_loc) = track_closure ( "first_arg" , true ) ;
168
+ let first_line = line ! ( ) - 1 ;
169
+ assert_eq ! ( first_arg, "first_arg" ) ;
170
+ assert_eq ! ( first_bool, true ) ;
171
+ assert_eq ! ( first_loc. file( ) , file!( ) ) ;
172
+ assert_eq ! ( first_loc. line( ) , first_line) ;
173
+ assert_eq ! ( first_loc. column( ) , 46 ) ;
120
174
121
- let intrinsic = core:: intrinsics:: caller_location ( ) ;
122
- let expected_line = line ! ( ) - 1 ;
123
- assert_eq ! ( intrinsic. file( ) , file!( ) ) ;
124
- assert_eq ! ( intrinsic. line( ) , expected_line) ;
125
- assert_eq ! ( intrinsic. column( ) , 21 ) ;
175
+ let ( dyn_arg, dyn_bool, dyn_loc) = dyn_invoke_fn_mut ( & mut track_closure) ;
176
+ assert_eq ! ( dyn_arg, "from_dyn" ) ;
177
+ assert_eq ! ( dyn_bool, false ) ;
178
+ // `FnMut::call_mut` does not have `#[track_caller]`,
179
+ // so this will not match
180
+ assert_ne ! ( dyn_loc. file( ) , file!( ) ) ;
181
+
182
+ let ( dyn_arg, dyn_bool, dyn_loc) = dyn_invoke_fn_once ( Box :: new ( track_closure) ) ;
183
+ assert_eq ! ( dyn_arg, "from_dyn" ) ;
184
+ assert_eq ! ( dyn_bool, false ) ;
185
+ // `FnOnce::call_once` does not have `#[track_caller]`
186
+ // so this will not match
187
+ assert_ne ! ( dyn_loc. file( ) , file!( ) ) ;
188
+
189
+ let ( mono_arg, mono_bool, mono_loc) = mono_invoke_fn ( & track_closure) ;
190
+ let mono_line = line ! ( ) - 1 ;
191
+ assert_eq ! ( mono_arg, "from_mono" ) ;
192
+ assert_eq ! ( mono_bool, false ) ;
193
+ assert_eq ! ( mono_loc. file( ) , file!( ) ) ;
194
+ assert_eq ! ( mono_loc. line( ) , mono_line) ;
195
+ assert_eq ! ( mono_loc. column( ) , 43 ) ;
196
+
197
+ let ( mono_arg, mono_bool, mono_loc) = mono_invoke_fn_once ( track_closure) ;
198
+ let mono_line = line ! ( ) - 1 ;
199
+ assert_eq ! ( mono_arg, "from_mono" ) ;
200
+ assert_eq ! ( mono_bool, false ) ;
201
+ assert_eq ! ( mono_loc. file( ) , file!( ) ) ;
202
+ assert_eq ! ( mono_loc. line( ) , mono_line) ;
203
+ assert_eq ! ( mono_loc. column( ) , 43 ) ;
204
+
205
+ let non_tracked_caller = || Location :: caller ( ) ;
206
+ let non_tracked_line = line ! ( ) - 1 ; // This is the line of the closure, not its caller
207
+ let non_tracked_loc = non_tracked_caller ( ) ;
208
+ assert_eq ! ( non_tracked_loc. file( ) , file!( ) ) ;
209
+ assert_eq ! ( non_tracked_loc. line( ) , non_tracked_line) ;
210
+ assert_eq ! ( non_tracked_loc. column( ) , 33 ) ;
211
+ }
212
+
213
+ fn test_generator ( ) {
214
+ #[ track_caller]
215
+ fn mono_generator < F : Generator < String , Yield = ( & ' static str , String , Loc ) , Return = ( ) > > (
216
+ val : Pin < & mut F > ,
217
+ ) -> ( & ' static str , String , Loc ) {
218
+ match val. resume ( "Mono" . to_string ( ) ) {
219
+ GeneratorState :: Yielded ( val) => val,
220
+ _ => unreachable ! ( ) ,
221
+ }
222
+ }
126
223
224
+ #[ track_caller]
225
+ fn dyn_generator (
226
+ val : Pin < & mut dyn Generator < String , Yield = ( & ' static str , String , Loc ) , Return = ( ) > > ,
227
+ ) -> ( & ' static str , String , Loc ) {
228
+ match val. resume ( "Dyn" . to_string ( ) ) {
229
+ GeneratorState :: Yielded ( val) => val,
230
+ _ => unreachable ! ( ) ,
231
+ }
232
+ }
233
+
234
+ #[ rustfmt:: skip]
235
+ let generator = #[ track_caller] |arg: String | {
236
+ yield ( "first" , arg. clone ( ) , Location :: caller ( ) ) ;
237
+ yield ( "second" , arg. clone ( ) , Location :: caller ( ) ) ;
238
+ } ;
239
+
240
+ let mut pinned = Box :: pin ( generator) ;
241
+ let ( dyn_ret, dyn_arg, dyn_loc) = dyn_generator ( pinned. as_mut ( ) ) ;
242
+ assert_eq ! ( dyn_ret, "first" ) ;
243
+ assert_eq ! ( dyn_arg, "Dyn" . to_string( ) ) ;
244
+ // The `Generator` trait does not have `#[track_caller]` on `resume`, so
245
+ // this will not match.
246
+ assert_ne ! ( dyn_loc. file( ) , file!( ) ) ;
247
+
248
+ let ( mono_ret, mono_arg, mono_loc) = mono_generator ( pinned. as_mut ( ) ) ;
249
+ let mono_line = line ! ( ) - 1 ;
250
+ assert_eq ! ( mono_ret, "second" ) ;
251
+ // The generator ignores the argument to the second `resume` call
252
+ assert_eq ! ( mono_arg, "Dyn" . to_string( ) ) ;
253
+ assert_eq ! ( mono_loc. file( ) , file!( ) ) ;
254
+ assert_eq ! ( mono_loc. line( ) , mono_line) ;
255
+ assert_eq ! ( mono_loc. column( ) , 42 ) ;
256
+
257
+ #[ rustfmt:: skip]
258
+ let non_tracked_generator = || { yield Location :: caller ( ) ; } ;
259
+ let non_tracked_line = line ! ( ) - 1 ; // This is the line of the generator, not its caller
260
+ let non_tracked_loc = match Box :: pin ( non_tracked_generator) . as_mut ( ) . resume ( ( ) ) {
261
+ GeneratorState :: Yielded ( val) => val,
262
+ _ => unreachable ! ( ) ,
263
+ } ;
264
+ assert_eq ! ( non_tracked_loc. file( ) , file!( ) ) ;
265
+ assert_eq ! ( non_tracked_loc. line( ) , non_tracked_line) ;
266
+ assert_eq ! ( non_tracked_loc. column( ) , 44 ) ;
267
+ }
268
+
269
+ fn main ( ) {
270
+ test_basic ( ) ;
127
271
test_fn_ptr ( ) ;
128
272
test_trait_obj ( ) ;
129
273
test_trait_obj2 ( ) ;
274
+ test_closure ( ) ;
275
+ test_generator ( ) ;
130
276
}
0 commit comments