diff --git a/src/tools/miri/ci.sh b/src/tools/miri/ci.sh index 3146ef41a3bbd..a3fc68e6758ae 100755 --- a/src/tools/miri/ci.sh +++ b/src/tools/miri/ci.sh @@ -107,7 +107,7 @@ case $HOST_TARGET in MIRI_TEST_TARGET=i686-unknown-linux-gnu run_tests MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests MIRI_TEST_TARGET=aarch64-apple-darwin run_tests - MIRI_TEST_TARGET=i686-pc-windows-msvc run_tests + MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple atomic data_race env/var MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal hello integer vec panic/panic MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal no_std integer strings diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index d96e7040aaf73..f173cc37e8536 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -c40cfcf0494ff7506e753e750adb00eeea839f9c +dca2d1ff00bf96d244b1bb9a2117a92ec50ac71d diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 75c6911bcbeed..298c53675229b 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -272,26 +272,24 @@ pub fn report_error<'tcx, 'mir>( } else { let title = match e.kind() { UndefinedBehavior(UndefinedBehaviorInfo::ValidationError(validation_err)) - if matches!(validation_err.kind, ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer) => + if matches!( + validation_err.kind, + ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer + ) => { ecx.handle_ice(); // print interpreter backtrace bug!("This validation error should be impossible in Miri: {}", ecx.format_error(e)); } - UndefinedBehavior(_) => - "Undefined Behavior", - ResourceExhaustion(_) => - "resource exhaustion", + UndefinedBehavior(_) => "Undefined Behavior", + ResourceExhaustion(_) => "resource exhaustion", Unsupported( // We list only the ones that can actually happen. - UnsupportedOpInfo::Unsupported(_) | UnsupportedOpInfo::UnsizedLocal - ) => - "unsupported operation", + UnsupportedOpInfo::Unsupported(_) | UnsupportedOpInfo::UnsizedLocal, + ) => "unsupported operation", InvalidProgram( // We list only the ones that can actually happen. - InvalidProgramInfo::AlreadyReported(_) | - InvalidProgramInfo::Layout(..) - ) => - "post-monomorphization error", + InvalidProgramInfo::AlreadyReported(_) | InvalidProgramInfo::Layout(..), + ) => "post-monomorphization error", _ => { ecx.handle_ice(); // print interpreter backtrace bug!("This error should be impossible in Miri: {}", ecx.format_error(e)); @@ -346,7 +344,8 @@ pub fn report_error<'tcx, 'mir>( extra, "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:", range = access.bad, - ).unwrap(); + ) + .unwrap(); writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap(); } _ => {} diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 98f82ec9a0ff0..9d01a04bf4adb 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -975,7 +975,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { ecx.start_panic_nounwind(msg) } - fn unwind_terminate(ecx: &mut InterpCx<'mir, 'tcx, Self>, reason: mir::UnwindTerminateReason) -> InterpResult<'tcx> { + fn unwind_terminate( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + reason: mir::UnwindTerminateReason, + ) -> InterpResult<'tcx> { // Call the lang item. let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap(); let panic = ty::Instance::mono(ecx.tcx.tcx, panic); @@ -1410,17 +1413,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { ecx: &mut InterpCx<'mir, 'tcx, Self>, frame: usize, local: mir::Local, - mplace: &MPlaceTy<'tcx, Provenance> + mplace: &MPlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr.provenance else { panic!("after_local_allocated should only be called on fresh allocations"); }; let local_decl = &ecx.active_thread_stack()[frame].body.local_decls[local]; let span = local_decl.source_info.span; - ecx.machine - .allocation_spans - .borrow_mut() - .insert(alloc_id, (span, None)); + ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None)); Ok(()) } } diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs index 10bd5eb7211de..e6aff19b02d49 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs @@ -1,8 +1,8 @@ //@revisions: extern_block definition both //@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" -//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@normalize-stderr-test: "\n +at [^\n]+" -> "" //@[definition,both]error-in-other-file: aborted execution #![feature(rustc_attrs, c_unwind)] diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs index b8fa32c986f1c..6e0e0ca9f53e1 100644 --- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs +++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs @@ -1,7 +1,7 @@ //@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" -//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@normalize-stderr-test: "\n +at [^\n]+" -> "" //@error-in-other-file: aborted execution #![feature(never_type)] diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs index 70ae60593a1d3..0e8d3d08c1287 100644 --- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs +++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs @@ -1,7 +1,7 @@ //@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" -//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@normalize-stderr-test: "\n +at [^\n]+" -> "" //@error-in-other-file: aborted execution #[allow(deprecated, invalid_value)] diff --git a/src/tools/miri/tests/fail/panic/double_panic.rs b/src/tools/miri/tests/fail/panic/double_panic.rs index 4627e9f1c8e17..ddc75521ecad0 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.rs +++ b/src/tools/miri/tests/fail/panic/double_panic.rs @@ -1,7 +1,7 @@ //@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" -//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@normalize-stderr-test: "\n +at [^\n]+" -> "" //@error-in-other-file: aborted execution struct Foo; diff --git a/src/tools/miri/tests/fail/terminate-terminator.rs b/src/tools/miri/tests/fail/terminate-terminator.rs index 5373b5f70c07e..7c6728280305d 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.rs +++ b/src/tools/miri/tests/fail/terminate-terminator.rs @@ -1,8 +1,8 @@ //@compile-flags: -Zmir-opt-level=3 -Zinline-mir-hint-threshold=1000 //@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" -//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@normalize-stderr-test: "\n +at [^\n]+" -> "" //@error-in-other-file: aborted execution // Enable MIR inlining to ensure that `TerminatorKind::UnwindTerminate` is generated // instead of just `UnwindAction::Terminate`. diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.rs b/src/tools/miri/tests/fail/unwind-action-terminate.rs index 77844844483ee..86406872c5991 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.rs +++ b/src/tools/miri/tests/fail/unwind-action-terminate.rs @@ -1,8 +1,8 @@ //@error-in-other-file: aborted execution //@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" -//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@normalize-stderr-test: "\n +at [^\n]+" -> "" #![feature(c_unwind)] extern "C" fn panic_abort() { diff --git a/src/tools/miri/tests/pass/function_calls/abi_compat.rs b/src/tools/miri/tests/pass/function_calls/abi_compat.rs index 0786450f75117..dc1e1f0ba8d6c 100644 --- a/src/tools/miri/tests/pass/function_calls/abi_compat.rs +++ b/src/tools/miri/tests/pass/function_calls/abi_compat.rs @@ -1,53 +1,91 @@ #![feature(portable_simd)] -use std::num; use std::mem; +use std::num; use std::simd; -fn test_abi_compat(t: T, u: U) { - fn id(x: T) -> T { x } - +#[derive(Copy, Clone)] +struct Zst; + +fn test_abi_compat(t: T, u: U) { + fn id(x: T) -> T { + x + } + extern "C" fn id_c(x: T) -> T { + x + } + // This checks ABI compatibility both for arguments and return values, // in both directions. let f: fn(T) -> T = id; let f: fn(U) -> U = unsafe { std::mem::transmute(f) }; - drop(f(u)); - + let _val = f(u); let f: fn(U) -> U = id; let f: fn(T) -> T = unsafe { std::mem::transmute(f) }; - drop(f(t)); + let _val = f(t); + + // And then we do the same for `extern "C"`. + let f: extern "C" fn(T) -> T = id_c; + let f: extern "C" fn(U) -> U = unsafe { std::mem::transmute(f) }; + let _val = f(u); + let f: extern "C" fn(U) -> U = id_c; + let f: extern "C" fn(T) -> T = unsafe { std::mem::transmute(f) }; + let _val = f(t); } /// Ensure that `T` is compatible with various repr(transparent) wrappers around `T`. fn test_abi_newtype(t: T) { #[repr(transparent)] + #[derive(Copy, Clone)] struct Wrapper1(T); #[repr(transparent)] + #[derive(Copy, Clone)] struct Wrapper2(T, ()); #[repr(transparent)] + #[derive(Copy, Clone)] struct Wrapper2a((), T); #[repr(transparent)] - struct Wrapper3(T, [u8; 0]); + #[derive(Copy, Clone)] + struct Wrapper3(Zst, T, [u8; 0]); test_abi_compat(t, Wrapper1(t)); test_abi_compat(t, Wrapper2(t, ())); test_abi_compat(t, Wrapper2a((), t)); - test_abi_compat(t, Wrapper3(t, [])); + test_abi_compat(t, Wrapper3(Zst, t, [])); + test_abi_compat(t, mem::MaybeUninit::new(t)); // MaybeUninit is `repr(transparent)` } fn main() { + // Here we check: + // - unsigned vs signed integer is allowed + // - u32/i32 vs char is allowed + // - u32 vs NonZeroU32/Option is allowed + // - reference vs raw pointer is allowed + // - references to things of the same size and alignment are allowed + // These are very basic tests that should work on all ABIs. However it is not clear that any of + // these would be stably guaranteed. Code that relies on this is equivalent to code that relies + // on the layout of `repr(Rust)` types. They are also fragile: the same mismatches in the fields + // of a struct (even with `repr(C)`) will not always be accepted by Miri. + test_abi_compat(0u32, 0i32); + test_abi_compat(simd::u32x8::splat(1), simd::i32x8::splat(1)); test_abi_compat(0u32, 'x'); - test_abi_compat(&0u32, &([true; 4], [0u32; 0])); - test_abi_compat(0u32, mem::MaybeUninit::new(0u32)); + test_abi_compat(0i32, 'x'); test_abi_compat(42u32, num::NonZeroU32::new(1).unwrap()); test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap())); - test_abi_compat(0u32, 0i32); - test_abi_compat(simd::u32x8::splat(1), simd::i32x8::splat(1)); + test_abi_compat(&0u32, &0u32 as *const u32); + test_abi_compat(&0u32, &([true; 4], [0u32; 0])); // Note that `bool` and `u8` are *not* compatible, at least on x86-64! // One of them has `arg_ext: Zext`, the other does not. + // These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible + // with the wrapped field. + test_abi_newtype(()); + // FIXME: this still fails! test_abi_newtype(Zst); test_abi_newtype(0u32); test_abi_newtype(0f32); test_abi_newtype((0u32, 1u32, 2u32)); - test_abi_newtype([0u32, 1u32, 2u32]); - test_abi_newtype([0i32; 0]); + // FIXME: skipping the array tests on mips64 due to https://github.com/rust-lang/rust/issues/115404 + if !cfg!(target_arch = "mips64") { + test_abi_newtype([0u32, 1u32, 2u32]); + test_abi_newtype([0i32; 0]); + } } diff --git a/src/tools/miri/tests/pass/panic/catch_panic.rs b/src/tools/miri/tests/pass/panic/catch_panic.rs index 1b00f7cea305f..e4a7f8e3481eb 100644 --- a/src/tools/miri/tests/pass/panic/catch_panic.rs +++ b/src/tools/miri/tests/pass/panic/catch_panic.rs @@ -1,5 +1,5 @@ // We test the `align_offset` panic below, make sure we test the interpreter impl and not the "real" one. -//@compile-flags: -Zmiri-symbolic-alignment-check -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-symbolic-alignment-check #![feature(never_type)] #![allow(unconditional_panic, non_fmt_panics)] @@ -48,14 +48,16 @@ fn main() { })); // Std panics - test(None, |_old_val| std::panic!("Hello from panic: std")); - test(None, |old_val| std::panic::panic_any(format!("Hello from panic: {:?}", old_val))); - test(None, |old_val| std::panic!("Hello from panic: {:?}", old_val)); + test(None, |_old_val| std::panic!("Hello from std::panic")); + test(None, |old_val| std::panic!("Hello from std::panic: {:?}", old_val)); + test(None, |old_val| { + std::panic::panic_any(format!("Hello from std::panic_any: {:?}", old_val)) + }); test(None, |_old_val| std::panic::panic_any(1337)); // Core panics - test(None, |_old_val| core::panic!("Hello from panic: core")); - test(None, |old_val| core::panic!("Hello from panic: {:?}", old_val)); + test(None, |_old_val| core::panic!("Hello from core::panic")); + test(None, |old_val| core::panic!("Hello from core::panic: {:?}", old_val)); // Built-in panics; also make sure the message is right. test(Some("index out of bounds: the len is 3 but the index is 4"), |_old_val| { @@ -68,7 +70,7 @@ fn main() { }); test(Some("align_offset: align is not a power-of-two"), |_old_val| { - let _ = (0usize as *const u8).align_offset(3); + let _ = std::ptr::null::().align_offset(3); loop {} }); diff --git a/src/tools/miri/tests/pass/panic/catch_panic.stderr b/src/tools/miri/tests/pass/panic/catch_panic.stderr index aa005590db3ee..cbcd626e39fb4 100644 --- a/src/tools/miri/tests/pass/panic/catch_panic.stderr +++ b/src/tools/miri/tests/pass/panic/catch_panic.stderr @@ -1,22 +1,22 @@ thread 'main' panicked at $DIR/catch_panic.rs:LL:CC: -Hello from panic: std +Hello from std::panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -Caught panic message (&str): Hello from panic: std +Caught panic message (&str): Hello from std::panic thread 'main' panicked at $DIR/catch_panic.rs:LL:CC: -Hello from panic: 1 -Caught panic message (String): Hello from panic: 1 +Hello from std::panic: 1 +Caught panic message (String): Hello from std::panic: 1 thread 'main' panicked at $DIR/catch_panic.rs:LL:CC: -Hello from panic: 2 -Caught panic message (String): Hello from panic: 2 +Hello from std::panic_any: 2 +Caught panic message (String): Hello from std::panic_any: 2 thread 'main' panicked at $DIR/catch_panic.rs:LL:CC: Box Failed to get caught panic message. thread 'main' panicked at $DIR/catch_panic.rs:LL:CC: -Hello from panic: core -Caught panic message (&str): Hello from panic: core +Hello from core::panic +Caught panic message (&str): Hello from core::panic thread 'main' panicked at $DIR/catch_panic.rs:LL:CC: -Hello from panic: 5 -Caught panic message (String): Hello from panic: 5 +Hello from core::panic: 5 +Caught panic message (String): Hello from core::panic: 5 thread 'main' panicked at $DIR/catch_panic.rs:LL:CC: index out of bounds: the len is 3 but the index is 4 Caught panic message (String): index out of bounds: the len is 3 but the index is 4 diff --git a/src/tools/miri/tests/pass/panic/nested_panic_caught.rs b/src/tools/miri/tests/pass/panic/nested_panic_caught.rs index 884813150ad2a..bbfcc25a0c688 100644 --- a/src/tools/miri/tests/pass/panic/nested_panic_caught.rs +++ b/src/tools/miri/tests/pass/panic/nested_panic_caught.rs @@ -1,6 +1,6 @@ //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1" -//@normalize-stderr-test: "\n at [^\n]+" -> "$1" +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@normalize-stderr-test: "\n +at [^\n]+" -> "" // Checks that nested panics work correctly. diff --git a/src/tools/miri/tests/pass/track-caller-attribute.rs b/src/tools/miri/tests/pass/track-caller-attribute.rs index 6694764a234d4..1b0226e61b57c 100644 --- a/src/tools/miri/tests/pass/track-caller-attribute.rs +++ b/src/tools/miri/tests/pass/track-caller-attribute.rs @@ -1,17 +1,25 @@ #![feature(core_intrinsics)] +#![feature(stmt_expr_attributes)] +#![feature(closure_track_caller)] +#![feature(generator_trait)] +#![feature(generators)] +use std::ops::{Generator, GeneratorState}; use std::panic::Location; +use std::pin::Pin; + +type Loc = &'static Location<'static>; #[track_caller] -fn tracked() -> &'static Location<'static> { +fn tracked() -> Loc { Location::caller() // most importantly, we never get line 7 } -fn nested_intrinsic() -> &'static Location<'static> { +fn nested_intrinsic() -> Loc { Location::caller() } -fn nested_tracked() -> &'static Location<'static> { +fn nested_tracked() -> Loc { tracked() } @@ -21,6 +29,44 @@ macro_rules! caller_location_from_macro { }; } +fn test_basic() { + let location = Location::caller(); + let expected_line = line!() - 1; + assert_eq!(location.file(), file!()); + assert_eq!(location.line(), expected_line); + assert_eq!(location.column(), 20); + + let tracked = tracked(); + let expected_line = line!() - 1; + assert_eq!(tracked.file(), file!()); + assert_eq!(tracked.line(), expected_line); + assert_eq!(tracked.column(), 19); + + let nested = nested_intrinsic(); + assert_eq!(nested.file(), file!()); + assert_eq!(nested.line(), 19); + assert_eq!(nested.column(), 5); + + let contained = nested_tracked(); + assert_eq!(contained.file(), file!()); + assert_eq!(contained.line(), 23); + assert_eq!(contained.column(), 5); + + // `Location::caller()` in a macro should behave similarly to `file!` and `line!`, + // i.e. point to where the macro was invoked, instead of the macro itself. + let inmacro = caller_location_from_macro!(); + let expected_line = line!() - 1; + assert_eq!(inmacro.file(), file!()); + assert_eq!(inmacro.line(), expected_line); + assert_eq!(inmacro.column(), 19); + + let intrinsic = core::intrinsics::caller_location(); + let expected_line = line!() - 1; + assert_eq!(intrinsic.file(), file!()); + assert_eq!(intrinsic.line(), expected_line); + assert_eq!(intrinsic.column(), 21); +} + fn test_fn_ptr() { fn pass_to_ptr_call(f: fn(T), x: T) { f(x); @@ -87,44 +133,144 @@ fn test_trait_obj2() { assert_eq!(loc.line(), expected_line); } -fn main() { - let location = Location::caller(); - let expected_line = line!() - 1; - assert_eq!(location.file(), file!()); - assert_eq!(location.line(), expected_line); - assert_eq!(location.column(), 20); +fn test_closure() { + #[track_caller] + fn mono_invoke_fn (&'static str, bool, Loc)>( + val: &F, + ) -> (&'static str, bool, Loc) { + val("from_mono", false) + } - let tracked = tracked(); - let expected_line = line!() - 1; - assert_eq!(tracked.file(), file!()); - assert_eq!(tracked.line(), expected_line); - assert_eq!(tracked.column(), 19); + #[track_caller] + fn mono_invoke_fn_once (&'static str, bool, Loc)>( + val: F, + ) -> (&'static str, bool, Loc) { + val("from_mono", false) + } - let nested = nested_intrinsic(); - assert_eq!(nested.file(), file!()); - assert_eq!(nested.line(), 11); - assert_eq!(nested.column(), 5); + #[track_caller] + fn dyn_invoke_fn_mut( + val: &mut dyn FnMut(&'static str, bool) -> (&'static str, bool, Loc), + ) -> (&'static str, bool, Loc) { + val("from_dyn", false) + } - let contained = nested_tracked(); - assert_eq!(contained.file(), file!()); - assert_eq!(contained.line(), 15); - assert_eq!(contained.column(), 5); + #[track_caller] + fn dyn_invoke_fn_once( + val: Box (&'static str, bool, Loc)>, + ) -> (&'static str, bool, Loc) { + val("from_dyn", false) + } - // `Location::caller()` in a macro should behave similarly to `file!` and `line!`, - // i.e. point to where the macro was invoked, instead of the macro itself. - let inmacro = caller_location_from_macro!(); - let expected_line = line!() - 1; - assert_eq!(inmacro.file(), file!()); - assert_eq!(inmacro.line(), expected_line); - assert_eq!(inmacro.column(), 19); + let mut track_closure = #[track_caller] + |first: &'static str, second: bool| (first, second, Location::caller()); + let (first_arg, first_bool, first_loc) = track_closure("first_arg", true); + let first_line = line!() - 1; + assert_eq!(first_arg, "first_arg"); + assert_eq!(first_bool, true); + assert_eq!(first_loc.file(), file!()); + assert_eq!(first_loc.line(), first_line); + assert_eq!(first_loc.column(), 46); - let intrinsic = core::intrinsics::caller_location(); - let expected_line = line!() - 1; - assert_eq!(intrinsic.file(), file!()); - assert_eq!(intrinsic.line(), expected_line); - assert_eq!(intrinsic.column(), 21); + let (dyn_arg, dyn_bool, dyn_loc) = dyn_invoke_fn_mut(&mut track_closure); + assert_eq!(dyn_arg, "from_dyn"); + assert_eq!(dyn_bool, false); + // `FnMut::call_mut` does not have `#[track_caller]`, + // so this will not match + assert_ne!(dyn_loc.file(), file!()); + + let (dyn_arg, dyn_bool, dyn_loc) = dyn_invoke_fn_once(Box::new(track_closure)); + assert_eq!(dyn_arg, "from_dyn"); + assert_eq!(dyn_bool, false); + // `FnOnce::call_once` does not have `#[track_caller]` + // so this will not match + assert_ne!(dyn_loc.file(), file!()); + + let (mono_arg, mono_bool, mono_loc) = mono_invoke_fn(&track_closure); + let mono_line = line!() - 1; + assert_eq!(mono_arg, "from_mono"); + assert_eq!(mono_bool, false); + assert_eq!(mono_loc.file(), file!()); + assert_eq!(mono_loc.line(), mono_line); + assert_eq!(mono_loc.column(), 43); + + let (mono_arg, mono_bool, mono_loc) = mono_invoke_fn_once(track_closure); + let mono_line = line!() - 1; + assert_eq!(mono_arg, "from_mono"); + assert_eq!(mono_bool, false); + assert_eq!(mono_loc.file(), file!()); + assert_eq!(mono_loc.line(), mono_line); + assert_eq!(mono_loc.column(), 43); + + let non_tracked_caller = || Location::caller(); + let non_tracked_line = line!() - 1; // This is the line of the closure, not its caller + let non_tracked_loc = non_tracked_caller(); + assert_eq!(non_tracked_loc.file(), file!()); + assert_eq!(non_tracked_loc.line(), non_tracked_line); + assert_eq!(non_tracked_loc.column(), 33); +} + +fn test_generator() { + #[track_caller] + fn mono_generator>( + val: Pin<&mut F>, + ) -> (&'static str, String, Loc) { + match val.resume("Mono".to_string()) { + GeneratorState::Yielded(val) => val, + _ => unreachable!(), + } + } + #[track_caller] + fn dyn_generator( + val: Pin<&mut dyn Generator>, + ) -> (&'static str, String, Loc) { + match val.resume("Dyn".to_string()) { + GeneratorState::Yielded(val) => val, + _ => unreachable!(), + } + } + + #[rustfmt::skip] + let generator = #[track_caller] |arg: String| { + yield ("first", arg.clone(), Location::caller()); + yield ("second", arg.clone(), Location::caller()); + }; + + let mut pinned = Box::pin(generator); + let (dyn_ret, dyn_arg, dyn_loc) = dyn_generator(pinned.as_mut()); + assert_eq!(dyn_ret, "first"); + assert_eq!(dyn_arg, "Dyn".to_string()); + // The `Generator` trait does not have `#[track_caller]` on `resume`, so + // this will not match. + assert_ne!(dyn_loc.file(), file!()); + + let (mono_ret, mono_arg, mono_loc) = mono_generator(pinned.as_mut()); + let mono_line = line!() - 1; + assert_eq!(mono_ret, "second"); + // The generator ignores the argument to the second `resume` call + assert_eq!(mono_arg, "Dyn".to_string()); + assert_eq!(mono_loc.file(), file!()); + assert_eq!(mono_loc.line(), mono_line); + assert_eq!(mono_loc.column(), 42); + + #[rustfmt::skip] + let non_tracked_generator = || { yield Location::caller(); }; + let non_tracked_line = line!() - 1; // This is the line of the generator, not its caller + let non_tracked_loc = match Box::pin(non_tracked_generator).as_mut().resume(()) { + GeneratorState::Yielded(val) => val, + _ => unreachable!(), + }; + assert_eq!(non_tracked_loc.file(), file!()); + assert_eq!(non_tracked_loc.line(), non_tracked_line); + assert_eq!(non_tracked_loc.column(), 44); +} + +fn main() { + test_basic(); test_fn_ptr(); test_trait_obj(); test_trait_obj2(); + test_closure(); + test_generator(); }