Skip to content

Commit 63bf240

Browse files
committed
Auto merge of rust-lang#3040 - RalfJung:track_caller, r=RalfJung
add tests for track_caller in closures and generators taken from rust-lang#87064
2 parents 4989b2e + e24fdbe commit 63bf240

File tree

1 file changed

+180
-34
lines changed

1 file changed

+180
-34
lines changed

src/tools/miri/tests/pass/track-caller-attribute.rs

+180-34
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
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};
38
use 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

@@ -21,6 +29,44 @@ macro_rules! caller_location_from_macro {
2129
};
2230
}
2331

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+
2470
fn test_fn_ptr() {
2571
fn pass_to_ptr_call<T>(f: fn(T), x: T) {
2672
f(x);
@@ -87,44 +133,144 @@ fn test_trait_obj2() {
87133
assert_eq!(loc.line(), expected_line);
88134
}
89135

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+
}
96143

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+
}
102150

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+
}
107157

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+
}
112164

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);
120174

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+
}
126223

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();
127271
test_fn_ptr();
128272
test_trait_obj();
129273
test_trait_obj2();
274+
test_closure();
275+
test_generator();
130276
}

0 commit comments

Comments
 (0)