11#![ feature( core_intrinsics) ]
2+ #![ feature( stmt_expr_attributes) ]
3+ #![ feature( closure_track_caller) ]
4+ #![ feature( generator_trait) ]
5+ #![ feature( generators) ]
26
7+ use std:: ops:: { Generator , GeneratorState } ;
38use std:: panic:: Location ;
9+ use std:: pin:: Pin ;
10+
11+ type Loc = & ' static Location < ' static > ;
412
513#[ track_caller]
6- fn tracked ( ) -> & ' static Location < ' static > {
14+ fn tracked ( ) -> Loc {
715 Location :: caller ( ) // most importantly, we never get line 7
816}
917
10- fn nested_intrinsic ( ) -> & ' static Location < ' static > {
18+ fn nested_intrinsic ( ) -> Loc {
1119 Location :: caller ( )
1220}
1321
14- fn nested_tracked ( ) -> & ' static Location < ' static > {
22+ fn nested_tracked ( ) -> Loc {
1523 tracked ( )
1624}
1725
@@ -36,12 +44,12 @@ fn test_basic() {
3644
3745 let nested = nested_intrinsic ( ) ;
3846 assert_eq ! ( nested. file( ) , file!( ) ) ;
39- assert_eq ! ( nested. line( ) , 11 ) ;
47+ assert_eq ! ( nested. line( ) , 19 ) ;
4048 assert_eq ! ( nested. column( ) , 5 ) ;
4149
4250 let contained = nested_tracked ( ) ;
4351 assert_eq ! ( contained. file( ) , file!( ) ) ;
44- assert_eq ! ( contained. line( ) , 15 ) ;
52+ assert_eq ! ( contained. line( ) , 23 ) ;
4553 assert_eq ! ( contained. column( ) , 5 ) ;
4654
4755 // `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
@@ -125,9 +133,144 @@ fn test_trait_obj2() {
125133 assert_eq ! ( loc. line( ) , expected_line) ;
126134}
127135
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+ }
143+
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+ }
150+
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+ }
157+
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+ }
164+
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 ) ;
174+
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+ }
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+
128269fn main ( ) {
129270 test_basic ( ) ;
130271 test_fn_ptr ( ) ;
131272 test_trait_obj ( ) ;
132273 test_trait_obj2 ( ) ;
274+ test_closure ( ) ;
275+ test_generator ( ) ;
133276}
0 commit comments