From a9e56e4b7aaadf9095032ddd410a8b9a35e2d744 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Dec 2025 15:23:19 +0100 Subject: [PATCH 01/26] fix empty stack in deadlock error message --- src/tools/miri/src/concurrency/thread.rs | 2 +- src/tools/miri/src/diagnostics.rs | 76 ++++++++++--------- src/tools/miri/src/shims/unix/sync.rs | 2 +- .../libc_pthread_mutex_deadlock.stderr | 14 ++-- .../libc_pthread_mutex_normal_reentrant.rs | 2 +- ...libc_pthread_mutex_normal_reentrant.stderr | 2 +- ...k_read_write_deadlock_single_thread.stderr | 2 +- ..._pthread_rwlock_write_read_deadlock.stderr | 14 ++-- ...k_write_read_deadlock_single_thread.stderr | 2 +- ...pthread_rwlock_write_write_deadlock.stderr | 14 ++-- ..._write_write_deadlock_single_thread.stderr | 2 +- .../concurrency/windows_join_main.stderr | 18 ++--- .../concurrency/windows_join_self.stderr | 14 ++-- .../libc/eventfd_block_read_twice.stderr | 4 +- .../libc/eventfd_block_write_twice.stderr | 4 +- .../libc/fcntl_fsetfl_while_blocking.rs | 1 - .../libc/fcntl_fsetfl_while_blocking.stderr | 9 +-- .../libc/libc_epoll_block_two_thread.stderr | 4 +- .../socketpair-close-while-blocked.stderr | 4 +- .../libc/socketpair_block_read_twice.stderr | 4 +- .../libc/socketpair_block_write_twice.stderr | 4 +- .../concurrency/mutex-leak-move-deadlock.rs | 3 + .../mutex-leak-move-deadlock.stderr | 2 +- 23 files changed, 101 insertions(+), 102 deletions(-) diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index c7ae335d04795..feed70cb18fd3 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -823,7 +823,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // sleep until the first callback. interp_ok(SchedulingAction::Sleep(sleep_time)) } else { - throw_machine_stop!(TerminationInfo::Deadlock); + throw_machine_stop!(TerminationInfo::GlobalDeadlock); } } } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 8e252d306b29b..a5505836da99f 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -32,7 +32,10 @@ pub enum TerminationInfo { history: tree_diagnostics::HistoryData, }, Int2PtrWithStrictProvenance, - Deadlock, + /// All threads are blocked. + GlobalDeadlock, + /// Some thread discovered a deadlock condition (e.g. in a mutex with reentrancy checking). + LocalDeadlock, MultipleSymbolDefinitions { link_name: Symbol, first: SpanData, @@ -76,7 +79,8 @@ impl fmt::Display for TerminationInfo { ), StackedBorrowsUb { msg, .. } => write!(f, "{msg}"), TreeBorrowsUb { title, .. } => write!(f, "{title}"), - Deadlock => write!(f, "the evaluated program deadlocked"), + GlobalDeadlock => write!(f, "the evaluated program deadlocked"), + LocalDeadlock => write!(f, "a thread deadlocked"), MultipleSymbolDefinitions { link_name, .. } => write!(f, "multiple definitions of symbol `{link_name}`"), SymbolShimClashing { link_name, .. } => @@ -245,10 +249,39 @@ pub fn report_result<'tcx>( Some("unsupported operation"), StackedBorrowsUb { .. } | TreeBorrowsUb { .. } | DataRace { .. } => Some("Undefined Behavior"), - Deadlock => { + LocalDeadlock => { labels.push(format!("this thread got stuck here")); None } + GlobalDeadlock => { + // Global deadlocks are reported differently: just show all blocked threads. + // The "active" thread might actually be terminated, so we ignore it. + let mut any_pruned = false; + for (thread, stack) in ecx.machine.threads.all_blocked_stacks() { + let stacktrace = Frame::generate_stacktrace_from_stack(stack); + let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); + any_pruned |= was_pruned; + report_msg( + DiagLevel::Error, + format!("the evaluated program deadlocked"), + vec![format!( + "thread `{}` got stuck here", + ecx.machine.threads.get_thread_display_name(thread) + )], + vec![], + vec![], + &stacktrace, + Some(thread), + &ecx.machine, + ) + } + if any_pruned { + ecx.tcx.dcx().note( + "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace" + ); + } + return None; + } MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None, }; #[rustfmt::skip] @@ -408,9 +441,7 @@ pub fn report_result<'tcx>( }; let stacktrace = ecx.generate_stacktrace(); - let (stacktrace, mut any_pruned) = prune_stacktrace(stacktrace, &ecx.machine); - - let mut show_all_threads = false; + let (stacktrace, pruned) = prune_stacktrace(stacktrace, &ecx.machine); // We want to dump the allocation if this is `InvalidUninitBytes`. // Since `format_interp_error` consumes `e`, we compute the outut early. @@ -425,15 +456,6 @@ pub fn report_result<'tcx>( .unwrap(); writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap(); } - MachineStop(info) => { - let info = info.downcast_ref::().expect("invalid MachineStop payload"); - match info { - TerminationInfo::Deadlock => { - show_all_threads = true; - } - _ => {} - } - } _ => {} } @@ -460,28 +482,8 @@ pub fn report_result<'tcx>( eprint!("{extra}"); // newlines are already in the string - if show_all_threads { - for (thread, stack) in ecx.machine.threads.all_blocked_stacks() { - if thread != ecx.active_thread() { - let stacktrace = Frame::generate_stacktrace_from_stack(stack); - let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); - any_pruned |= was_pruned; - report_msg( - DiagLevel::Error, - format!("the evaluated program deadlocked"), - vec![format!("this thread got stuck here")], - vec![], - vec![], - &stacktrace, - Some(thread), - &ecx.machine, - ) - } - } - } - // Include a note like `std` does when we omit frames from a backtrace - if any_pruned { + if pruned { ecx.tcx.dcx().note( "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace", ); @@ -563,7 +565,7 @@ pub fn report_msg<'tcx>( err.span(span); // Show main message. - if span != DUMMY_SP { + if !span.is_dummy() { for line in span_msg { err.span_label(span, line); } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 39014f34c8972..39ad66041820f 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -538,7 +538,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_ub_format!( "trying to acquire default mutex already locked by the current thread" ), - MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock), + MutexKind::Normal => throw_machine_stop!(TerminationInfo::LocalDeadlock), MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"), MutexKind::Recursive => { this.mutex_lock(&mutex.mutex_ref)?; diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr index c80cbf835a386..e3b0036c9aa9b 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr @@ -1,14 +1,8 @@ -error: the evaluated program deadlocked - --> tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC - | -LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0); - | ^ this thread got stuck here - error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -23,6 +17,12 @@ LL | | }) LL | | .join() | |_______________^ +error: the evaluated program deadlocked + --> tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC + | +LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0); + | ^ thread `unnamed-ID` got stuck here + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs index b88257992a5c5..3008705ebfdea 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs @@ -12,6 +12,6 @@ fn main() { assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); // A "normal" mutex properly tries to acquire the lock even if its is already held // by the current thread -- and then we deadlock. - libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: the evaluated program deadlocked + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: a thread deadlocked } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr index c611d0ff7b006..782322d5c32b3 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr @@ -1,4 +1,4 @@ -error: the evaluated program deadlocked +error: a thread deadlocked --> tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs:LL:CC | LL | libc::pthread_mutex_lock(&mut mutex as *mut _); diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr index 763a52963a4d5..ea8cbcada970d 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs:LL:CC | LL | libc::pthread_rwlock_wrlock(rw.get()); - | ^ this thread got stuck here + | ^ thread `main` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr index f174d387f76e3..255632870efc8 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr @@ -1,14 +1,8 @@ -error: the evaluated program deadlocked - --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC - | -LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); - | ^ this thread got stuck here - error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -23,6 +17,12 @@ LL | | }) LL | | .join() | |_______________^ +error: the evaluated program deadlocked + --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC + | +LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); + | ^ thread `unnamed-ID` got stuck here + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr index 4c8d3fddb97c2..0208a5bae4f52 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs:LL:CC | LL | libc::pthread_rwlock_rdlock(rw.get()); - | ^ this thread got stuck here + | ^ thread `main` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr index 06b8e1246e016..6891b989d05f8 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr @@ -1,14 +1,8 @@ -error: the evaluated program deadlocked - --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC - | -LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); - | ^ this thread got stuck here - error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -23,6 +17,12 @@ LL | | }) LL | | .join() | |_______________^ +error: the evaluated program deadlocked + --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC + | +LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); + | ^ thread `unnamed-ID` got stuck here + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr index e6a4b2814d83c..314e60b023607 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs:LL:CC | LL | libc::pthread_rwlock_wrlock(rw.get()); - | ^ this thread got stuck here + | ^ thread `main` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr index 0ab89676db370..d9cc93d0fc495 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr @@ -1,16 +1,8 @@ -error: the evaluated program deadlocked - --> tests/fail-dep/concurrency/windows_join_main.rs:LL:CC - | -LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this thread got stuck here - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) - error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -26,6 +18,14 @@ LL | | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_O LL | | .join() | |___________^ +error: the evaluated program deadlocked + --> tests/fail-dep/concurrency/windows_join_main.rs:LL:CC + | +LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ thread `unnamed-ID` got stuck here + | + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr index bdfab966d6d98..f5515983da2b4 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr @@ -1,14 +1,8 @@ -error: the evaluated program deadlocked - --> tests/fail-dep/concurrency/windows_join_self.rs:LL:CC - | -LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0); - | ^ this thread got stuck here - error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -26,6 +20,12 @@ LL | | }) LL | | .join() | |___________^ +error: the evaluated program deadlocked + --> tests/fail-dep/concurrency/windows_join_self.rs:LL:CC + | +LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0); + | ^ thread `unnamed-ID` got stuck here + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr index 6253fe6d2c768..53ae7ea82bd95 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/eventfd_block_read_twice.rs:LL:CC | LL | let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() }; - | ^ this thread got stuck here + | ^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr index aecc54c2fd882..62810f17be887 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/eventfd_block_write_twice.rs:LL:CC | LL | libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap() - | ^ this thread got stuck here + | ^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs index cfebb7c643512..c7a2c5b4d2973 100644 --- a/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs +++ b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # Sockets/pipes are not implemented yet -//~^ ERROR: the evaluated program deadlocked //@compile-flags: -Zmiri-deterministic-concurrency use std::thread; diff --git a/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr index e451fdb9d19f2..307762fb12c63 100644 --- a/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr +++ b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr @@ -1,15 +1,10 @@ -error: the evaluated program deadlocked - | - = note: this thread got stuck here - = note: (no span available) - error: the evaluated program deadlocked --> tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs:LL:CC | LL | let _res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr index 14390d632738d..dba12d1903170 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC | LL | check_epoll_wait::(epfd, &expected, -1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this thread got stuck here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr index 97b5df7fb17ff..a7c660e1adcc3 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/socketpair-close-while-blocked.rs:LL:CC | LL | libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - | ^ this thread got stuck here + | ^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr index 38db735e9b4d4..faab75f7840b6 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC | LL | libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - | ^ this thread got stuck here + | ^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr index a2f1c67b5efc8..9f95d98beb6f0 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC | LL | let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) }; - | ^ this thread got stuck here + | ^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs index b996fcaf45df1..9c73f6c03edd4 100644 --- a/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs +++ b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs @@ -3,6 +3,9 @@ //@normalize-stderr-test: "LL \| .*" -> "LL | $$CODE" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n *= note:.*" -> "" +// On macOS we use chekced pthread mutexes which changes the error +//@normalize-stderr-test: "this thread got stuck here" -> "thread `main` got stuck here" +//@normalize-stderr-test: "a thread deadlocked" -> "the evaluated program deadlocked" use std::mem; use std::sync::Mutex; diff --git a/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr index cc487c62d08d2..7784132a54ce8 100644 --- a/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr +++ b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/$FILE:LL:CC | LL | $CODE - | ^ this thread got stuck here + | ^ thread `main` got stuck here | note: inside `main` --> tests/fail/concurrency/mutex-leak-move-deadlock.rs:LL:CC From ef33430266a91e8260cbac58b30811e0cb96df2c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 7 Dec 2025 10:57:13 +0100 Subject: [PATCH 02/26] run ptr_int tests with Tree Borrows --- src/tools/miri/tests/pass/ptr_int_casts.rs | 2 + .../miri/tests/pass/ptr_int_casts.tree.stderr | 89 ------------------- .../miri/tests/pass/ptr_int_from_exposed.rs | 2 + .../pass/ptr_int_from_exposed.tree.stderr | 19 ---- 4 files changed, 4 insertions(+), 108 deletions(-) delete mode 100644 src/tools/miri/tests/pass/ptr_int_casts.tree.stderr delete mode 100644 src/tools/miri/tests/pass/ptr_int_from_exposed.tree.stderr diff --git a/src/tools/miri/tests/pass/ptr_int_casts.rs b/src/tools/miri/tests/pass/ptr_int_casts.rs index 94391eac3b274..2d99a8a449b32 100644 --- a/src/tools/miri/tests/pass/ptr_int_casts.rs +++ b/src/tools/miri/tests/pass/ptr_int_casts.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-permissive-provenance +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows use std::{mem, ptr}; fn eq_ref(x: &T, y: &T) -> bool { diff --git a/src/tools/miri/tests/pass/ptr_int_casts.tree.stderr b/src/tools/miri/tests/pass/ptr_int_casts.tree.stderr deleted file mode 100644 index 21c5b2b0dfe8f..0000000000000 --- a/src/tools/miri/tests/pass/ptr_int_casts.tree.stderr +++ /dev/null @@ -1,89 +0,0 @@ -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | assert_eq!(1 as *const i32 as usize, 1); - | ^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = help: this program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program - = help: see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation - = help: to ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead - = help: you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics - = help: Tree Borrows does not support integer-to-pointer casts, so the program is likely to go wrong when this pointer gets used - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | assert_eq!((1 as *const i32).wrapping_offset(4) as usize, 1 + 4 * 4); - | ^^^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | *val = (1 as *const u8).wrapping_offset(-4); - | ^^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | let y = y as *const _; - | ^^^^^^^^^^^^^ integer-to-pointer cast - | - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | let x: fn() -> i32 = unsafe { mem::transmute(y as *mut u8) }; - | ^^^^^^^^^^^^ integer-to-pointer cast - | - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | assert_eq!((-1i32) as usize as *const i32 as usize, (-1i32) as usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs index 98f8f15608e1d..200e4b2ea3e94 100644 --- a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs +++ b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-permissive-provenance +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows use std::ptr; diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.tree.stderr b/src/tools/miri/tests/pass/ptr_int_from_exposed.tree.stderr deleted file mode 100644 index aac6d0f48d015..0000000000000 --- a/src/tools/miri/tests/pass/ptr_int_from_exposed.tree.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning: integer-to-pointer cast - --> tests/pass/ptr_int_from_exposed.rs:LL:CC - | -LL | let ptr = ptr::with_exposed_provenance::(x_usize).wrapping_offset(-128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = help: this program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program - = help: see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation - = help: to ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead - = help: you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics - = help: Tree Borrows does not support integer-to-pointer casts, so the program is likely to go wrong when this pointer gets used - = note: BACKTRACE: - = note: inside `ptr_roundtrip_out_of_bounds` at tests/pass/ptr_int_from_exposed.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_from_exposed.rs:LL:CC - | -LL | ptr_roundtrip_out_of_bounds(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - From 8a99d48f84aed46c118395548bd652c9f72ffd11 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 7 Dec 2025 12:10:38 +0100 Subject: [PATCH 03/26] genmc: suppress compare_exchange warnings from dependencies --- src/tools/miri/doc/genmc.md | 47 +++--- .../miri/src/concurrency/genmc/helper.rs | 7 +- src/tools/miri/src/concurrency/genmc/mod.rs | 18 ++- .../shims/mutex_diff_thread_unlock.stderr | 62 +------- .../tests/genmc/pass/atomics/cas_simple.rs | 5 + .../genmc/pass/atomics/cas_simple.stderr | 18 +++ .../genmc/pass/std/arc.check_count.stderr | 124 --------------- src/tools/miri/tests/genmc/pass/std/arc.rs | 1 - .../genmc/pass/std/arc.try_upgrade.stderr | 150 ------------------ .../miri/tests/genmc/pass/std/empty_main.rs | 1 - .../tests/genmc/pass/std/empty_main.stderr | 43 ----- .../tests/genmc/pass/std/spawn_std_threads.rs | 1 - .../genmc/pass/std/spawn_std_threads.stderr | 128 --------------- .../tests/genmc/pass/std/thread_locals.rs | 1 - .../tests/genmc/pass/std/thread_locals.stderr | 111 ------------- 15 files changed, 62 insertions(+), 655 deletions(-) diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 7da7a3d189487..e9d4849bc5960 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -1,7 +1,6 @@ # **(WIP)** Documentation for Miri-GenMC -**NOTE: GenMC mode is not yet fully implemented, and has [several correctness issues](https://github.com/rust-lang/miri/issues/4572). Using GenMC mode currently requires manually compiling Miri, see [Usage](#usage).** - +**NOTE: GenMC mode is not yet fully implemented, and has [several correctness issues](https://github.com/rust-lang/miri/issues/4572) and [other limitations](#limitations). Using GenMC mode currently requires manually compiling Miri, see [Usage](#usage).** [GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program. Miri-GenMC integrates that model checker into Miri. @@ -12,11 +11,14 @@ This includes all possible thread interleavings and all allowed return values fo It is hence still possible to have latent bugs in a test case even if they passed GenMC.) GenMC requires the input program to be bounded, i.e., have finitely many possible executions, otherwise it will not terminate. -Any loops that may run infinitely must be replaced or bounded (see below). +Any loops that may run infinitely must be replaced or bounded (see [below](#eliminating-unbounded-loops)). GenMC makes use of Dynamic Partial Order Reduction (DPOR) to reduce the number of executions that must be explored, but the runtime can still be super-exponential in the size of the input program (number of threads and amount of interaction between threads). Large programs may not be verifiable in a reasonable amount of time. +GenMC currently only supports Linux hosts. +Both the host and the target must be 64-bit little-endian. + ## Usage For testing/developing Miri-GenMC: @@ -50,16 +52,24 @@ Note that `cargo miri test` in GenMC mode is currently not supported. - `debug2`: Print the execution graph after every memory access. - `debug3`: Print reads-from values considered by GenMC. - `-Zmiri-genmc-verbose`: Show more information, such as estimated number of executions, and time taken for verification. - -#### Regular Miri parameters useful for GenMC mode - - `-Zmiri-disable-weak-memory-emulation`: Disable any weak memory effects (effectively upgrading all atomic orderings in the program to `SeqCst`). This option may reduce the number of explored program executions, but any bugs related to weak memory effects will be missed. This option can help determine if an error is caused by weak memory effects (i.e., if it disappears with this option enabled). -## Tips +## Limitations + +There are several limitations which can make GenMC miss bugs: +- GenMC does not support re-using freed memory for new allocations, so any bugs related to that will be missed. +- GenMC does not support `compare_exchange_weak`, so the consequences of spurious failures are not explored. + A warning will be emitted if this affects code you wrote (but not if it happens inside your dependencies). +- GenMC does not support the separate failure ordering of `compare_exchange`. Miri will take the maximum of the success and failure ordering and use that for the access; outcomes that rely on the real ordering being weaker will not be explored. + A warning will be emitted if this affects code you wrote (but not if it happens inside your dependencies). +- GenMC is incompatible with borrow tracking (Stacked/Tree Borrows). You need to set `-Zmiri-disable-stacked-borrows` to use GenMC. +- Like all C++ memory model verification tools, GenMC has to solve the [out-of-thin-air problem](https://www.cl.cam.ac.uk/~pes20/cpp/notes42.html). + It takes the [usual approach](https://plv.mpi-sws.org/scfix/paper.pdf) of requiring the union of "program-order" and "reads-from" to be acyclic. + This means it excludes certain behaviors allowed by the C++ memory model, some of which can occur on hardware that performs load buffering. - +## Tips ### Eliminating unbounded loops @@ -121,24 +131,11 @@ fn count_until_true_genmc(flag: &AtomicBool) -> u64 { -## Limitations - -Some or all of these limitations might get removed in the future: - -- Borrow tracking is currently incompatible (stacked/tree borrows). -- Only Linux is supported for now. -- No support for 32-bit or big-endian targets. -- No cross-target interpretation. - - - ## Development GenMC is written in C++, which complicates development a bit. The prerequisites for building Miri-GenMC are: -- A compiler with C++23 support. -- LLVM developments headers and clang. - +- A compiler with sufficient C++20 support (we are testing GCC 13). The actual code for GenMC is not contained in the Miri repo itself, but in a [separate GenMC repo](https://github.com/MPI-SWS/genmc) (with its own maintainers). These sources need to be available to build Miri-GenMC. @@ -149,6 +146,8 @@ The process for obtaining them is as follows: If you place this directory inside the Miri folder, it is recommended to call it `genmc-src` as that tells `./miri fmt` to avoid formatting the Rust files inside that folder. + + ### Formatting the C++ code For formatting the C++ code we provide a `.clang-format` file in the `genmc-sys` directory. @@ -157,7 +156,3 @@ With `clang-format` installed, run this command to format the c++ files (replace find ./genmc-sys/cpp/ -name "*.cpp" -o -name "*.hpp" | xargs clang-format --style=file:"./genmc-sys/.clang-format" -i ``` NOTE: this is currently not done automatically on pull requests to Miri. - - - - diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index ae16898106d86..f539e783fd3f5 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -171,11 +171,8 @@ impl AtomicRwOrd { (AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed) => AtomicRwOrd::Acquire, (AtomicReadOrd::Relaxed, AtomicWriteOrd::Release) => AtomicRwOrd::Release, (AtomicReadOrd::Acquire, AtomicWriteOrd::Release) => AtomicRwOrd::AcqRel, - (AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst) => AtomicRwOrd::SeqCst, - _ => - panic!( - "Unsupported memory ordering combination ({read_ordering:?}, {write_ordering:?})" - ), + (AtomicReadOrd::SeqCst, _) => AtomicRwOrd::SeqCst, + (_, AtomicWriteOrd::SeqCst) => AtomicRwOrd::SeqCst, } } diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 73da0e11daaf7..36b1d2afade4f 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -402,8 +402,20 @@ impl GenmcCtx { // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. let (effective_failure_ordering, _) = upgraded_success_ordering.split_memory_orderings(); - // Return a warning if the actual orderings don't match the upgraded ones. - if success != upgraded_success_ordering || effective_failure_ordering != fail { + + // Return a warning if we cannot explore all behaviors of this operation. + // Only emit this if the operation is "in user code": walk up across `#[track_caller]` + // frames, then check if the next frame is local. + let show_warning = || { + ecx.active_thread_stack() + .iter() + .rev() + .find(|f| !f.instance().def.requires_caller_location(*ecx.tcx)) + .is_none_or(|f| ecx.machine.is_local(f.instance())) + }; + if (success != upgraded_success_ordering || effective_failure_ordering != fail) + && show_warning() + { static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); ecx.dedup_diagnostic(&DEDUP, |_first| { NonHaltingDiagnostic::GenmcCompareExchangeOrderingMismatch { @@ -415,7 +427,7 @@ impl GenmcCtx { }); } // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`. - if can_fail_spuriously { + if can_fail_spuriously && show_warning() { static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); ecx.dedup_diagnostic(&DEDUP, |_first| NonHaltingDiagnostic::GenmcCompareExchangeWeak); } diff --git a/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr index e2148bedd3187..db465969cda81 100644 --- a/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr +++ b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr @@ -1,64 +1,4 @@ Running GenMC Verification... -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `miri_start` - --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -LL | | let guard = guard; // avoid field capturing -LL | | drop(guard); -LL | | }); - | |______^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `miri_start` - --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -LL | | let guard = guard; // avoid field capturing -LL | | drop(guard); -LL | | }); - | |______^ - error: Undefined Behavior: Invalid unlock() operation --> RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC | @@ -82,5 +22,5 @@ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report -error: aborting due to 1 previous error; 2 warnings emitted +error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs index e32c7cdf80c43..c19c81995d1bc 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs @@ -30,5 +30,10 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { if 2 != VALUE.load(SeqCst) { std::process::abort() } + + // Check that we emit warnings for cases that are not fully supported. + let _ = VALUE.compare_exchange(99, 99, SeqCst, Relaxed); + let _ = VALUE.compare_exchange_weak(99, 99, Relaxed, SeqCst); + 0 } diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr index 7867be2dbe8ed..4351b312c75dc 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr @@ -1,2 +1,20 @@ Running GenMC Verification... +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'SeqCst', the failure ordering 'Relaxed' is treated like 'SeqCst'. Miri with GenMC might miss bugs related to this memory access. + --> tests/genmc/pass/atomics/cas_simple.rs:LL:CC + | +LL | let _ = VALUE.compare_exchange(99, 99, SeqCst, Relaxed); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Success ordering 'Relaxed' was upgraded to 'SeqCst' to match failure ordering 'SeqCst'. Miri with GenMC might miss bugs related to this memory access. + --> tests/genmc/pass/atomics/cas_simple.rs:LL:CC + | +LL | let _ = VALUE.compare_exchange_weak(99, 99, Relaxed, SeqCst); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> tests/genmc/pass/atomics/cas_simple.rs:LL:CC + | +LL | let _ = VALUE.compare_exchange_weak(99, 99, Relaxed, SeqCst); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr index fdbb9eff2faa6..a67635dee1bef 100644 --- a/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr +++ b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr @@ -1,126 +1,2 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/id.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let weak = Arc::downgrade(&data_clone); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let weak = Arc::downgrade(&data_clone); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -... | -LL | | }); - | |______^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -... | -LL | | }); - | |______^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | handle.join().unwrap(); - | ^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sync/once.rs:LL:CC - | -LL | self.inner.call(true, &mut |p| f.take().unwrap()(p)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sync/once.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/arc.rs b/src/tools/miri/tests/genmc/pass/std/arc.rs index dee29127856df..addf6408c006f 100644 --- a/src/tools/miri/tests/genmc/pass/std/arc.rs +++ b/src/tools/miri/tests/genmc/pass/std/arc.rs @@ -1,6 +1,5 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: check_count try_upgrade -//@normalize-stderr-test: "\n *= note: inside `std::.*" -> "" // Check that various operations on `std::sync::Arc` are handled properly in GenMC mode. // diff --git a/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr index a5423f9a398bb..f527b61202327 100644 --- a/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr +++ b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr @@ -1,152 +1,2 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/id.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let weak = Arc::downgrade(&data_clone); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let weak = Arc::downgrade(&data_clone); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -... | -LL | | }); - | |______^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -... | -LL | | }); - | |______^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | handle.join().unwrap(); - | ^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sync/once.rs:LL:CC - | -LL | self.inner.call(true, &mut |p| f.take().unwrap()(p)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sync/once.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if self.inner()?.strong.fetch_update(Acquire, Relaxed, checked_increment).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE on thread `unnamed-ID`: -note: inside closure - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | if let Some(strong) = weak_.upgrade() { - | ^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if self.inner()?.strong.fetch_update(Acquire, Relaxed, checked_increment).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE on thread `unnamed-ID`: -note: inside closure - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | if let Some(strong) = weak_.upgrade() { - | ^^^^^^^^^^^^^^^ - Verification complete with 7 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/empty_main.rs b/src/tools/miri/tests/genmc/pass/std/empty_main.rs index f0e4155ccd5dd..2ffc3388fb36c 100644 --- a/src/tools/miri/tests/genmc/pass/std/empty_main.rs +++ b/src/tools/miri/tests/genmc/pass/std/empty_main.rs @@ -1,5 +1,4 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@normalize-stderr-test: "\n *= note: inside `std::.*" -> "" // A lot of code runs before main, which we should be able to handle in GenMC mode. diff --git a/src/tools/miri/tests/genmc/pass/std/empty_main.stderr b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr index de07943b0d8d1..7867be2dbe8ed 100644 --- a/src/tools/miri/tests/genmc/pass/std/empty_main.stderr +++ b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr @@ -1,45 +1,2 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/id.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sync/once.rs:LL:CC - | -LL | self.inner.call(true, &mut |p| f.take().unwrap()(p)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sync/once.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs index e32979dc2b510..dadbee47b9860 100644 --- a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs +++ b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs @@ -1,5 +1,4 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@normalize-stderr-test: "\n *= note: inside `std::.*" -> "" // We should be able to spawn and join standard library threads in GenMC mode. // Since these threads do nothing, we should only explore 1 program execution. diff --git a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr index 701934a7cd7f1..7867be2dbe8ed 100644 --- a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr +++ b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr @@ -1,130 +1,2 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/id.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside closure - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: inside closure at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC - = note: inside ` as std::iter::Iterator>::fold::<(), {closure@std::iter::adapters::map::map_fold, (), {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}, {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::for_each::<{closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside `> as std::vec::spec_extend::SpecExtend, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::spec_extend` at RUSTLIB/alloc/src/vec/spec_extend.rs:LL:CC - = note: inside `> as std::vec::spec_from_iter_nested::SpecFromIterNested, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter_nested.rs:LL:CC - = note: inside `> as std::vec::spec_from_iter::SpecFromIter, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter.rs:LL:CC - = note: inside `> as std::iter::FromIterator>>::from_iter::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::collect::>>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside closure - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: inside closure at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC - = note: inside ` as std::iter::Iterator>::fold::<(), {closure@std::iter::adapters::map::map_fold, (), {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}, {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::for_each::<{closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside `> as std::vec::spec_extend::SpecExtend, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::spec_extend` at RUSTLIB/alloc/src/vec/spec_extend.rs:LL:CC - = note: inside `> as std::vec::spec_from_iter_nested::SpecFromIterNested, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter_nested.rs:LL:CC - = note: inside `> as std::vec::spec_from_iter::SpecFromIter, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter.rs:LL:CC - = note: inside `> as std::iter::FromIterator>>::from_iter::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::collect::>>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside closure - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); - | ^^^^^^^^^^^^^ - = note: inside closure at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside `> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>::{closure#0}}>` at RUSTLIB/alloc/src/vec/into_iter.rs:LL:CC - = note: inside `> as std::iter::Iterator>::for_each::<{closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sync/once.rs:LL:CC - | -LL | self.inner.call(true, &mut |p| f.take().unwrap()(p)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sync/once.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/thread_locals.rs b/src/tools/miri/tests/genmc/pass/std/thread_locals.rs index 4dac775d34070..d76975d2e92c2 100644 --- a/src/tools/miri/tests/genmc/pass/std/thread_locals.rs +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.rs @@ -1,5 +1,4 @@ //@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@normalize-stderr-test: "\n *= note: inside `std::.*" -> "" use std::alloc::{Layout, alloc}; use std::cell::Cell; diff --git a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr index fd6538fd70faa..bde951866d013 100644 --- a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr @@ -1,113 +1,2 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/id.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | / std::thread::spawn(|| { -LL | | R.set(unsafe { malloc() }); -LL | | let r_ptr = R.get(); -LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); -LL | | }), - | |__________^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | / std::thread::spawn(|| { -LL | | R.set(unsafe { malloc() }); -LL | | let r_ptr = R.get(); -LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); -LL | | }), - | |__________^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside closure - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); - | ^^^^^^^^^^^^^ - = note: inside closure at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside closure at RUSTLIB/core/src/ops/try_trait.rs:LL:CC - = note: inside closure at RUSTLIB/core/src/array/iter/iter_inner.rs:LL:CC - = note: inside `::try_fold::<(), {closure@std::array::iter::iter_inner::PolymorphicIter<[std::mem::MaybeUninit>]>::try_fold<(), {closure@std::ops::try_trait::NeverShortCircuit<()>::wrap_mut_2<(), std::thread::JoinHandle<()>, {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>::{closure#0}}, std::ops::try_trait::NeverShortCircuit<()>>::{closure#0}}, std::ops::try_trait::NeverShortCircuit<()>>` at RUSTLIB/core/src/ops/index_range.rs:LL:CC - = note: inside `, 3> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>` at RUSTLIB/core/src/array/iter.rs:LL:CC - = note: inside `, 3> as std::iter::Iterator>::for_each::<{closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sync/once.rs:LL:CC - | -LL | self.inner.call(true, &mut |p| f.take().unwrap()(p)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sync/once.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - Verification complete with 2 executions. No errors found. From 3f48189e97dfd3f527b6be288c3a8aeaf8773ae2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Dec 2025 13:55:32 +0100 Subject: [PATCH 04/26] ftruncate: return proper error code for non-file-backed FDs --- src/tools/miri/src/shims/unix/fs.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 650972be5574e..e17456c3bc0e3 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1157,10 +1157,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; - // FIXME: Support ftruncate64 for all FDs - let file = fd.downcast::().ok_or_else(|| { - err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors") - })?; + let Some(file) = fd.downcast::() else { + // The docs say that EINVAL is returned when the FD "does not reference a regular file + // or a POSIX shared memory object" (and we don't support shmem objects). + return interp_ok(this.eval_libc("EINVAL")); + }; if file.writable { if let Ok(length) = length.try_into() { @@ -1202,10 +1203,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(fd) = this.machine.fds.get(fd_num) else { return interp_ok(this.eval_libc("EBADF")); }; - let file = match fd.downcast::() { - Some(file_handle) => file_handle, + let Some(file) = fd.downcast::() else { // Man page specifies to return ENODEV if `fd` is not a regular file. - None => return interp_ok(this.eval_libc("ENODEV")), + return interp_ok(this.eval_libc("ENODEV")); }; if !file.writable { From c03344429d9b482b85d66e1d0589e813afc4d2b9 Mon Sep 17 00:00:00 2001 From: Roy Ammerschuber Date: Wed, 3 Dec 2025 10:57:38 +0100 Subject: [PATCH 05/26] tree borrows: split perform_protector_end_access from perform_access --- .../src/borrow_tracker/tree_borrows/mod.rs | 11 +- .../src/borrow_tracker/tree_borrows/tree.rs | 118 ++++++++++-------- 2 files changed, 71 insertions(+), 58 deletions(-) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index e1da12282cd95..5322b067aec6d 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -59,7 +59,9 @@ impl<'tcx> Tree { let span = machine.current_user_relevant_span(); self.perform_access( prov, - Some((range, access_kind, diagnostics::AccessCause::Explicit(access_kind))), + range, + access_kind, + diagnostics::AccessCause::Explicit(access_kind), global, alloc_id, span, @@ -93,8 +95,7 @@ impl<'tcx> Tree { alloc_id: AllocId, // diagnostics ) -> InterpResult<'tcx> { let span = machine.current_user_relevant_span(); - // `None` makes it the magic on-protector-end operation - self.perform_access(ProvenanceExtra::Concrete(tag), None, global, alloc_id, span)?; + self.perform_protector_end_access(tag, global, alloc_id, span)?; self.update_exposure_for_protector_release(tag); @@ -343,7 +344,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { tree_borrows.perform_access( parent_prov, - Some((range_in_alloc, AccessKind::Read, diagnostics::AccessCause::Reborrow)), + range_in_alloc, + AccessKind::Read, + diagnostics::AccessCause::Reborrow, this.machine.borrow_tracker.as_ref().unwrap(), alloc_id, this.machine.current_user_relevant_span(), diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 7fc9fa786e10b..547ea5f9cc8b3 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -828,7 +828,9 @@ impl<'tcx> Tree { ) -> InterpResult<'tcx> { self.perform_access( prov, - Some((access_range, AccessKind::Write, diagnostics::AccessCause::Dealloc)), + access_range, + AccessKind::Write, + diagnostics::AccessCause::Dealloc, global, alloc_id, span, @@ -903,14 +905,6 @@ impl<'tcx> Tree { /// to each location of the first component of `access_range_and_kind`, /// on every tag of the allocation. /// - /// If `access_range_and_kind` is `None`, this is interpreted as the special - /// access that is applied on protector release: - /// - the access will be applied only to accessed locations of the allocation, - /// - it will not be visible to children, - /// - it will be recorded as a `FnExit` diagnostic access - /// - and it will be a read except if the location is `Unique`, i.e. has been written to, - /// in which case it will be a write. - /// /// `LocationState::perform_access` will take care of raising transition /// errors and updating the `accessed` status of each location, /// this traversal adds to that: @@ -920,7 +914,9 @@ impl<'tcx> Tree { pub fn perform_access( &mut self, prov: ProvenanceExtra, - access_range_and_kind: Option<(AllocRange, AccessKind, diagnostics::AccessCause)>, + access_range: AllocRange, + access_kind: AccessKind, + access_cause: AccessCause, // diagnostics global: &GlobalState, alloc_id: AllocId, // diagnostics span: Span, // diagnostics @@ -934,61 +930,75 @@ impl<'tcx> Tree { ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()), ProvenanceExtra::Wildcard => None, }; - if let Some((access_range, access_kind, access_cause)) = access_range_and_kind { - // Default branch: this is a "normal" access through a known range. - // We iterate over affected locations and traverse the tree for each of them. - for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { + // We iterate over affected locations and traverse the tree for each of them. + for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { + loc.perform_access( + self.roots.iter().copied(), + &mut self.nodes, + source_idx, + loc_range, + Some(access_range), + access_kind, + access_cause, + global, + alloc_id, + span, + ChildrenVisitMode::VisitChildrenOfAccessed, + )?; + } + interp_ok(()) + } + /// This is the special access that is applied on protector release: + /// - the access will be applied only to accessed locations of the allocation, + /// - it will not be visible to children, + /// - it will be recorded as a `FnExit` diagnostic access + /// - and it will be a read except if the location is `Unique`, i.e. has been written to, + /// in which case it will be a write. + /// - otherwise identical to `Tree::perform_access` + pub fn perform_protector_end_access( + &mut self, + tag: BorTag, + global: &GlobalState, + alloc_id: AllocId, // diagnostics + span: Span, // diagnostics + ) -> InterpResult<'tcx> { + #[cfg(feature = "expensive-consistency-checks")] + if self.roots.len() > 1 { + self.verify_wildcard_consistency(global); + } + + let source_idx = self.tag_mapping.get(&tag).unwrap(); + + // This is a special access through the entire allocation. + // It actually only affects `accessed` locations, so we need + // to filter on those before initiating the traversal. + // + // In addition this implicit access should not be visible to children, + // thus the use of `traverse_nonchildren`. + // See the test case `returned_mut_is_usable` from + // `tests/pass/tree_borrows/tree-borrows.rs` for an example of + // why this is important. + for (loc_range, loc) in self.locations.iter_mut_all() { + // Only visit accessed permissions + if let Some(p) = loc.perms.get(source_idx) + && let Some(access_kind) = p.permission.protector_end_access() + && p.accessed + { + let access_cause = diagnostics::AccessCause::FnExit(access_kind); loc.perform_access( self.roots.iter().copied(), &mut self.nodes, - source_idx, + Some(source_idx), loc_range, - Some(access_range), + None, access_kind, access_cause, global, alloc_id, span, - ChildrenVisitMode::VisitChildrenOfAccessed, + ChildrenVisitMode::SkipChildrenOfAccessed, )?; } - } else { - // This is a special access through the entire allocation. - // It actually only affects `accessed` locations, so we need - // to filter on those before initiating the traversal. - // - // In addition this implicit access should not be visible to children, - // thus the use of `traverse_nonchildren`. - // See the test case `returned_mut_is_usable` from - // `tests/pass/tree_borrows/tree-borrows.rs` for an example of - // why this is important. - - // Wildcard references are never protected. So this can never be - // called with a wildcard reference. - let source_idx = source_idx.unwrap(); - - for (loc_range, loc) in self.locations.iter_mut_all() { - // Only visit accessed permissions - if let Some(p) = loc.perms.get(source_idx) - && let Some(access_kind) = p.permission.protector_end_access() - && p.accessed - { - let access_cause = diagnostics::AccessCause::FnExit(access_kind); - loc.perform_access( - self.roots.iter().copied(), - &mut self.nodes, - Some(source_idx), - loc_range, - None, - access_kind, - access_cause, - global, - alloc_id, - span, - ChildrenVisitMode::SkipChildrenOfAccessed, - )?; - } - } } interp_ok(()) } From 5516e54726ab45ea3f122d986ff046395c90d1e0 Mon Sep 17 00:00:00 2001 From: Urgau <3616612+Urgau@users.noreply.github.com> Date: Mon, 8 Dec 2025 19:25:31 +0100 Subject: [PATCH 06/26] Remove `[no-mentions]` handler in our triagebot config https://github.blog/changelog/2025-11-07-removing-notifications-for-mentions-in-commit-messages/ --- src/tools/miri/triagebot.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tools/miri/triagebot.toml b/src/tools/miri/triagebot.toml index 910e5f56e1e4d..b9edd03f7a22b 100644 --- a/src/tools/miri/triagebot.toml +++ b/src/tools/miri/triagebot.toml @@ -53,8 +53,5 @@ new_draft = true # Canonicalize issue numbers to avoid closing the wrong issue when upstreaming this subtree. [canonicalize-issue-links] -# Prevents mentions in commits to avoid users being spammed. -[no-mentions] - # Show range-diff links on force pushes. [range-diff] From 27590b0832bedfc0a3bead82634a365ced74d88b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 9 Dec 2025 08:29:55 +0100 Subject: [PATCH 07/26] fix manual_let_else --- .../src/borrow_tracker/stacked_borrows/mod.rs | 13 +++--- .../src/borrow_tracker/tree_borrows/mod.rs | 40 +++++++++---------- src/tools/miri/src/concurrency/weak_memory.rs | 5 +-- src/tools/miri/src/eval.rs | 4 +- src/tools/miri/src/lib.rs | 7 +++- src/tools/miri/src/shims/backtrace.rs | 11 ++--- src/tools/miri/src/shims/native_lib/mod.rs | 9 ++--- src/tools/miri/src/shims/time.rs | 14 ++----- src/tools/miri/src/shims/unix/freebsd/sync.rs | 21 +++------- .../miri/src/shims/unix/linux_like/sync.rs | 7 +--- src/tools/miri/src/shims/unix/sync.rs | 13 +++--- src/tools/miri/src/shims/windows/fs.rs | 21 ++-------- 12 files changed, 59 insertions(+), 106 deletions(-) diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index e8d97491acaf3..a21898c506ab9 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -826,15 +826,12 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { // FIXME: If we cannot determine the size (because the unsized tail is an `extern type`), // bail out -- we cannot reasonably figure out which memory range to reborrow. // See https://github.com/rust-lang/unsafe-code-guidelines/issues/276. - let size = match size { - Some(size) => size, - None => { - static DEDUP: AtomicBool = AtomicBool::new(false); - if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { - this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow); - } - return interp_ok(place.clone()); + let Some(size) = size else { + static DEDUP: AtomicBool = AtomicBool::new(false); + if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { + this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow); } + return interp_ok(place.clone()); }; // Compute new borrow. diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 018421ad1064f..6b1194e5e5dac 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -219,26 +219,18 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; trace!("Reborrow of size {:?}", ptr_size); - let (alloc_id, base_offset, parent_prov) = match this.ptr_try_get_alloc_id(place.ptr(), 0) { - Ok(data) => { - // Unlike SB, we *do* a proper retag for size 0 if can identify the allocation. - // After all, the pointer may be lazily initialized outside this initial range. - data - } - Err(_) => { - assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here - // This pointer doesn't come with an AllocId, so there's no - // memory to do retagging in. - let new_prov = place.ptr().provenance; - trace!( - "reborrow of size 0: reusing {:?} (pointee {})", - place.ptr(), - place.layout.ty, - ); - log_creation(this, None)?; - // Keep original provenance. - return interp_ok(new_prov); - } + // Unlike SB, we *do* a proper retag for size 0 if can identify the allocation. + // After all, the pointer may be lazily initialized outside this initial range. + let Ok((alloc_id, base_offset, parent_prov)) = this.ptr_try_get_alloc_id(place.ptr(), 0) + else { + assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here + // This pointer doesn't come with an AllocId, so there's no + // memory to do retagging in. + let new_prov = place.ptr().provenance; + trace!("reborrow of size 0: reusing {:?} (pointee {})", place.ptr(), place.layout.ty,); + log_creation(this, None)?; + // Keep original provenance. + return interp_ok(new_prov); }; let new_prov = Provenance::Concrete { alloc_id, tag: new_tag }; @@ -609,8 +601,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let (tag, alloc_id) = match ptr.provenance { Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id), - _ => { - eprintln!("Can't give the name {name} to Wildcard pointer"); + Some(Provenance::Wildcard) => { + eprintln!("Can't give the name {name} to wildcard pointer"); + return interp_ok(()); + } + None => { + eprintln!("Can't give the name {name} to pointer without provenance"); return interp_ok(()); } }; diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index 2255e0d48174e..6fe73fec0f57f 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -208,11 +208,10 @@ impl StoreBufferAlloc { range: AllocRange, ) -> InterpResult<'tcx, Option>> { let access_type = self.store_buffers.borrow().access_type(range); - let pos = match access_type { - AccessType::PerfectlyOverlapping(pos) => pos, + let AccessType::PerfectlyOverlapping(pos) = access_type else { // If there is nothing here yet, that means there wasn't an atomic write yet so // we can't return anything outdated. - _ => return interp_ok(None), + return interp_ok(None); }; let store_buffer = Ref::map(self.store_buffers.borrow(), |buffer| &buffer[pos]); interp_ok(Some(store_buffer)) diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 01a54bbb33746..0423b0ea5abdf 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -543,9 +543,7 @@ where { // Parse argv[0]. Slashes aren't escaped. Literal double quotes are not allowed. let mut cmd = { - let arg0 = if let Some(arg0) = args.next() { - arg0 - } else { + let Some(arg0) = args.next() else { return vec![0]; }; let arg0 = arg0.as_ref(); diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index fe501b8d7b30d..9776ef825e729 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -46,7 +46,12 @@ rustc::potential_query_instability, rustc::untranslatable_diagnostic, )] -#![warn(rust_2018_idioms, unqualified_local_imports, clippy::as_conversions)] +#![warn( + rust_2018_idioms, + unqualified_local_imports, + clippy::as_conversions, + clippy::manual_let_else +)] // Needed for rustdoc from bootstrap (with `-Znormalize-docs`). #![recursion_limit = "256"] diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index bd3914b652ac9..1ca814ee7afff 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -92,21 +92,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (alloc_id, offset, _prov) = this.ptr_get_alloc_id(ptr, 0)?; // This has to be an actual global fn ptr, not a dlsym function. - let fn_instance = if let Some(GlobalAlloc::Function { instance, .. }) = - this.tcx.try_get_global_alloc(alloc_id) - { - instance - } else { + let Some(GlobalAlloc::Function { instance, .. }) = this.tcx.try_get_global_alloc(alloc_id) + else { throw_ub_format!("expected static function pointer, found {:?}", ptr); }; let lo = this.tcx.sess.source_map().lookup_char_pos(BytePos(offset.bytes().try_into().unwrap())); - let name = fn_instance.to_string(); + let name = instance.to_string(); let filename = lo.file.name.prefer_remapped_unconditionally().to_string(); - interp_ok((fn_instance, lo, name, filename)) + interp_ok((instance, lo, name, filename)) } fn handle_miri_resolve_frame( diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 445f9618e7e36..12abe841c0528 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -459,12 +459,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); // Get the pointer to the function in the shared object file if it exists. - let code_ptr = match this.get_func_ptr_explicitly_from_lib(link_name) { - Some(ptr) => ptr, - None => { - // Shared object file does not export this function -- try the shims next. - return interp_ok(false); - } + let Some(code_ptr) = this.get_func_ptr_explicitly_from_lib(link_name) else { + // Shared object file does not export this function -- try the shims next. + return interp_ok(false); }; // Do we have ptrace? diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 614cc75c6d580..2bbae80a0b919 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -337,11 +337,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let duration = this.deref_pointer_as(duration, this.libc_ty_layout("timespec"))?; let _rem = this.read_pointer(rem)?; // Signal handlers are not supported, so rem will never be written to. - let duration = match this.read_timespec(&duration)? { - Some(duration) => duration, - None => { - return this.set_last_error_and_return_i32(LibcError("EINVAL")); - } + let Some(duration) = this.read_timespec(&duration)? else { + return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; this.block_thread( @@ -378,11 +375,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("clock_nanosleep: only CLOCK_MONOTONIC is supported"); } - let duration = match this.read_timespec(×pec)? { - Some(duration) => duration, - None => { - return this.set_last_error_and_return_i32(LibcError("EINVAL")); - } + let Some(duration) = this.read_timespec(×pec)? else { + return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; let timeout_anchor = if flags == 0 { diff --git a/src/tools/miri/src/shims/unix/freebsd/sync.rs b/src/tools/miri/src/shims/unix/freebsd/sync.rs index bd1ee31553727..ae8a167080b90 100644 --- a/src/tools/miri/src/shims/unix/freebsd/sync.rs +++ b/src/tools/miri/src/shims/unix/freebsd/sync.rs @@ -101,12 +101,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `uaddr2` points to a `struct _umtx_time`. let umtx_time_place = this.ptr_to_mplace(uaddr2, umtx_time_layout); - let umtx_time = match this.read_umtx_time(&umtx_time_place)? { - Some(ut) => ut, - None => { - return this - .set_last_error_and_return(LibcError("EINVAL"), dest); - } + let Some(umtx_time) = this.read_umtx_time(&umtx_time_place)? else { + return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; let anchor = if umtx_time.abs_time { @@ -122,12 +118,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `uaddr2` points to a `struct timespec`. let timespec = this.ptr_to_mplace(uaddr2, timespec_layout); - let duration = match this.read_timespec(×pec)? { - Some(duration) => duration, - None => { - return this - .set_last_error_and_return(LibcError("EINVAL"), dest); - } + let Some(duration) = this.read_timespec(×pec)? else { + return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; // FreeBSD does not seem to document which clock is used when the timeout @@ -220,10 +212,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let timespec_place = this.project_field(ut, FieldIdx::from_u32(0))?; // Inner `timespec` must still be valid. - let duration = match this.read_timespec(×pec_place)? { - Some(dur) => dur, - None => return interp_ok(None), - }; + let Some(duration) = this.read_timespec(×pec_place)? else { return interp_ok(None) }; let flags_place = this.project_field(ut, FieldIdx::from_u32(1))?; let flags = this.read_scalar(&flags_place)?.to_u32()?; diff --git a/src/tools/miri/src/shims/unix/linux_like/sync.rs b/src/tools/miri/src/shims/unix/linux_like/sync.rs index 8ff7fe0a4563b..00df4ee2b2ae3 100644 --- a/src/tools/miri/src/shims/unix/linux_like/sync.rs +++ b/src/tools/miri/src/shims/unix/linux_like/sync.rs @@ -71,11 +71,8 @@ pub fn futex<'tcx>( let timeout = if ecx.ptr_is_null(timeout.ptr())? { None } else { - let duration = match ecx.read_timespec(&timeout)? { - Some(duration) => duration, - None => { - return ecx.set_last_error_and_return(LibcError("EINVAL"), dest); - } + let Some(duration) = ecx.read_timespec(&timeout)? else { + return ecx.set_last_error_and_return(LibcError("EINVAL"), dest); }; let timeout_clock = if op & futex_realtime == futex_realtime { ecx.check_no_isolation( diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 39ad66041820f..58a612102b180 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -887,15 +887,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone(); // Extract the timeout. - let duration = match this + let Some(duration) = this .read_timespec(&this.deref_pointer_as(timeout_op, this.libc_ty_layout("timespec"))?)? - { - Some(duration) => duration, - None => { - let einval = this.eval_libc("EINVAL"); - this.write_scalar(einval, dest)?; - return interp_ok(()); - } + else { + let einval = this.eval_libc("EINVAL"); + this.write_scalar(einval, dest)?; + return interp_ok(()); }; let (clock, anchor) = if macos_relative_np { diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index 775c08388c078..ad22df2425af9 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -321,11 +321,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.windows_ty_layout("BY_HANDLE_FILE_INFORMATION"), )?; - let fd_num = if let Handle::File(fd_num) = file { - fd_num - } else { - this.invalid_handle("GetFileInformationByHandle")? - }; + let Handle::File(fd_num) = file else { this.invalid_handle("GetFileInformationByHandle")? }; let Some(desc) = this.machine.fds.get(fd_num) else { this.invalid_handle("GetFileInformationByHandle")? @@ -448,10 +444,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("`NtWriteFile` `Key` parameter is non-null, which is unsupported"); } - let fd = match handle { - Handle::File(fd) => fd, - _ => this.invalid_handle("NtWriteFile")?, - }; + let Handle::File(fd) = handle else { this.invalid_handle("NtWriteFile")? }; let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtWriteFile")? }; @@ -561,10 +554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let io_status_info = this.project_field_named(&io_status_block, "Information")?; - let fd = match handle { - Handle::File(fd) => fd, - _ => this.invalid_handle("NtWriteFile")?, - }; + let Handle::File(fd) = handle else { this.invalid_handle("NtWriteFile")? }; let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtReadFile")? }; @@ -620,10 +610,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let new_fp_ptr = this.read_pointer(new_fp)?; let move_method = this.read_scalar(move_method)?.to_u32()?; - let fd = match file { - Handle::File(fd) => fd, - _ => this.invalid_handle("SetFilePointerEx")?, - }; + let Handle::File(fd) = file else { this.invalid_handle("SetFilePointerEx")? }; let Some(desc) = this.machine.fds.get(fd) else { throw_unsup_format!("`SetFilePointerEx` is only supported on file backed handles"); From 85e3acc947064454d907cc5a4195a7dcbc4193a9 Mon Sep 17 00:00:00 2001 From: Roy Ammerschuber Date: Wed, 3 Dec 2025 11:56:07 +0100 Subject: [PATCH 08/26] tree borrows: put accesses diagnostics data into single struct --- .../tree_borrows/diagnostics.rs | 66 ++++++--- .../src/borrow_tracker/tree_borrows/tree.rs | 130 +++++++----------- 2 files changed, 94 insertions(+), 102 deletions(-) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index a91f35a9dcad3..055baca8542fd 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -77,6 +77,32 @@ pub struct Event { pub span: Span, } +/// Diagnostics data about the current access and the location we are accessing. +/// Used to create history events and errors. +#[derive(Clone, Debug)] +pub struct DiagnosticInfo { + pub alloc_id: AllocId, + pub span: Span, + /// The range the diagnostic actually applies to. + /// This is always a subset of `access_range`. + pub transition_range: Range, + /// The range the access is happening to. Is `None` if this is the protector release access + pub access_range: Option, + pub access_cause: AccessCause, +} +impl DiagnosticInfo { + /// Creates a history event. + pub fn create_event(&self, transition: PermTransition, is_foreign: bool) -> Event { + Event { + transition, + is_foreign, + access_cause: self.access_cause, + access_range: self.access_range, + transition_range: self.transition_range.clone(), + span: self.span, + } + } +} /// List of all events that affected a tag. /// NOTE: not all of these events are relevant for a particular location, /// the events should be filtered before the generation of diagnostics. @@ -280,32 +306,29 @@ impl History { pub(super) struct TbError<'node> { /// What failure occurred. pub error_kind: TransitionError, - /// The allocation in which the error is happening. - pub alloc_id: AllocId, - /// The offset (into the allocation) at which the conflict occurred. - pub error_offset: u64, + /// Diagnostic data about the access that caused the error. + pub access_info: &'node DiagnosticInfo, /// The tag on which the error was triggered. /// On protector violations, this is the tag that was protected. /// On accesses rejected due to insufficient permissions, this is the /// tag that lacked those permissions. - pub conflicting_info: &'node NodeDebugInfo, - // What kind of access caused this error (read, write, reborrow, deallocation) - pub access_cause: AccessCause, + pub conflicting_node_info: &'node NodeDebugInfo, /// Which tag, if any, the access that caused this error was made through, i.e. /// which tag was used to read/write/deallocate. /// Not set on wildcard accesses. - pub accessed_info: Option<&'node NodeDebugInfo>, + pub accessed_node_info: Option<&'node NodeDebugInfo>, } impl TbError<'_> { /// Produce a UB error. pub fn build<'tcx>(self) -> InterpErrorKind<'tcx> { use TransitionError::*; - let cause = self.access_cause; - let accessed = self.accessed_info; + let cause = self.access_info.access_cause; + let error_offset = self.access_info.transition_range.start; + let accessed = self.accessed_node_info; let accessed_str = - self.accessed_info.map(|v| format!("{v}")).unwrap_or_else(|| "".into()); - let conflicting = self.conflicting_info; + self.accessed_node_info.map(|v| format!("{v}")).unwrap_or_else(|| "".into()); + let conflicting = self.conflicting_node_info; // An access is considered conflicting if it happened through a // different tag than the one who caused UB. // When doing a wildcard access (where `accessed` is `None`) we @@ -316,9 +339,8 @@ impl TbError<'_> { // all tags through which an access would cause UB. let accessed_is_conflicting = accessed.map(|a| a.tag) == Some(conflicting.tag); let title = format!( - "{cause} through {accessed_str} at {alloc_id:?}[{offset:#x}] is forbidden", - alloc_id = self.alloc_id, - offset = self.error_offset + "{cause} through {accessed_str} at {alloc_id:?}[{error_offset:#x}] is forbidden", + alloc_id = self.access_info.alloc_id ); let (title, details, conflicting_tag_name) = match self.error_kind { ChildAccessForbidden(perm) => { @@ -362,13 +384,13 @@ impl TbError<'_> { } }; let mut history = HistoryData::default(); - if let Some(accessed_info) = self.accessed_info + if let Some(accessed_info) = self.accessed_node_info && !accessed_is_conflicting { history.extend(accessed_info.history.forget(), "accessed", false); } history.extend( - self.conflicting_info.history.extract_relevant(self.error_offset, self.error_kind), + self.conflicting_node_info.history.extract_relevant(error_offset, self.error_kind), conflicting_tag_name, true, ); @@ -379,12 +401,12 @@ impl TbError<'_> { /// Cannot access this allocation with wildcard provenance, as there are no /// valid exposed references for this access kind. pub fn no_valid_exposed_references_error<'tcx>( - alloc_id: AllocId, - offset: u64, - access_cause: AccessCause, + DiagnosticInfo { alloc_id, transition_range, access_cause, .. }: &DiagnosticInfo, ) -> InterpErrorKind<'tcx> { - let title = - format!("{access_cause} through at {alloc_id:?}[{offset:#x}] is forbidden"); + let title = format!( + "{access_cause} through at {alloc_id:?}[{offset:#x}] is forbidden", + offset = transition_range.start + ); let details = vec![format!("there are no exposed tags which may perform this access here")]; let history = HistoryData::default(); err_machine_stop!(TerminationInfo::TreeBorrowsUb { title, details, history }) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 7630c73f4dacd..900e9c3729c84 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -18,12 +18,12 @@ use rustc_data_structures::fx::FxHashSet; use rustc_span::Span; use smallvec::SmallVec; -use super::Permission; use super::diagnostics::{ - self, AccessCause, NodeDebugInfo, TbError, TransitionError, no_valid_exposed_references_error, + AccessCause, DiagnosticInfo, NodeDebugInfo, TbError, TransitionError, + no_valid_exposed_references_error, }; use super::foreign_access_skipping::IdempotentForeignAccess; -use super::perms::PermTransition; +use super::perms::{PermTransition, Permission}; use super::tree_visitor::{ChildrenVisitMode, ContinueTraversal, NodeAppArgs, TreeVisitor}; use super::unimap::{UniIndex, UniKeyMap, UniValMap}; use super::wildcard::WildcardState; @@ -91,12 +91,9 @@ impl LocationState { nodes: &mut UniValMap, wildcard_accesses: &mut UniValMap, access_kind: AccessKind, - access_cause: AccessCause, //diagnostics - access_range: Option, //diagnostics relatedness: AccessRelatedness, - span: Span, //diagnostics - location_range: Range, //diagnostics protected: bool, + diagnostics: &DiagnosticInfo, ) -> Result<(), TransitionError> { // Call this function now (i.e. only if we know `relatedness`), which // ensures it is only called when `skip_if_known_noop` returns @@ -107,14 +104,9 @@ impl LocationState { if !transition.is_noop() { let node = nodes.get_mut(idx).unwrap(); // Record the event as part of the history. - node.debug_info.history.push(diagnostics::Event { - transition, - is_foreign: relatedness.is_foreign(), - access_cause, - access_range, - transition_range: location_range, - span, - }); + node.debug_info + .history + .push(diagnostics.create_event(transition, relatedness.is_foreign())); // We need to update the wildcard state, if the permission // of an exposed pointer changes. @@ -546,7 +538,7 @@ impl<'tcx> Tree { prov, access_range, AccessKind::Write, - diagnostics::AccessCause::Dealloc, + AccessCause::Dealloc, global, alloc_id, span, @@ -560,6 +552,13 @@ impl<'tcx> Tree { // Check if this breaks any strong protector. // (Weak protectors are already handled by `perform_access`.) for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { + let diagnostics = DiagnosticInfo { + alloc_id, + span, + transition_range: loc_range, + access_range: Some(access_range), + access_cause: AccessCause::Dealloc, + }; // Checks the tree containing `idx` for strong protector violations. // It does this in traversal order. let mut check_tree = |idx| { @@ -586,12 +585,10 @@ impl<'tcx> Tree { && perm.accessed { Err(TbError { - conflicting_info: &node.debug_info, - access_cause: diagnostics::AccessCause::Dealloc, - alloc_id, - error_offset: loc_range.start, error_kind: TransitionError::ProtectedDealloc, - accessed_info: start_idx + access_info: &diagnostics, + conflicting_node_info: &node.debug_info, + accessed_node_info: start_idx .map(|idx| &args.nodes.get(idx).unwrap().debug_info), } .build()) @@ -649,18 +646,21 @@ impl<'tcx> Tree { }; // We iterate over affected locations and traverse the tree for each of them. for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { + let diagnostics = DiagnosticInfo { + access_cause, + access_range: Some(access_range), + alloc_id, + span, + transition_range: loc_range, + }; loc.perform_access( self.roots.iter().copied(), &mut self.nodes, source_idx, - loc_range, - Some(access_range), access_kind, - access_cause, global, - alloc_id, - span, ChildrenVisitMode::VisitChildrenOfAccessed, + &diagnostics, )?; } interp_ok(()) @@ -701,19 +701,21 @@ impl<'tcx> Tree { && let Some(access_kind) = p.permission.protector_end_access() && p.accessed { - let access_cause = diagnostics::AccessCause::FnExit(access_kind); + let diagnostics = DiagnosticInfo { + access_cause: AccessCause::FnExit(access_kind), + access_range: None, + alloc_id, + span, + transition_range: loc_range, + }; loc.perform_access( self.roots.iter().copied(), &mut self.nodes, Some(source_idx), - loc_range, - None, access_kind, - access_cause, global, - alloc_id, - span, ChildrenVisitMode::SkipChildrenOfAccessed, + &diagnostics, )?; } } @@ -882,27 +884,19 @@ impl<'tcx> LocationTree { roots: impl Iterator, nodes: &mut UniValMap, access_source: Option, - loc_range: Range, // diagnostics - access_range: Option, // diagnostics access_kind: AccessKind, - access_cause: diagnostics::AccessCause, // diagnostics global: &GlobalState, - alloc_id: AllocId, // diagnostics - span: Span, // diagnostics visit_children: ChildrenVisitMode, + diagnostics: &DiagnosticInfo, ) -> InterpResult<'tcx> { let accessed_root = if let Some(idx) = access_source { Some(self.perform_normal_access( idx, nodes, - loc_range.clone(), - access_range, access_kind, - access_cause, global, - alloc_id, - span, visit_children, + diagnostics, )?) } else { // `SkipChildrenOfAccessed` only gets set on protector release, which only @@ -936,13 +930,9 @@ impl<'tcx> LocationTree { access_source, /*max_local_tag*/ accessed_root_tag, nodes, - loc_range.clone(), - access_range, access_kind, - access_cause, global, - alloc_id, - span, + diagnostics, )?; } interp_ok(()) @@ -958,14 +948,10 @@ impl<'tcx> LocationTree { &mut self, access_source: UniIndex, nodes: &mut UniValMap, - loc_range: Range, // diagnostics - access_range: Option, // diagnostics access_kind: AccessKind, - access_cause: diagnostics::AccessCause, // diagnostics global: &GlobalState, - alloc_id: AllocId, // diagnostics - span: Span, // diagnostics visit_children: ChildrenVisitMode, + diagnostics: &DiagnosticInfo, ) -> InterpResult<'tcx, UniIndex> { // Performs the per-node work: // - insert the permission if it does not exist @@ -997,21 +983,18 @@ impl<'tcx> LocationTree { args.nodes, &mut args.data.wildcard_accesses, access_kind, - access_cause, - access_range, args.rel_pos, - span, - loc_range.clone(), protected, + diagnostics, ) .map_err(|error_kind| { TbError { - conflicting_info: &args.nodes.get(args.idx).unwrap().debug_info, - access_cause, - alloc_id, - error_offset: loc_range.start, error_kind, - accessed_info: Some(&args.nodes.get(access_source).unwrap().debug_info), + access_info: diagnostics, + conflicting_node_info: &args.nodes.get(args.idx).unwrap().debug_info, + accessed_node_info: Some( + &args.nodes.get(access_source).unwrap().debug_info, + ), } .build() }) @@ -1040,13 +1023,9 @@ impl<'tcx> LocationTree { access_source: Option, max_local_tag: Option, nodes: &mut UniValMap, - loc_range: Range, // diagnostics - access_range: Option, // diagnostics access_kind: AccessKind, - access_cause: diagnostics::AccessCause, // diagnostics global: &GlobalState, - alloc_id: AllocId, // diagnostics - span: Span, // diagnostics + diagnostics: &DiagnosticInfo, ) -> InterpResult<'tcx> { let get_relatedness = |idx: UniIndex, node: &Node, loc: &LocationTree| { let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default(); @@ -1100,11 +1079,7 @@ impl<'tcx> LocationTree { // This can only happen if `root` is the main root: We set // `max_foreign_access==Write` on all wildcard roots, so at least a foreign access // is always possible on all nodes in a wildcard subtree. - return Err(no_valid_exposed_references_error( - alloc_id, - loc_range.start, - access_cause, - )); + return Err(no_valid_exposed_references_error(diagnostics)); }; let Some(relatedness) = wildcard_relatedness.to_relatedness() else { @@ -1123,22 +1098,17 @@ impl<'tcx> LocationTree { args.nodes, &mut args.data.wildcard_accesses, access_kind, - access_cause, - access_range, relatedness, - span, - loc_range.clone(), protected, + diagnostics, ) .map_err(|trans| { let node = args.nodes.get(args.idx).unwrap(); TbError { - conflicting_info: &node.debug_info, - access_cause, - alloc_id, - error_offset: loc_range.start, error_kind: trans, - accessed_info: access_source + access_info: diagnostics, + conflicting_node_info: &node.debug_info, + accessed_node_info: access_source .map(|idx| &args.nodes.get(idx).unwrap().debug_info), } .build() From ff43366dc24cbd7e6ee72489e6a05df5b1f17dab Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 9 Dec 2025 23:25:05 -0800 Subject: [PATCH 09/26] Update `wrapping_sh[lr]` docs and examples --- library/core/src/num/int_macros.rs | 28 ++++++++++++++++++++++++---- library/core/src/num/uint_macros.rs | 28 ++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 99662768a29f2..c260cd63f7c46 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2288,6 +2288,13 @@ macro_rules! int_impl { /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes /// any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `<<` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shl`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to /// the range of the type, rather than the bits shifted out of the LHS being returned to the other end. /// The primitive integer types all implement a [`rotate_left`](Self::rotate_left) function, @@ -2296,8 +2303,11 @@ macro_rules! int_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(7), -128);")] - #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(128), -1);")] + #[doc = concat!("assert_eq!((-1_", stringify!($SelfT), ").wrapping_shl(7), -128);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(1).wrapping_shl(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!((-1_", stringify!($SelfT), ").wrapping_shl(128), -1);")] + #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".wrapping_shl(1025), 10);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] @@ -2315,6 +2325,13 @@ macro_rules! int_impl { /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` /// removes any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `>>` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shr`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted /// to the range of the type, rather than the bits shifted out of the LHS being returned to the other /// end. The primitive integer types all implement a [`rotate_right`](Self::rotate_right) function, @@ -2323,8 +2340,11 @@ macro_rules! int_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!((-128", stringify!($SelfT), ").wrapping_shr(7), -1);")] - /// assert_eq!((-128i16).wrapping_shr(64), -128); + #[doc = concat!("assert_eq!((-128_", stringify!($SelfT), ").wrapping_shr(7), -1);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(1).wrapping_shr(", stringify!($BITS_MINUS_ONE), "), 0);")] + /// assert_eq!((-128_i16).wrapping_shr(64), -128); + #[doc = concat!("assert_eq!(10_", stringify!($SelfT), ".wrapping_shr(1025), 5);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index c8224e92b17e4..89f330a063ace 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2593,6 +2593,13 @@ macro_rules! uint_impl { /// where `mask` removes any high-order bits of `rhs` that /// would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `<<` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shl`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-left; the /// RHS of a wrapping shift-left is restricted to the range /// of the type, rather than the bits shifted out of the LHS @@ -2603,8 +2610,11 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(7), 128);")] - #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(128), 1);")] + #[doc = concat!("assert_eq!(1_", stringify!($SelfT), ".wrapping_shl(7), 128);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(1).wrapping_shl(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!(1_", stringify!($SelfT), ".wrapping_shl(128), 1);")] + #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".wrapping_shl(1025), 10);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] @@ -2623,6 +2633,13 @@ macro_rules! uint_impl { /// where `mask` removes any high-order bits of `rhs` that /// would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `>>` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shr`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-right; the /// RHS of a wrapping shift-right is restricted to the range /// of the type, rather than the bits shifted out of the LHS @@ -2633,8 +2650,11 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(7), 1);")] - #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(128), 128);")] + #[doc = concat!("assert_eq!(128_", stringify!($SelfT), ".wrapping_shr(7), 1);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(1).wrapping_shr(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!(128_", stringify!($SelfT), ".wrapping_shr(128), 128);")] + #[doc = concat!("assert_eq!(10_", stringify!($SelfT), ".wrapping_shr(1025), 5);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] From 71d7ae2f208ca3d16db7faef93f11e694693e3a9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 10 Dec 2025 12:35:04 -0800 Subject: [PATCH 10/26] std: Don't use `linkat` on the `wasm32-wasi*` targets This commit is a follow-up to 147572 and the issue reported at the end of that PR where the `std::fs::hard_link` function is broken after that PR landed. The true culprit and bug here is fixed in WebAssembly/wasi-libc/690 but until that's released in a wasi-sdk version it should be reasonable, on WASI, to skip the `linkat` function. --- library/std/src/sys/fs/unix.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 507d093d2b2f6..74206405bfe7b 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2077,12 +2077,25 @@ pub fn symlink(original: &CStr, link: &CStr) -> io::Result<()> { pub fn link(original: &CStr, link: &CStr) -> io::Result<()> { cfg_select! { - any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70") => { - // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves - // it implementation-defined whether `link` follows symlinks, so rely on the - // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. - // Android has `linkat` on newer versions, but we happen to know `link` - // always has the correct behavior, so it's here as well. + any( + // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. + // POSIX leaves it implementation-defined whether `link` follows + // symlinks, so rely on the `symlink_hard_link` test in + // library/std/src/fs/tests.rs to check the behavior. + target_os = "vxworks", + target_os = "redox", + target_os = "espidf", + // Android has `linkat` on newer versions, but we happen to know + // `link` always has the correct behavior, so it's here as well. + target_os = "android", + // wasi-sdk-29-and-prior have a buggy `linkat` so use `link` instead + // until wasi-sdk is updated (see WebAssembly/wasi-libc#690) + target_os = "wasi", + // Other misc platforms + target_os = "horizon", + target_os = "vita", + target_env = "nto70", + ) => { cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; } _ => { From f9b830c10239f5ff2c3a6b2b7f8183a4a855e9fe Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 10 Dec 2025 18:59:52 -0800 Subject: [PATCH 11/26] Add more basic shift examples --- library/core/src/num/uint_macros.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 89f330a063ace..9e69dec9547f6 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2611,6 +2611,10 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("assert_eq!(1_", stringify!($SelfT), ".wrapping_shl(7), 128);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".wrapping_shl(0), 0b101);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".wrapping_shl(1), 0b1010);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".wrapping_shl(2), 0b10100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_shl(2), ", stringify!($SelfT), "::MAX - 3);")] #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(", stringify!($BITS), "), 42);")] #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(1).wrapping_shl(", stringify!($BITS_MINUS_ONE), "), 0);")] #[doc = concat!("assert_eq!(1_", stringify!($SelfT), ".wrapping_shl(128), 1);")] @@ -2651,6 +2655,10 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("assert_eq!(128_", stringify!($SelfT), ".wrapping_shr(7), 1);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".wrapping_shr(0), 0b1010);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".wrapping_shr(1), 0b101);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".wrapping_shr(2), 0b10);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_shr(1), ", stringify!($SignedT), "::MAX.cast_unsigned());")] #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(", stringify!($BITS), "), 42);")] #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(1).wrapping_shr(", stringify!($BITS_MINUS_ONE), "), 0);")] #[doc = concat!("assert_eq!(128_", stringify!($SelfT), ".wrapping_shr(128), 128);")] From 0b11cdfa417ff1bc19f6bfe880307188674b147f Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 11 Dec 2025 04:59:05 +0000 Subject: [PATCH 12/26] Prepare for merging from rust-lang/rust This updates the rust-version file to f5209000832c9d3bc29c91f4daef4ca9f28dc797. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index b6a1415f1834c..2e81ccbba1846 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -36b2369c91d32c2659887ed6fe3d570640f44fd2 +f5209000832c9d3bc29c91f4daef4ca9f28dc797 From 82118ff316c67710f1677ae6e36660f7fed91d3a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 11 Dec 2025 17:03:49 +0100 Subject: [PATCH 13/26] gitconfig is not a toml file --- src/tools/miri/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 073ad267476c4..1995300c5bcbb 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -351,7 +351,7 @@ you need to pull rustc changes into Miri first, and then re-do the rustc push. If this fails due to authentication problems, it can help to make josh push via ssh instead of https. Add the following to your `.gitconfig`: -```toml +```text [url "git@github.com:"] pushInsteadOf = https://github.com/ ``` From 395c6f56a3ed8d6e696dd86915f0272a62ec8e8e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Dec 2025 19:14:52 +0100 Subject: [PATCH 14/26] README: add more papers to the paper list --- src/tools/miri/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 32494141589ac..86f190134d452 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -651,6 +651,11 @@ Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows * [Stacked Borrows: An Aliasing Model for Rust](https://plv.mpi-sws.org/rustbelt/stacked-borrows/) * [Using Lightweight Formal Methods to Validate a Key-Value Storage Node in Amazon S3](https://www.amazon.science/publications/using-lightweight-formal-methods-to-validate-a-key-value-storage-node-in-amazon-s3) * [SyRust: Automatic Testing of Rust Libraries with Semantic-Aware Program Synthesis](https://dl.acm.org/doi/10.1145/3453483.3454084) +* [Crabtree: Rust API Test Synthesis Guided by Coverage and Type](https://dl.acm.org/doi/10.1145/3689733) +* [Rustlantis: Randomized Differential Testing of the Rust Compiler](https://dl.acm.org/doi/10.1145/3689780) +* [A Study of Undefined Behavior Across Foreign Function Boundaries in Rust Libraries](https://arxiv.org/abs/2404.11671) +* [Tree Borrows](https://plf.inf.ethz.ch/research/pldi25-tree-borrows.html) +* [Miri: Practical Undefined Behavior Detection for Rust](https://plf.inf.ethz.ch/research/popl26-miri.html) **(this paper describes Miri itself)** ## License From 860716faa38c09a4e119c06f030ab222252b6509 Mon Sep 17 00:00:00 2001 From: joboet Date: Fri, 19 Sep 2025 20:56:07 +0200 Subject: [PATCH 15/26] std: reorganize pipe implementations --- library/std/src/io/pipe.rs | 24 +-- library/std/src/os/unix/process.rs | 16 +- library/std/src/os/windows/process.rs | 6 +- library/std/src/process.rs | 51 +++-- library/std/src/sys/anonymous_pipe/motor.rs | 11 -- library/std/src/sys/anonymous_pipe/unix.rs | 11 -- .../std/src/sys/anonymous_pipe/unsupported.rs | 7 - library/std/src/sys/mod.rs | 2 +- library/std/src/sys/pal/hermit/mod.rs | 2 - library/std/src/sys/pal/motor/mod.rs | 1 - library/std/src/sys/pal/motor/pipe.rs | 121 ------------ library/std/src/sys/pal/sgx/mod.rs | 2 - library/std/src/sys/pal/solid/mod.rs | 2 - library/std/src/sys/pal/teeos/mod.rs | 2 - library/std/src/sys/pal/trusty/mod.rs | 2 - library/std/src/sys/pal/uefi/mod.rs | 2 - library/std/src/sys/pal/unix/mod.rs | 1 - library/std/src/sys/pal/unix/pipe.rs | 184 ------------------ library/std/src/sys/pal/unsupported/mod.rs | 1 - library/std/src/sys/pal/wasi/mod.rs | 2 - library/std/src/sys/pal/wasm/mod.rs | 2 - library/std/src/sys/pal/windows/mod.rs | 1 - library/std/src/sys/pal/xous/mod.rs | 2 - library/std/src/sys/pal/zkvm/mod.rs | 2 - .../src/sys/{anonymous_pipe => pipe}/mod.rs | 8 +- library/std/src/sys/pipe/motor.rs | 9 + library/std/src/sys/pipe/unix.rs | 44 +++++ .../pipe.rs => pipe/unsupported.rs} | 46 ++--- .../sys/{anonymous_pipe => pipe}/windows.rs | 4 +- library/std/src/sys/process/mod.rs | 7 +- library/std/src/sys/process/motor.rs | 36 +++- library/std/src/sys/process/uefi.rs | 16 +- library/std/src/sys/process/unix/common.rs | 70 +++++-- library/std/src/sys/process/unix/mod.rs | 2 +- library/std/src/sys/process/unix/unix.rs | 2 +- library/std/src/sys/process/unsupported.rs | 16 +- library/std/src/sys/process/windows.rs | 18 +- .../pipe.rs => process/windows/child_pipe.rs} | 72 +++---- library/std/src/sys/stdio/motor.rs | 6 +- 39 files changed, 296 insertions(+), 517 deletions(-) delete mode 100644 library/std/src/sys/anonymous_pipe/motor.rs delete mode 100644 library/std/src/sys/anonymous_pipe/unix.rs delete mode 100644 library/std/src/sys/anonymous_pipe/unsupported.rs delete mode 100644 library/std/src/sys/pal/motor/pipe.rs delete mode 100644 library/std/src/sys/pal/unix/pipe.rs rename library/std/src/sys/{anonymous_pipe => pipe}/mod.rs (57%) create mode 100644 library/std/src/sys/pipe/motor.rs create mode 100644 library/std/src/sys/pipe/unix.rs rename library/std/src/sys/{pal/unsupported/pipe.rs => pipe/unsupported.rs} (72%) rename library/std/src/sys/{anonymous_pipe => pipe}/windows.rs (85%) rename library/std/src/sys/{pal/windows/pipe.rs => process/windows/child_pipe.rs} (91%) diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs index 16727d445416c..8457e7bceddca 100644 --- a/library/std/src/io/pipe.rs +++ b/library/std/src/io/pipe.rs @@ -1,5 +1,5 @@ use crate::io; -use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; +use crate::sys::pipe as imp; use crate::sys_common::{FromInner, IntoInner}; /// Creates an anonymous pipe. @@ -84,39 +84,39 @@ use crate::sys_common::{FromInner, IntoInner}; #[stable(feature = "anonymous_pipe", since = "1.87.0")] #[inline] pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { - pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) + imp::pipe().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) } /// Read end of an anonymous pipe. #[stable(feature = "anonymous_pipe", since = "1.87.0")] #[derive(Debug)] -pub struct PipeReader(pub(crate) AnonPipe); +pub struct PipeReader(pub(crate) imp::Pipe); /// Write end of an anonymous pipe. #[stable(feature = "anonymous_pipe", since = "1.87.0")] #[derive(Debug)] -pub struct PipeWriter(pub(crate) AnonPipe); +pub struct PipeWriter(pub(crate) imp::Pipe); -impl FromInner for PipeReader { - fn from_inner(inner: AnonPipe) -> Self { +impl FromInner for PipeReader { + fn from_inner(inner: imp::Pipe) -> Self { Self(inner) } } -impl IntoInner for PipeReader { - fn into_inner(self) -> AnonPipe { +impl IntoInner for PipeReader { + fn into_inner(self) -> imp::Pipe { self.0 } } -impl FromInner for PipeWriter { - fn from_inner(inner: AnonPipe) -> Self { +impl FromInner for PipeWriter { + fn from_inner(inner: imp::Pipe) -> Self { Self(inner) } } -impl IntoInner for PipeWriter { - fn into_inner(self) -> AnonPipe { +impl IntoInner for PipeWriter { + fn into_inner(self) -> imp::Pipe { self.0 } } diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index ee0c460f7dfa7..74b46d2975716 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -8,6 +8,7 @@ use crate::ffi::OsStr; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sealed::Sealed; +use crate::sys::process::ChildPipe; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{io, process, sys}; @@ -511,7 +512,7 @@ impl From for OwnedFd { /// Takes ownership of a [`ChildStdin`](crate::process::ChildStdin)'s file descriptor. #[inline] fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd { - child_stdin.into_inner().into_inner().into_inner() + child_stdin.into_inner().into_inner() } } @@ -523,8 +524,7 @@ impl From for OwnedFd { impl From for process::ChildStdin { #[inline] fn from(fd: OwnedFd) -> process::ChildStdin { - let fd = sys::fd::FileDesc::from_inner(fd); - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = ChildPipe::from_inner(fd); process::ChildStdin::from_inner(pipe) } } @@ -542,7 +542,7 @@ impl From for OwnedFd { /// Takes ownership of a [`ChildStdout`](crate::process::ChildStdout)'s file descriptor. #[inline] fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd { - child_stdout.into_inner().into_inner().into_inner() + child_stdout.into_inner().into_inner() } } @@ -554,8 +554,7 @@ impl From for OwnedFd { impl From for process::ChildStdout { #[inline] fn from(fd: OwnedFd) -> process::ChildStdout { - let fd = sys::fd::FileDesc::from_inner(fd); - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = ChildPipe::from_inner(fd); process::ChildStdout::from_inner(pipe) } } @@ -573,7 +572,7 @@ impl From for OwnedFd { /// Takes ownership of a [`ChildStderr`](crate::process::ChildStderr)'s file descriptor. #[inline] fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd { - child_stderr.into_inner().into_inner().into_inner() + child_stderr.into_inner().into_inner() } } @@ -585,8 +584,7 @@ impl From for OwnedFd { impl From for process::ChildStderr { #[inline] fn from(fd: OwnedFd) -> process::ChildStderr { - let fd = sys::fd::FileDesc::from_inner(fd); - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = ChildPipe::from_inner(fd); process::ChildStderr::from_inner(pipe) } } diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index f21ed51606f6d..b34b5d2802ca0 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -117,7 +117,7 @@ impl IntoRawHandle for process::ChildStderr { impl From for process::ChildStdin { fn from(handle: OwnedHandle) -> process::ChildStdin { let handle = sys::handle::Handle::from_inner(handle); - let pipe = sys::pipe::AnonPipe::from_inner(handle); + let pipe = sys::process::ChildPipe::from_inner(handle); process::ChildStdin::from_inner(pipe) } } @@ -130,7 +130,7 @@ impl From for process::ChildStdin { impl From for process::ChildStdout { fn from(handle: OwnedHandle) -> process::ChildStdout { let handle = sys::handle::Handle::from_inner(handle); - let pipe = sys::pipe::AnonPipe::from_inner(handle); + let pipe = sys::process::ChildPipe::from_inner(handle); process::ChildStdout::from_inner(pipe) } } @@ -143,7 +143,7 @@ impl From for process::ChildStdout { impl From for process::ChildStderr { fn from(handle: OwnedHandle) -> process::ChildStderr { let handle = sys::handle::Handle::from_inner(handle); - let pipe = sys::pipe::AnonPipe::from_inner(handle); + let pipe = sys::process::ChildPipe::from_inner(handle); process::ChildStderr::from_inner(pipe) } } diff --git a/library/std/src/process.rs b/library/std/src/process.rs index dbcf2684c6fb1..68bdf04d08b39 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -166,7 +166,6 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::num::NonZero; use crate::path::Path; -use crate::sys::pipe::{AnonPipe, read2}; use crate::sys::process as imp; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{fmt, fs, str}; @@ -300,9 +299,9 @@ impl fmt::Debug for Child { /// /// Used to pass pipe handles between this module and [`imp`]. pub(crate) struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, } /// A handle to a child process's standard input (stdin). @@ -317,7 +316,7 @@ pub(crate) struct StdioPipes { /// [dropped]: Drop #[stable(feature = "process", since = "1.0.0")] pub struct ChildStdin { - inner: AnonPipe, + inner: imp::ChildPipe, } // In addition to the `impl`s here, `ChildStdin` also has `impl`s for @@ -366,21 +365,21 @@ impl Write for &ChildStdin { } } -impl AsInner for ChildStdin { +impl AsInner for ChildStdin { #[inline] - fn as_inner(&self) -> &AnonPipe { + fn as_inner(&self) -> &imp::ChildPipe { &self.inner } } -impl IntoInner for ChildStdin { - fn into_inner(self) -> AnonPipe { +impl IntoInner for ChildStdin { + fn into_inner(self) -> imp::ChildPipe { self.inner } } -impl FromInner for ChildStdin { - fn from_inner(pipe: AnonPipe) -> ChildStdin { +impl FromInner for ChildStdin { + fn from_inner(pipe: imp::ChildPipe) -> ChildStdin { ChildStdin { inner: pipe } } } @@ -403,7 +402,7 @@ impl fmt::Debug for ChildStdin { /// [dropped]: Drop #[stable(feature = "process", since = "1.0.0")] pub struct ChildStdout { - inner: AnonPipe, + inner: imp::ChildPipe, } // In addition to the `impl`s here, `ChildStdout` also has `impl`s for @@ -436,21 +435,21 @@ impl Read for ChildStdout { } } -impl AsInner for ChildStdout { +impl AsInner for ChildStdout { #[inline] - fn as_inner(&self) -> &AnonPipe { + fn as_inner(&self) -> &imp::ChildPipe { &self.inner } } -impl IntoInner for ChildStdout { - fn into_inner(self) -> AnonPipe { +impl IntoInner for ChildStdout { + fn into_inner(self) -> imp::ChildPipe { self.inner } } -impl FromInner for ChildStdout { - fn from_inner(pipe: AnonPipe) -> ChildStdout { +impl FromInner for ChildStdout { + fn from_inner(pipe: imp::ChildPipe) -> ChildStdout { ChildStdout { inner: pipe } } } @@ -473,7 +472,7 @@ impl fmt::Debug for ChildStdout { /// [dropped]: Drop #[stable(feature = "process", since = "1.0.0")] pub struct ChildStderr { - inner: AnonPipe, + inner: imp::ChildPipe, } // In addition to the `impl`s here, `ChildStderr` also has `impl`s for @@ -506,21 +505,21 @@ impl Read for ChildStderr { } } -impl AsInner for ChildStderr { +impl AsInner for ChildStderr { #[inline] - fn as_inner(&self) -> &AnonPipe { + fn as_inner(&self) -> &imp::ChildPipe { &self.inner } } -impl IntoInner for ChildStderr { - fn into_inner(self) -> AnonPipe { +impl IntoInner for ChildStderr { + fn into_inner(self) -> imp::ChildPipe { self.inner } } -impl FromInner for ChildStderr { - fn from_inner(pipe: AnonPipe) -> ChildStderr { +impl FromInner for ChildStderr { + fn from_inner(pipe: imp::ChildPipe) -> ChildStderr { ChildStderr { inner: pipe } } } @@ -2380,7 +2379,7 @@ impl Child { res.unwrap(); } (Some(out), Some(err)) => { - let res = read2(out.inner, &mut stdout, err.inner, &mut stderr); + let res = imp::read_output(out.inner, &mut stdout, err.inner, &mut stderr); res.unwrap(); } } diff --git a/library/std/src/sys/anonymous_pipe/motor.rs b/library/std/src/sys/anonymous_pipe/motor.rs deleted file mode 100644 index dfe10f7fafe49..0000000000000 --- a/library/std/src/sys/anonymous_pipe/motor.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::io; -use crate::sys::fd::FileDesc; -use crate::sys::pipe::anon_pipe; -use crate::sys_common::IntoInner; - -pub type AnonPipe = FileDesc; - -#[inline] -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) -} diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs deleted file mode 100644 index dfe10f7fafe49..0000000000000 --- a/library/std/src/sys/anonymous_pipe/unix.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::io; -use crate::sys::fd::FileDesc; -use crate::sys::pipe::anon_pipe; -use crate::sys_common::IntoInner; - -pub type AnonPipe = FileDesc; - -#[inline] -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) -} diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs deleted file mode 100644 index a0805ba9540e0..0000000000000 --- a/library/std/src/sys/anonymous_pipe/unsupported.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::io; -pub use crate::sys::pipe::AnonPipe; - -#[inline] -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - Err(io::Error::UNSUPPORTED_PLATFORM) -} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 2dbdc8a4e026e..3c44543bce321 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -13,7 +13,6 @@ mod pal; mod alloc; mod personality; -pub mod anonymous_pipe; pub mod args; pub mod backtrace; pub mod cmath; @@ -26,6 +25,7 @@ pub mod io; pub mod net; pub mod os_str; pub mod path; +pub mod pipe; pub mod platform_version; pub mod process; pub mod random; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 3ddf6e5acb02e..52bcd8da24209 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -23,8 +23,6 @@ use crate::sys::env; pub mod futex; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod time; pub fn unsupported() -> crate::io::Result { diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs index 32f95df6ad082..9bca264d78ea9 100644 --- a/library/std/src/sys/pal/motor/mod.rs +++ b/library/std/src/sys/pal/motor/mod.rs @@ -1,7 +1,6 @@ #![allow(unsafe_op_in_unsafe_fn)] pub mod os; -pub mod pipe; pub mod time; pub use moto_rt::futex; diff --git a/library/std/src/sys/pal/motor/pipe.rs b/library/std/src/sys/pal/motor/pipe.rs deleted file mode 100644 index d3be6ddf1573e..0000000000000 --- a/library/std/src/sys/pal/motor/pipe.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::fd::FileDesc; -use crate::sys::map_motor_error; -use crate::sys_common::{FromInner, IntoInner}; - -#[derive(Debug)] -pub struct AnonPipe(FileDesc); - -impl From for AnonPipe { - fn from(rt_fd: moto_rt::RtFd) -> AnonPipe { - unsafe { AnonPipe::from_raw_fd(rt_fd) } - } -} - -impl AnonPipe { - pub fn read(&self, buf: &mut [u8]) -> io::Result { - moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error) - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - crate::io::default_read_buf(|buf| self.read(buf), cursor) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|b| self.read(b), bufs) - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|b| self.write(b), bufs) - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - let mut temp_vec = Vec::new(); - let mut size = 0_usize; - loop { - temp_vec.resize(256, 0_u8); - match self.read(&mut temp_vec[..]) { - Ok(sz) => { - if sz == 0 { - return Ok(size); - } - size += sz; - temp_vec.truncate(sz); - buf.append(&mut temp_vec); - } - Err(err) => { - if size != 0 { - return Ok(size); - } else { - return Err(err); - } - } - } - } - } -} - -impl AsRawFd for AnonPipe { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl FromRawFd for AnonPipe { - unsafe fn from_raw_fd(fd: RawFd) -> Self { - let desc = FileDesc::from_raw_fd(fd); - Self(desc) - } -} - -impl IntoRawFd for AnonPipe { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl AsFd for AnonPipe { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> OwnedFd { - self.0.into_inner() - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner for AnonPipe { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self(FileDesc::from_inner(owned_fd)) - } -} - -pub fn read2(_p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { - Err(io::Error::from_raw_os_error(moto_rt::E_NOT_IMPLEMENTED.into())) -} - -#[inline] -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - Err(io::Error::UNSUPPORTED_PLATFORM) -} diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 9a33873af581f..5d57be59aadf7 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -11,8 +11,6 @@ use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; pub mod abi; mod libunwind_integration; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod thread_parking; pub mod time; pub mod waitqueue; diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 9ca6dc581183d..33df9116f5c2d 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -18,8 +18,6 @@ pub mod itron { // `crate::sys::error` pub(crate) mod error; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub use self::itron::thread_parking; pub mod time; diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index dd0155265da63..fe2dd9c6c493e 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -7,8 +7,6 @@ #![allow(dead_code)] pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; #[allow(non_upper_case_globals)] #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/trusty/mod.rs b/library/std/src/sys/pal/trusty/mod.rs index cf0c098f8a2f3..76a3a75b10c1a 100644 --- a/library/std/src/sys/pal/trusty/mod.rs +++ b/library/std/src/sys/pal/trusty/mod.rs @@ -5,8 +5,6 @@ mod common; #[path = "../unsupported/os.rs"] pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index ebd311db1e12c..e8236437b9c97 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -15,8 +15,6 @@ pub mod helpers; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod time; #[cfg(test)] diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 81533d593131b..a6c5decf2d341 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -8,7 +8,6 @@ pub mod futex; #[cfg(target_os = "linux")] pub mod linux; pub mod os; -pub mod pipe; pub mod stack_overflow; pub mod sync; pub mod thread_parking; diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs deleted file mode 100644 index 4798acf9dad6b..0000000000000 --- a/library/std/src/sys/pal/unix/pipe.rs +++ /dev/null @@ -1,184 +0,0 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::fd::FileDesc; -use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{FromInner, IntoInner}; - -//////////////////////////////////////////////////////////////////////////////// -// Anonymous pipes -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Debug)] -pub struct AnonPipe(FileDesc); - -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - let mut fds = [0; 2]; - - // The only known way right now to create atomically set the CLOEXEC flag is - // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9 - // and musl 0.9.3, and some other targets also have it. - cfg_select! { - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "redox" - ) => { - unsafe { - cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?; - Ok((AnonPipe(FileDesc::from_raw_fd(fds[0])), AnonPipe(FileDesc::from_raw_fd(fds[1])))) - } - } - _ => { - unsafe { - cvt(libc::pipe(fds.as_mut_ptr()))?; - - let fd0 = FileDesc::from_raw_fd(fds[0]); - let fd1 = FileDesc::from_raw_fd(fds[1]); - fd0.set_cloexec()?; - fd1.set_cloexec()?; - Ok((AnonPipe(fd0), AnonPipe(fd1))) - } - } - } -} - -impl AnonPipe { - #[allow(dead_code)] - // FIXME: This function seems legitimately unused. - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(Self) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - self.0.read_to_end(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - #[allow(dead_code)] - // FIXME: This function seems legitimately unused. - pub fn as_file_desc(&self) -> &FileDesc { - &self.0 - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> io::Result<()> { - // Set both pipes into nonblocking mode as we're gonna be reading from both - // in the `select` loop below, and we wouldn't want one to block the other! - let p1 = p1.into_inner(); - let p2 = p2.into_inner(); - p1.set_nonblocking(true)?; - p2.set_nonblocking(true)?; - - let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; - fds[0].fd = p1.as_raw_fd(); - fds[0].events = libc::POLLIN; - fds[1].fd = p2.as_raw_fd(); - fds[1].events = libc::POLLIN; - loop { - // wait for either pipe to become readable using `poll` - cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?; - - if fds[0].revents != 0 && read(&p1, v1)? { - p2.set_nonblocking(false)?; - return p2.read_to_end(v2).map(drop); - } - if fds[1].revents != 0 && read(&p2, v2)? { - p1.set_nonblocking(false)?; - return p1.read_to_end(v1).map(drop); - } - } - - // Read as much as we can from each pipe, ignoring EWOULDBLOCK or - // EAGAIN. If we hit EOF, then this will happen because the underlying - // reader will return Ok(0), in which case we'll see `Ok` ourselves. In - // this case we flip the other fd back into blocking mode and read - // whatever's leftover on that file descriptor. - fn read(fd: &FileDesc, dst: &mut Vec) -> Result { - match fd.read_to_end(dst) { - Ok(_) => Ok(true), - Err(e) => { - if e.raw_os_error() == Some(libc::EWOULDBLOCK) - || e.raw_os_error() == Some(libc::EAGAIN) - { - Ok(false) - } else { - Err(e) - } - } - } - } -} - -impl AsRawFd for AnonPipe { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl AsFd for AnonPipe { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl IntoRawFd for AnonPipe { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for AnonPipe { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } -} - -impl FromInner for AnonPipe { - fn from_inner(fd: FileDesc) -> Self { - Self(fd) - } -} diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index e64bbc7c6169d..c33d2e5fb02a1 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -1,7 +1,6 @@ #![deny(unsafe_op_in_unsafe_fn)] pub mod os; -pub mod pipe; pub mod time; mod common; diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 42629a91e1afe..5d36687df0977 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -9,8 +9,6 @@ pub mod futex; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod stack_overflow; #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index a20cd0e9ac776..80429a9aae18d 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -18,8 +18,6 @@ #[path = "../unsupported/os.rs"] pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index a5f060080130f..32bd6ea3a4f6c 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -19,7 +19,6 @@ pub mod c; pub mod futex; pub mod handle; pub mod os; -pub mod pipe; pub mod time; cfg_select! { // We don't care about printing nice error messages for panic=immediate-abort diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index e673157e0eb55..077cff1ee0f21 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -3,8 +3,6 @@ use crate::os::xous::ffi::exit; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 9069c8d12fa1d..6dece1055a089 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -12,8 +12,6 @@ pub const WORD_SIZE: usize = size_of::(); pub mod abi; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/pipe/mod.rs similarity index 57% rename from library/std/src/sys/anonymous_pipe/mod.rs rename to library/std/src/sys/pipe/mod.rs index 64b2c014b54fe..85228963b4adf 100644 --- a/library/std/src/sys/anonymous_pipe/mod.rs +++ b/library/std/src/sys/pipe/mod.rs @@ -3,18 +3,18 @@ cfg_select! { unix => { mod unix; - pub use unix::{AnonPipe, pipe}; + pub use unix::{Pipe, pipe}; } windows => { mod windows; - pub use windows::{AnonPipe, pipe}; + pub use windows::{Pipe, pipe}; } target_os = "motor" => { mod motor; - pub use motor::{AnonPipe, pipe}; + pub use motor::{Pipe, pipe}; } _ => { mod unsupported; - pub use unsupported::{AnonPipe, pipe}; + pub use unsupported::{Pipe, pipe}; } } diff --git a/library/std/src/sys/pipe/motor.rs b/library/std/src/sys/pipe/motor.rs new file mode 100644 index 0000000000000..50787c5801339 --- /dev/null +++ b/library/std/src/sys/pipe/motor.rs @@ -0,0 +1,9 @@ +use crate::io; +use crate::sys::fd::FileDesc; + +pub type Pipe = FileDesc; + +#[inline] +pub fn pipe() -> io::Result<(Pipe, Pipe)> { + Err(io::Error::UNSUPPORTED_PLATFORM) +} diff --git a/library/std/src/sys/pipe/unix.rs b/library/std/src/sys/pipe/unix.rs new file mode 100644 index 0000000000000..bb97e806a0400 --- /dev/null +++ b/library/std/src/sys/pipe/unix.rs @@ -0,0 +1,44 @@ +use crate::io; +use crate::os::fd::FromRawFd; +use crate::sys::fd::FileDesc; +use crate::sys::pal::cvt; + +pub type Pipe = FileDesc; + +pub fn pipe() -> io::Result<(Pipe, Pipe)> { + let mut fds = [0; 2]; + + // The only known way right now to create atomically set the CLOEXEC flag is + // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9 + // and musl 0.9.3, and some other targets also have it. + cfg_select! { + any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "redox" + ) => { + unsafe { + cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?; + Ok((Pipe::from_raw_fd(fds[0]), Pipe::from_raw_fd(fds[1]))) + } + } + _ => { + unsafe { + cvt(libc::pipe(fds.as_mut_ptr()))?; + + let fd0 = Pipe::from_raw_fd(fds[0]); + let fd1 = Pipe::from_raw_fd(fds[1]); + fd0.set_cloexec()?; + fd1.set_cloexec()?; + Ok((fd0, fd1)) + } + } + } +} diff --git a/library/std/src/sys/pal/unsupported/pipe.rs b/library/std/src/sys/pipe/unsupported.rs similarity index 72% rename from library/std/src/sys/pal/unsupported/pipe.rs rename to library/std/src/sys/pipe/unsupported.rs index 988e551de5223..115e913e0f33a 100644 --- a/library/std/src/sys/pal/unsupported/pipe.rs +++ b/library/std/src/sys/pipe/unsupported.rs @@ -1,16 +1,14 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::sys_common::{FromInner, IntoInner}; -pub struct AnonPipe(!); +pub struct Pipe(!); -impl fmt::Debug for AnonPipe { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } +#[inline] +pub fn pipe() -> io::Result<(Pipe, Pipe)> { + Err(io::Error::UNSUPPORTED_PLATFORM) } -impl AnonPipe { +impl Pipe { pub fn try_clone(&self) -> io::Result { self.0 } @@ -52,56 +50,52 @@ impl AnonPipe { } } -pub fn read2(p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { - match p1.0 {} -} - -impl FromInner for AnonPipe { - fn from_inner(inner: !) -> Self { - inner - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> ! { +impl fmt::Debug for Pipe { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { self.0 } } #[cfg(any(unix, target_os = "hermit", target_os = "wasi"))] mod unix_traits { - use super::AnonPipe; + use super::Pipe; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; - use crate::sys_common::FromInner; + use crate::sys_common::{FromInner, IntoInner}; - impl AsRawFd for AnonPipe { + impl AsRawFd for Pipe { #[inline] fn as_raw_fd(&self) -> RawFd { self.0 } } - impl AsFd for AnonPipe { + impl AsFd for Pipe { fn as_fd(&self) -> BorrowedFd<'_> { self.0 } } - impl IntoRawFd for AnonPipe { + impl IntoRawFd for Pipe { fn into_raw_fd(self) -> RawFd { self.0 } } - impl FromRawFd for AnonPipe { + impl FromRawFd for Pipe { unsafe fn from_raw_fd(_: RawFd) -> Self { panic!("creating pipe on this platform is unsupported!") } } - impl FromInner for AnonPipe { + impl FromInner for Pipe { fn from_inner(_: OwnedFd) -> Self { panic!("creating pipe on this platform is unsupported!") } } + + impl IntoInner for Pipe { + fn into_inner(self) -> OwnedFd { + self.0 + } + } } diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/pipe/windows.rs similarity index 85% rename from library/std/src/sys/anonymous_pipe/windows.rs rename to library/std/src/sys/pipe/windows.rs index bdda7ffc5d251..5bd90b43d7427 100644 --- a/library/std/src/sys/anonymous_pipe/windows.rs +++ b/library/std/src/sys/pipe/windows.rs @@ -3,9 +3,9 @@ use crate::sys::c; use crate::sys::handle::Handle; use crate::{io, ptr}; -pub type AnonPipe = Handle; +pub type Pipe = Handle; -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { +pub fn pipe() -> io::Result<(Pipe, Pipe)> { let mut read_pipe = c::INVALID_HANDLE_VALUE; let mut write_pipe = c::INVALID_HANDLE_VALUE; diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index 92e459298fc4c..121d3bc9d5c3c 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -28,7 +28,8 @@ mod env; pub use env::CommandEnvs; pub use imp::{ - Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, + ChildPipe, Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, + read_output, }; #[cfg(any( @@ -45,8 +46,6 @@ pub use imp::{ target_os = "motor" ))] pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec)> { - use crate::sys::pipe::read2; - let (mut process, mut pipes) = cmd.spawn(Stdio::MakePipe, false)?; drop(pipes.stdin.take()); @@ -62,7 +61,7 @@ pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec< res.unwrap(); } (Some(out), Some(err)) => { - let res = read2(out, &mut stdout, err, &mut stderr); + let res = read_output(out, &mut stdout, err, &mut stderr); res.unwrap(); } } diff --git a/library/std/src/sys/process/motor.rs b/library/std/src/sys/process/motor.rs index 949a9d4942901..7a23a5d902c0a 100644 --- a/library/std/src/sys/process/motor.rs +++ b/library/std/src/sys/process/motor.rs @@ -9,7 +9,6 @@ use crate::path::Path; use crate::process::StdioPipes; use crate::sys::fs::File; use crate::sys::map_motor_error; -use crate::sys::pipe::AnonPipe; use crate::sys_common::{AsInner, FromInner}; use crate::{fmt, io}; @@ -150,20 +149,26 @@ impl Command { Ok(( Process { handle }, StdioPipes { - stdin: if stdin >= 0 { Some(stdin.into()) } else { None }, - stdout: if stdout >= 0 { Some(stdout.into()) } else { None }, - stderr: if stderr >= 0 { Some(stderr.into()) } else { None }, + stdin: if stdin >= 0 { + Some(unsafe { ChildPipe::from_raw_fd(stdin) }) + } else { + None + }, + stdout: if stdout >= 0 { + Some(unsafe { ChildPipe::from_raw_fd(stdout) }) + } else { + None + }, + stderr: if stderr >= 0 { + Some(unsafe { ChildPipe::from_raw_fd(stderr) }) + } else { + None + }, }, )) } } -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - unsafe { Stdio::Fd(crate::sys::fd::FileDesc::from_raw_fd(pipe.into_raw_fd())) } - } -} - impl From for Stdio { fn from(fd: crate::sys::fd::FileDesc) -> Stdio { Stdio::Fd(fd) @@ -315,3 +320,14 @@ impl<'a> fmt::Debug for CommandArgs<'a> { f.debug_list().entries(self.iter.clone()).finish() } } + +pub type ChildPipe = crate::sys::pipe::Pipe; + +pub fn read_output( + _out: ChildPipe, + _stdout: &mut Vec, + _err: ChildPipe, + _stderr: &mut Vec, +) -> io::Result<()> { + Err(io::Error::from_raw_os_error(moto_rt::E_NOT_IMPLEMENTED.into())) +} diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs index 8d44292611bcb..0e10bc02a0b90 100644 --- a/library/std/src/sys/process/uefi.rs +++ b/library/std/src/sys/process/uefi.rs @@ -10,7 +10,6 @@ use crate::process::StdioPipes; use crate::sys::fs::File; use crate::sys::pal::helpers; use crate::sys::pal::os::error_string; -use crate::sys::pipe::AnonPipe; use crate::sys::unsupported; use crate::{fmt, io}; @@ -204,8 +203,8 @@ pub fn output(command: &mut Command) -> io::Result<(ExitStatus, Vec, Vec Ok((ExitStatus(stat), stdout, stderr)) } -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { +impl From for Stdio { + fn from(pipe: ChildPipe) -> Stdio { pipe.diverge() } } @@ -356,6 +355,17 @@ impl<'a> fmt::Debug for CommandArgs<'a> { } } +pub type ChildPipe = crate::sys::pipe::Pipe; + +pub fn read_output( + out: ChildPipe, + _stdout: &mut Vec, + _err: ChildPipe, + _stderr: &mut Vec, +) -> io::Result<()> { + match out.diverge() {} +} + #[allow(dead_code)] mod uefi_command_internal { use r_efi::protocols::{loaded_image, simple_text_input, simple_text_output}; diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index 44d54aaf51512..d4344d1191a43 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -10,14 +10,15 @@ use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::os::unix::prelude::*; use crate::path::Path; use crate::process::StdioPipes; +use crate::sys::cvt_r; use crate::sys::fd::FileDesc; use crate::sys::fs::File; #[cfg(not(target_os = "fuchsia"))] use crate::sys::fs::OpenOptions; -use crate::sys::pipe::{self, AnonPipe}; +use crate::sys::pipe::pipe; use crate::sys::process::env::{CommandEnv, CommandEnvs}; use crate::sys_common::{FromInner, IntoInner}; -use crate::{fmt, io}; +use crate::{fmt, io, mem}; mod cstring_array; @@ -393,7 +394,7 @@ fn construct_envp(env: BTreeMap, saw_nul: &mut bool) -> CStr } impl Stdio { - pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option)> { + pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option)> { match *self { Stdio::Inherit => Ok((ChildStdio::Inherit, None)), @@ -418,9 +419,9 @@ impl Stdio { } Stdio::MakePipe => { - let (reader, writer) = pipe::anon_pipe()?; + let (reader, writer) = pipe()?; let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; - Ok((ChildStdio::Owned(theirs.into_inner()), Some(ours))) + Ok((ChildStdio::Owned(theirs), Some(ours))) } #[cfg(not(target_os = "fuchsia"))] @@ -438,12 +439,6 @@ impl Stdio { } } -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Fd(pipe.into_inner()) - } -} - impl From for Stdio { fn from(fd: FileDesc) -> Stdio { Stdio::Fd(fd) @@ -632,3 +627,56 @@ impl<'a> fmt::Debug for CommandArgs<'a> { f.debug_list().entries(self.iter.clone()).finish() } } + +pub type ChildPipe = crate::sys::pipe::Pipe; + +pub fn read_output( + out: ChildPipe, + stdout: &mut Vec, + err: ChildPipe, + stderr: &mut Vec, +) -> io::Result<()> { + // Set both pipes into nonblocking mode as we're gonna be reading from both + // in the `select` loop below, and we wouldn't want one to block the other! + out.set_nonblocking(true)?; + err.set_nonblocking(true)?; + + let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; + fds[0].fd = out.as_raw_fd(); + fds[0].events = libc::POLLIN; + fds[1].fd = err.as_raw_fd(); + fds[1].events = libc::POLLIN; + loop { + // wait for either pipe to become readable using `poll` + cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?; + + if fds[0].revents != 0 && read(&out, stdout)? { + err.set_nonblocking(false)?; + return err.read_to_end(stderr).map(drop); + } + if fds[1].revents != 0 && read(&err, stderr)? { + out.set_nonblocking(false)?; + return out.read_to_end(stdout).map(drop); + } + } + + // Read as much as we can from each pipe, ignoring EWOULDBLOCK or + // EAGAIN. If we hit EOF, then this will happen because the underlying + // reader will return Ok(0), in which case we'll see `Ok` ourselves. In + // this case we flip the other fd back into blocking mode and read + // whatever's leftover on that file descriptor. + fn read(fd: &FileDesc, dst: &mut Vec) -> Result { + match fd.read_to_end(dst) { + Ok(_) => Ok(true), + Err(e) => { + if e.raw_os_error() == Some(libc::EWOULDBLOCK) + || e.raw_os_error() == Some(libc::EAGAIN) + { + Ok(false) + } else { + Err(e) + } + } + } + } +} diff --git a/library/std/src/sys/process/unix/mod.rs b/library/std/src/sys/process/unix/mod.rs index cda1bf74f1cad..1938e8f4b737c 100644 --- a/library/std/src/sys/process/unix/mod.rs +++ b/library/std/src/sys/process/unix/mod.rs @@ -23,5 +23,5 @@ cfg_select! { pub use imp::{ExitStatus, ExitStatusError, Process}; -pub use self::common::{Command, CommandArgs, ExitCode, Stdio}; +pub use self::common::{ChildPipe, Command, CommandArgs, ExitCode, Stdio, read_output}; pub use crate::ffi::OsString as EnvKey; diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 7d944f2f7eef1..d14caf6dc88d0 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -78,7 +78,7 @@ impl Command { let (input, output) = sys::net::Socket::new_pair(libc::AF_UNIX, libc::SOCK_SEQPACKET)?; #[cfg(not(target_os = "linux"))] - let (input, output) = sys::pipe::anon_pipe()?; + let (input, output) = sys::pipe::pipe()?; // Whatever happens after the fork is almost for sure going to touch or // look at the environment in one way or another (PATH in `execvp` or diff --git a/library/std/src/sys/process/unsupported.rs b/library/std/src/sys/process/unsupported.rs index 2dfc676ec0059..455c38e55b7ba 100644 --- a/library/std/src/sys/process/unsupported.rs +++ b/library/std/src/sys/process/unsupported.rs @@ -5,7 +5,6 @@ use crate::num::NonZero; use crate::path::Path; use crate::process::StdioPipes; use crate::sys::fs::File; -use crate::sys::pipe::AnonPipe; use crate::sys::unsupported; use crate::{fmt, io}; @@ -107,8 +106,8 @@ pub fn output(_cmd: &mut Command) -> io::Result<(ExitStatus, Vec, Vec)> unsupported() } -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { +impl From for Stdio { + fn from(pipe: ChildPipe) -> Stdio { pipe.diverge() } } @@ -317,3 +316,14 @@ impl<'a> fmt::Debug for CommandArgs<'a> { f.debug_list().entries(self.iter.clone()).finish() } } + +pub type ChildPipe = crate::sys::pipe::Pipe; + +pub fn read_output( + out: ChildPipe, + _stdout: &mut Vec, + _err: ChildPipe, + _stderr: &mut Vec, +) -> io::Result<()> { + match out.diverge() {} +} diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index 6e8be21a1fa6d..85b9682320d28 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -23,11 +23,14 @@ use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::pal::api::{self, WinError, utf16}; use crate::sys::pal::{ensure_no_nuls, fill_utf16_buf}; -use crate::sys::pipe::{self, AnonPipe}; use crate::sys::{cvt, path, stdio}; use crate::sys_common::IntoInner; use crate::{cmp, env, fmt, ptr}; +mod child_pipe; + +pub use self::child_pipe::{ChildPipe, read_output}; + //////////////////////////////////////////////////////////////////////////////// // Command //////////////////////////////////////////////////////////////////////////////// @@ -167,7 +170,7 @@ pub enum Stdio { InheritSpecific { from_stdio_id: u32 }, Null, MakePipe, - Pipe(AnonPipe), + Pipe(ChildPipe), Handle(Handle), } @@ -596,7 +599,7 @@ fn program_exists(path: &Path) -> Option> { } impl Stdio { - fn to_handle(&self, stdio_id: u32, pipe: &mut Option) -> io::Result { + fn to_handle(&self, stdio_id: u32, pipe: &mut Option) -> io::Result { let use_stdio_id = |stdio_id| match stdio::get_handle(stdio_id) { Ok(io) => unsafe { let io = Handle::from_raw_handle(io); @@ -613,14 +616,15 @@ impl Stdio { Stdio::MakePipe => { let ours_readable = stdio_id != c::STD_INPUT_HANDLE; - let pipes = pipe::anon_pipe(ours_readable, true)?; + let pipes = child_pipe::child_pipe(ours_readable, true)?; *pipe = Some(pipes.ours); Ok(pipes.theirs.into_handle()) } Stdio::Pipe(ref source) => { let ours_readable = stdio_id != c::STD_INPUT_HANDLE; - pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::into_handle) + child_pipe::spawn_pipe_relay(source, ours_readable, true) + .map(ChildPipe::into_handle) } Stdio::Handle(ref handle) => handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS), @@ -639,8 +643,8 @@ impl Stdio { } } -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { +impl From for Stdio { + fn from(pipe: ChildPipe) -> Stdio { Stdio::Pipe(pipe) } } diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/process/windows/child_pipe.rs similarity index 91% rename from library/std/src/sys/pal/windows/pipe.rs rename to library/std/src/sys/process/windows/child_pipe.rs index 32cf4695d4a15..6b00c54407ad5 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/process/windows/child_pipe.rs @@ -6,39 +6,37 @@ use crate::sys::{api, c}; use crate::sys_common::{FromInner, IntoInner}; use crate::{mem, ptr}; -//////////////////////////////////////////////////////////////////////////////// -// Anonymous pipes -//////////////////////////////////////////////////////////////////////////////// - -pub struct AnonPipe { +pub struct ChildPipe { inner: Handle, } -impl IntoInner for AnonPipe { +impl IntoInner for ChildPipe { fn into_inner(self) -> Handle { self.inner } } -impl FromInner for AnonPipe { - fn from_inner(inner: Handle) -> AnonPipe { +impl FromInner for ChildPipe { + fn from_inner(inner: Handle) -> ChildPipe { Self { inner } } } -pub struct Pipes { - pub ours: AnonPipe, - pub theirs: AnonPipe, +pub(super) struct Pipes { + pub ours: ChildPipe, + pub theirs: ChildPipe, } -/// Although this looks similar to `anon_pipe` in the Unix module it's actually -/// subtly different. Here we'll return two pipes in the `Pipes` return value, -/// but one is intended for "us" where as the other is intended for "someone -/// else". +/// Creates an anonymous pipe suitable for communication with a child process. /// -/// Currently the only use case for this function is pipes for stdio on -/// processes in the standard library, so "ours" is the one that'll stay in our -/// process whereas "theirs" will be inherited to a child. +/// Windows unfortunately does not have a way of performing asynchronous operations +/// on a handle originally created for synchronous operation. As `read_output` can +/// only be correctly implemented with asynchronous reads but the pipe created by +/// `CreatePipe` is synchronous, we cannot use it (and thus [`io::pipe`]) to create +/// a pipe for communicating with a child. Instead, this function uses the NT API +/// to create a pipe where one pipe handle (`ours`) is asynchronous and one is +/// synchronous and can be inherited by a child for use as a console handle +/// (`theirs`). /// /// The ours/theirs pipes are *not* specifically readable or writable. Each /// one only supports a read or a write, but which is which depends on the @@ -50,7 +48,11 @@ pub struct Pipes { /// mode. This means that technically speaking it should only ever be used /// with `OVERLAPPED` instances, but also works out ok if it's only ever used /// once at a time (which we do indeed guarantee). -pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Result { +// FIXME(joboet): No, we don't guarantee that? E.g. `&Stdout` is both `Read` +// and `Sync`, so there could be multiple operations at the same +// time. All the functions below that forward to the inner handle +// methods could abort if used concurrently. +pub(super) fn child_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Result { // A 64kb pipe capacity is the same as a typical Linux default. const PIPE_BUFFER_CAPACITY: u32 = 64 * 1024; @@ -166,7 +168,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res } }; - Ok(Pipes { ours: AnonPipe { inner: ours }, theirs: AnonPipe { inner: theirs } }) + Ok(Pipes { ours: ChildPipe { inner: ours }, theirs: ChildPipe { inner: theirs } }) } } @@ -175,16 +177,16 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res /// /// This is achieved by creating a new set of pipes and spawning a thread that /// relays messages between the source and the synchronous pipe. -pub fn spawn_pipe_relay( - source: &AnonPipe, +pub(super) fn spawn_pipe_relay( + source: &ChildPipe, ours_readable: bool, their_handle_inheritable: bool, -) -> io::Result { +) -> io::Result { // We need this handle to live for the lifetime of the thread spawned below. let source = source.try_clone()?; // create a new pair of anon pipes. - let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?; + let Pipes { theirs, ours } = child_pipe(ours_readable, their_handle_inheritable)?; // Spawn a thread that passes messages from one pipe to the other. // Any errors will simply cause the thread to exit. @@ -210,7 +212,7 @@ pub fn spawn_pipe_relay( Ok(theirs) } -impl AnonPipe { +impl ChildPipe { pub fn handle(&self) -> &Handle { &self.inner } @@ -219,7 +221,7 @@ impl AnonPipe { } pub fn try_clone(&self) -> io::Result { - self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) + self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| ChildPipe { inner }) } pub fn read(&self, buf: &mut [u8]) -> io::Result { @@ -347,18 +349,17 @@ impl AnonPipe { // STEP 3: The callback. unsafe extern "system" fn callback( - dwErrorCode: u32, - dwNumberOfBytesTransferred: u32, - lpOverlapped: *mut c::OVERLAPPED, + error: u32, + transferred: u32, + overlapped: *mut c::OVERLAPPED, ) { // Set `async_result` using a pointer smuggled through `hEvent`. // SAFETY: // At this point, the OVERLAPPED struct will have been written to by the OS, // except for our `hEvent` field which we set to a valid AsyncResult pointer (see below) unsafe { - let result = - AsyncResult { error: dwErrorCode, transferred: dwNumberOfBytesTransferred }; - *(*lpOverlapped).hEvent.cast::>() = Some(result); + let result = AsyncResult { error, transferred }; + *(*overlapped).hEvent.cast::>() = Some(result); } } @@ -395,7 +396,12 @@ impl AnonPipe { } } -pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> io::Result<()> { +pub fn read_output( + p1: ChildPipe, + v1: &mut Vec, + p2: ChildPipe, + v2: &mut Vec, +) -> io::Result<()> { let p1 = p1.into_handle(); let p2 = p2.into_handle(); diff --git a/library/std/src/sys/stdio/motor.rs b/library/std/src/sys/stdio/motor.rs index 0a44feab723d0..e268bd5413df2 100644 --- a/library/std/src/sys/stdio/motor.rs +++ b/library/std/src/sys/stdio/motor.rs @@ -166,7 +166,7 @@ impl From for OwnedFd { impl From for process::ChildStdin { #[inline] fn from(fd: OwnedFd) -> process::ChildStdin { - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = sys::process::ChildPipe::from_inner(fd); process::ChildStdin::from_inner(pipe) } } @@ -196,7 +196,7 @@ impl From for OwnedFd { impl From for process::ChildStdout { #[inline] fn from(fd: OwnedFd) -> process::ChildStdout { - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = sys::process::ChildPipe::from_inner(fd); process::ChildStdout::from_inner(pipe) } } @@ -226,7 +226,7 @@ impl From for OwnedFd { impl From for process::ChildStderr { #[inline] fn from(fd: OwnedFd) -> process::ChildStderr { - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = sys::process::ChildPipe::from_inner(fd); process::ChildStderr::from_inner(pipe) } } From 653520afb2fb75df7e7e88ab068202cd66725fa2 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 20 Sep 2025 11:39:07 +0200 Subject: [PATCH 16/26] std: update pipe tests --- .../std/src/sys/io/kernel_copy/linux/tests.rs | 14 ++-------- library/std/src/sys/pal/windows/handle.rs | 3 --- .../std/src/sys/pal/windows/handle/tests.rs | 22 --------------- library/std/src/sys/process/windows/tests.rs | 27 ++++++++++++++++++- 4 files changed, 28 insertions(+), 38 deletions(-) delete mode 100644 library/std/src/sys/pal/windows/handle/tests.rs diff --git a/library/std/src/sys/io/kernel_copy/linux/tests.rs b/library/std/src/sys/io/kernel_copy/linux/tests.rs index 15dee768d928b..9b2d9cbef9902 100644 --- a/library/std/src/sys/io/kernel_copy/linux/tests.rs +++ b/library/std/src/sys/io/kernel_copy/linux/tests.rs @@ -88,13 +88,8 @@ fn dont_splice_pipes_from_files() -> Result<()> { use crate::io::SeekFrom; use crate::os::unix::fs::FileExt; - use crate::process::{ChildStdin, ChildStdout}; - use crate::sys_common::FromInner; - let (read_end, write_end) = crate::sys::pipe::anon_pipe()?; - - let mut read_end = ChildStdout::from_inner(read_end); - let mut write_end = ChildStdin::from_inner(write_end); + let (mut read_end, mut write_end) = crate::io::pipe()?; let tmp_path = tmpdir(); let file = tmp_path.join("to_be_modified"); @@ -220,13 +215,8 @@ fn bench_file_to_uds_copy(b: &mut test::Bencher) { fn bench_socket_pipe_socket_copy(b: &mut test::Bencher) { use super::CopyResult; use crate::io::ErrorKind; - use crate::process::{ChildStdin, ChildStdout}; - use crate::sys_common::FromInner; - - let (read_end, write_end) = crate::sys::pipe::anon_pipe().unwrap(); - let mut read_end = ChildStdout::from_inner(read_end); - let write_end = ChildStdin::from_inner(write_end); + let (mut read_end, write_end) = crate::io::pipe().unwrap(); let acceptor = crate::net::TcpListener::bind("localhost:0").unwrap(); let mut remote_end = crate::net::TcpStream::connect(acceptor.local_addr().unwrap()).unwrap(); diff --git a/library/std/src/sys/pal/windows/handle.rs b/library/std/src/sys/pal/windows/handle.rs index 712f41ba80308..fc17e0b5ebd3d 100644 --- a/library/std/src/sys/pal/windows/handle.rs +++ b/library/std/src/sys/pal/windows/handle.rs @@ -1,8 +1,5 @@ #![unstable(issue = "none", feature = "windows_handle")] -#[cfg(test)] -mod tests; - use core::ffi::c_void; use core::{cmp, mem, ptr}; diff --git a/library/std/src/sys/pal/windows/handle/tests.rs b/library/std/src/sys/pal/windows/handle/tests.rs deleted file mode 100644 index 0c976ed84e6ba..0000000000000 --- a/library/std/src/sys/pal/windows/handle/tests.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::sys::pipe::{Pipes, anon_pipe}; -use crate::{thread, time}; - -/// Test the synchronous fallback for overlapped I/O. -#[test] -fn overlapped_handle_fallback() { - // Create some pipes. `ours` will be asynchronous. - let Pipes { ours, theirs } = anon_pipe(true, false).unwrap(); - - let async_readable = ours.into_handle(); - let sync_writeable = theirs.into_handle(); - - thread::scope(|_| { - thread::sleep(time::Duration::from_millis(100)); - sync_writeable.write(b"hello world!").unwrap(); - }); - - // The pipe buffer starts empty so reading won't complete synchronously unless - // our fallback path works. - let mut buffer = [0u8; 1024]; - async_readable.read(&mut buffer).unwrap(); -} diff --git a/library/std/src/sys/process/windows/tests.rs b/library/std/src/sys/process/windows/tests.rs index a21afe3363c55..bc5e0d5c7fc97 100644 --- a/library/std/src/sys/process/windows/tests.rs +++ b/library/std/src/sys/process/windows/tests.rs @@ -1,8 +1,10 @@ +use super::child_pipe::{Pipes, child_pipe}; use super::{Arg, make_command_line}; -use crate::env; use crate::ffi::{OsStr, OsString}; use crate::os::windows::io::AsHandle; use crate::process::{Command, Stdio}; +use crate::time::Duration; +use crate::{env, thread}; #[test] fn test_raw_args() { @@ -233,3 +235,26 @@ fn windows_exe_resolver() { ); } } + +/// Test the synchronous fallback for overlapped I/O. +/// +/// While technically testing `Handle` functionality, this is situated in this +/// module to allow easier access to `ChildPipe`. +#[test] +fn overlapped_handle_fallback() { + // Create some pipes. `ours` will be asynchronous. + let Pipes { ours, theirs } = child_pipe(true, false).unwrap(); + + let async_readable = ours.into_handle(); + let sync_writeable = theirs.into_handle(); + + thread::scope(|_| { + thread::sleep(Duration::from_millis(100)); + sync_writeable.write(b"hello world!").unwrap(); + }); + + // The pipe buffer starts empty so reading won't complete synchronously unless + // our fallback path works. + let mut buffer = [0u8; 1024]; + async_readable.read(&mut buffer).unwrap(); +} From 3fd0fb600528c8c339a74d6b02ec90d6c0c51dcc Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 13 Dec 2025 04:54:48 +0000 Subject: [PATCH 17/26] Prepare for merging from rust-lang/rust This updates the rust-version file to dc47a69ed94bc88b10b7d500cceacf29b87bcbbe. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 2e81ccbba1846..e79ad9362c085 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f5209000832c9d3bc29c91f4daef4ca9f28dc797 +dc47a69ed94bc88b10b7d500cceacf29b87bcbbe From 25a6fca2df6a417e4364225c7feea7fb236d1623 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sat, 13 Dec 2025 13:10:35 +0100 Subject: [PATCH 18/26] Cleanup context --- compiler/rustc_attr_parsing/src/context.rs | 152 ++++++--------------- 1 file changed, 38 insertions(+), 114 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 074f3b4194aee..2cbc4a65b276e 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -434,6 +434,22 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { self.emit_err(UnknownMetaItem { span, item: found, expected: options }) } + fn emit_parse_error( + &self, + span: Span, + reason: AttributeParseErrorReason<'_>, + ) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + path: self.attr_path.clone(), + description: self.parsed_description, + reason, + suggestions: self.suggestions(), + }) + } + /// error that a string literal was expected. /// You can optionally give the literal you did find (which you found not to be a string literal) /// which can make better errors. For example, if the literal was a byte string it will suggest @@ -443,133 +459,56 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { span: Span, actual_literal: Option<&MetaItemLit>, ) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { + self.emit_parse_error( span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedStringLiteral { + AttributeParseErrorReason::ExpectedStringLiteral { byte_string: actual_literal.and_then(|i| { i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span)) }), }, - suggestions: self.suggestions(), - }) + ) } pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedIntegerLiteral, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral) } pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedList, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedList) } - pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span: args_span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedNoArgs, - suggestions: self.suggestions(), - }) + pub(crate) fn expected_no_args(&self, span: Span) -> ErrorGuaranteed { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs) } /// emit an error that a `name` was expected here pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedIdentifier, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIdentifier) } /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for /// a nicer error message talking about the specific name that was found lacking a value. pub(crate) fn expected_name_value(&self, span: Span, name: Option) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedNameValue(name), - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValue(name)) } /// emit an error that a `name = value` pair was found where that name was already seen. pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::DuplicateKey(key), - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::DuplicateKey(key)) } /// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser) /// was expected *not* to be a literal, but instead a meta item. pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::UnexpectedLiteral, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::UnexpectedLiteral) } pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedSingleArgument, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedSingleArgument) } pub(crate) fn expected_at_least_one_argument(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedAtLeastOneArgument) } /// produces an error along the lines of `expected one of [foo, meow]` @@ -578,19 +517,14 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { span: Span, possibilities: &[Symbol], ) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { + self.emit_parse_error( span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedSpecificArgument { + AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: false, list: false, }, - suggestions: self.suggestions(), - }) + ) } /// produces an error along the lines of `expected one of [foo, meow] as an argument`. @@ -600,19 +534,14 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { span: Span, possibilities: &[Symbol], ) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { + self.emit_parse_error( span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedSpecificArgument { + AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: false, list: true, }, - suggestions: self.suggestions(), - }) + ) } /// produces an error along the lines of `expected one of ["foo", "meow"]` @@ -621,19 +550,14 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { span: Span, possibilities: &[Symbol], ) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { + self.emit_parse_error( span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedSpecificArgument { + AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: true, list: false, }, - suggestions: self.suggestions(), - }) + ) } pub(crate) fn warn_empty_attribute(&mut self, span: Span) { From ebbb4d5ea4a52b048d4d25c16a118fc6d8252717 Mon Sep 17 00:00:00 2001 From: increasing Date: Thu, 11 Dec 2025 19:16:55 +0100 Subject: [PATCH 19/26] replace addr_of_mut with &raw mut in maybeuninit docs --- library/core/src/mem/maybe_uninit.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index e00cf45fcab20..1f066697fa487 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -160,11 +160,10 @@ use crate::{fmt, intrinsics, ptr, slice}; /// /// ## Initializing a struct field-by-field /// -/// You can use `MaybeUninit`, and the [`std::ptr::addr_of_mut`] macro, to initialize structs field by field: +/// You can use `MaybeUninit` and the [`&raw mut`] syntax to initialize structs field by field: /// /// ```rust /// use std::mem::MaybeUninit; -/// use std::ptr::addr_of_mut; /// /// #[derive(Debug, PartialEq)] /// pub struct Foo { @@ -179,11 +178,11 @@ use crate::{fmt, intrinsics, ptr, slice}; /// // Initializing the `name` field /// // Using `write` instead of assignment via `=` to not call `drop` on the /// // old, uninitialized value. -/// unsafe { addr_of_mut!((*ptr).name).write("Bob".to_string()); } +/// unsafe { (&raw mut (*ptr).name).write("Bob".to_string()); } /// /// // Initializing the `list` field /// // If there is a panic here, then the `String` in the `name` field leaks. -/// unsafe { addr_of_mut!((*ptr).list).write(vec![0, 1, 2]); } +/// unsafe { (&raw mut (*ptr).list).write(vec![0, 1, 2]); } /// /// // All the fields are initialized, so we call `assume_init` to get an initialized Foo. /// unsafe { uninit.assume_init() } @@ -197,7 +196,7 @@ use crate::{fmt, intrinsics, ptr, slice}; /// } /// ); /// ``` -/// [`std::ptr::addr_of_mut`]: crate::ptr::addr_of_mut +/// [`&raw mut`]: https://doc.rust-lang.org/reference/types/pointer.html#r-type.pointer.raw.constructor /// [ub]: ../../reference/behavior-considered-undefined.html /// /// # Layout From 1da1a39c9ae2ad51bc5bb438e45658f965ac530e Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sat, 13 Dec 2025 13:18:59 +0100 Subject: [PATCH 20/26] Remove `UnknownMetaItem` error --- compiler/rustc_attr_parsing/messages.ftl | 4 ---- .../src/attributes/deprecation.rs | 7 +++--- .../src/attributes/prototype.rs | 5 ++--- .../src/attributes/stability.rs | 22 ++++++++++--------- compiler/rustc_attr_parsing/src/context.rs | 11 +--------- .../src/session_diagnostics.rs | 20 ----------------- .../src/error_codes/E0541.md | 4 +++- tests/ui/deprecation/deprecation-sanity.rs | 2 +- .../ui/deprecation/deprecation-sanity.stderr | 10 +++++---- .../stability-attribute-sanity-2.rs | 2 +- .../stability-attribute-sanity-2.stderr | 11 ++++++---- .../stability-attribute-sanity.rs | 2 +- .../stability-attribute-sanity.stderr | 11 ++++++---- 13 files changed, 44 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 61f816f0baf8f..d28b2aec4b687 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -213,10 +213,6 @@ attr_parsing_stability_outside_std = stability attributes may not be used outsid attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) -attr_parsing_unknown_meta_item = - unknown meta item '{$item}' - .label = expected one of {$expected} - attr_parsing_unknown_version_literal = unknown version literal format, assuming it refers to a future version diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index ad3e2ced60c7d..2d79e3a103d6e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -110,13 +110,12 @@ impl SingleAttributeParser for DeprecationParser { Some(get(cx, name, param.span(), param.args(), &suggestion)?); } _ => { - cx.unknown_key( + cx.expected_specific_argument( param.span(), - param.path().to_string(), if features.deprecated_suggestion() { - &["since", "note", "suggestion"] + &[sym::since, sym::note, sym::suggestion] } else { - &["since", "note"] + &[sym::since, sym::note] }, ); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs index cd7c84f45fe51..aef983df02049 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -46,9 +46,8 @@ impl SingleAttributeParser for CustomMirParser { extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed); } else if let Some(arg) = meta_item.word_is(sym::phase) { extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed); - } else if let Some(word) = meta_item.path().word() { - let word = word.to_string(); - cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]); + } else if let Some(..) = meta_item.path().word() { + cx.expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]); failed = true; } else { cx.expected_name_value(meta_item.span(), None); diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 571cb884c1fcd..383d727666866 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -315,11 +315,7 @@ pub(crate) fn parse_stability( insert_value_into_option_or_error(cx, ¶m, &mut since, word.unwrap())? } _ => { - cx.emit_err(session_diagnostics::UnknownMetaItem { - span: param_span, - item: param.path().to_string(), - expected: &["feature", "since"], - }); + cx.expected_specific_argument(param_span, &[sym::feature, sym::since]); return None; } } @@ -426,11 +422,17 @@ pub(crate) fn parse_unstability( insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())? } _ => { - cx.emit_err(session_diagnostics::UnknownMetaItem { - span: param.span(), - item: param.path().to_string(), - expected: &["feature", "reason", "issue", "soft", "implied_by", "old_name"], - }); + cx.expected_specific_argument( + param.span(), + &[ + sym::feature, + sym::reason, + sym::issue, + sym::soft, + sym::implied_by, + sym::old_name, + ], + ); return None; } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 2cbc4a65b276e..8712035957d0c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -77,7 +77,7 @@ use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; use crate::parser::{ArgParser, RefPathParser}; use crate::session_diagnostics::{ - AttributeParseError, AttributeParseErrorReason, ParsedDescription, UnknownMetaItem, + AttributeParseError, AttributeParseErrorReason, ParsedDescription, }; use crate::target_checking::AllowedTargets; @@ -425,15 +425,6 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { } impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { - pub(crate) fn unknown_key( - &self, - span: Span, - found: String, - options: &[&'static str], - ) -> ErrorGuaranteed { - self.emit_err(UnknownMetaItem { span, item: found, expected: options }) - } - fn emit_parse_error( &self, span: Span, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index b50a7f92fcdc5..f103faec80af4 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -64,26 +64,6 @@ pub(crate) struct DocAttributeNotAttribute { pub attribute: Symbol, } -/// Error code: E0541 -pub(crate) struct UnknownMetaItem<'a> { - pub span: Span, - pub item: String, - pub expected: &'a [&'a str], -} - -// Manual implementation to be able to format `expected` items correctly. -impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnknownMetaItem<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::>(); - Diag::new(dcx, level, fluent::attr_parsing_unknown_meta_item) - .with_span(self.span) - .with_code(E0541) - .with_arg("item", self.item) - .with_arg("expected", expected.join(", ")) - .with_span_label(self.span, fluent::attr_parsing_label) - } -} - #[derive(Diagnostic)] #[diag(attr_parsing_missing_since, code = E0542)] pub(crate) struct MissingSince { diff --git a/compiler/rustc_error_codes/src/error_codes/E0541.md b/compiler/rustc_error_codes/src/error_codes/E0541.md index 96334088feeef..f1f97b39fa282 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0541.md +++ b/compiler/rustc_error_codes/src/error_codes/E0541.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + An unknown meta item was used. Erroneous code example: -```compile_fail,E0541 +```compile_fail (no longer emitted) #[deprecated( since="1.0.0", // error: unknown meta item diff --git a/tests/ui/deprecation/deprecation-sanity.rs b/tests/ui/deprecation/deprecation-sanity.rs index 45ee91741e5a1..d1061dc1e170b 100644 --- a/tests/ui/deprecation/deprecation-sanity.rs +++ b/tests/ui/deprecation/deprecation-sanity.rs @@ -3,7 +3,7 @@ // Various checks that deprecation attributes are used correctly mod bogus_attribute_types_1 { - #[deprecated(since = "a", note = "a", reason)] //~ ERROR unknown meta item 'reason' + #[deprecated(since = "a", note = "a", reason)] //~ ERROR malformed `deprecated` attribute input [E0539] fn f1() { } #[deprecated(since = "a", note)] //~ ERROR malformed `deprecated` attribute input [E0539] diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr index a96d4a0bdea88..a4dc9f23d3d2f 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr @@ -1,8 +1,10 @@ -error[E0541]: unknown meta item 'reason' - --> $DIR/deprecation-sanity.rs:6:43 +error[E0539]: malformed `deprecated` attribute input + --> $DIR/deprecation-sanity.rs:6:5 | LL | #[deprecated(since = "a", note = "a", reason)] - | ^^^^^^ expected one of `since`, `note` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------^^ + | | + | valid arguments are `since` or `note` error[E0539]: malformed `deprecated` attribute input --> $DIR/deprecation-sanity.rs:9:5 @@ -86,5 +88,5 @@ LL | #[deprecated = "hello"] error: aborting due to 10 previous errors -Some errors have detailed explanations: E0538, E0539, E0541, E0565. +Some errors have detailed explanations: E0538, E0539, E0565. For more information about an error, try `rustc --explain E0538`. diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-2.rs b/tests/ui/stability-attribute/stability-attribute-sanity-2.rs index 92e300d33d6ec..dabff97ad52dd 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity-2.rs +++ b/tests/ui/stability-attribute/stability-attribute-sanity-2.rs @@ -7,7 +7,7 @@ #[stable(feature = "a", feature = "b", since = "1.0.0")] //~ ERROR malformed `stable` attribute input [E0538] fn f1() { } -#[stable(feature = "a", sinse = "1.0.0")] //~ ERROR unknown meta item 'sinse' +#[stable(feature = "a", sinse = "1.0.0")] //~ ERROR malformed `stable` attribute input [E0539] fn f2() { } #[unstable(feature = "a", issue = "no")] diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr b/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr index 5b35a51cad729..7beb9fd979ce7 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr @@ -7,11 +7,14 @@ LL | #[stable(feature = "a", feature = "b", since = "1.0.0")] | | found `feature` used as a key more than once | help: must be of the form: `#[stable(feature = "name", since = "version")]` -error[E0541]: unknown meta item 'sinse' - --> $DIR/stability-attribute-sanity-2.rs:10:25 +error[E0539]: malformed `stable` attribute input + --> $DIR/stability-attribute-sanity-2.rs:10:1 | LL | #[stable(feature = "a", sinse = "1.0.0")] - | ^^^^^^^^^^^^^^^ expected one of `feature`, `since` + | ^^^^^^^^^^^^^^^^^^^^^^^^---------------^^ + | | | + | | valid arguments are `feature` or `since` + | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0545]: `issue` must be a non-zero numeric string or "none" --> $DIR/stability-attribute-sanity-2.rs:13:27 @@ -23,5 +26,5 @@ LL | #[unstable(feature = "a", issue = "no")] error: aborting due to 3 previous errors -Some errors have detailed explanations: E0538, E0541, E0545. +Some errors have detailed explanations: E0538, E0539, E0545. For more information about an error, try `rustc --explain E0538`. diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.rs b/tests/ui/stability-attribute/stability-attribute-sanity.rs index c4c86e12d267e..cee8d5fae1d2e 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.rs +++ b/tests/ui/stability-attribute/stability-attribute-sanity.rs @@ -5,7 +5,7 @@ #![stable(feature = "rust1", since = "1.0.0")] mod bogus_attribute_types_1 { - #[stable(feature = "a", since = "4.4.4", reason)] //~ ERROR unknown meta item 'reason' [E0541] + #[stable(feature = "a", since = "4.4.4", reason)] //~ ERROR malformed `stable` attribute input [E0539] fn f1() { } #[stable(feature = "a", since)] //~ ERROR malformed `stable` attribute input [E0539] diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.stderr b/tests/ui/stability-attribute/stability-attribute-sanity.stderr index ae948237d7edf..05c34484b9f86 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity.stderr @@ -1,8 +1,11 @@ -error[E0541]: unknown meta item 'reason' - --> $DIR/stability-attribute-sanity.rs:8:46 +error[E0539]: malformed `stable` attribute input + --> $DIR/stability-attribute-sanity.rs:8:5 | LL | #[stable(feature = "a", since = "4.4.4", reason)] - | ^^^^^^ expected one of `feature`, `since` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------^^ + | | | + | | valid arguments are `feature` or `since` + | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0539]: malformed `stable` attribute input --> $DIR/stability-attribute-sanity.rs:11:5 @@ -138,5 +141,5 @@ LL | #[stable(feature = "a", since = "1.0.0")] error: aborting due to 20 previous errors -Some errors have detailed explanations: E0539, E0541, E0542, E0543, E0544, E0546, E0547, E0549, E0711. +Some errors have detailed explanations: E0539, E0542, E0543, E0544, E0546, E0547, E0549, E0711. For more information about an error, try `rustc --explain E0539`. From f357e51e89113b9e8a59b580d635a1838aa1e8f1 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sat, 13 Dec 2025 13:31:32 +0100 Subject: [PATCH 21/26] Stop using `IllFormedAttributeInputLint` for `macro_use` --- .../src/attributes/macro_attrs.rs | 11 +----- compiler/rustc_attr_parsing/src/context.rs | 4 ++ .../src/session_diagnostics.rs | 4 ++ tests/ui/attributes/invalid-macro-use.rs | 4 +- tests/ui/attributes/invalid-macro-use.stderr | 38 ++++++++++++------- tests/ui/attributes/malformed-attrs.rs | 2 +- tests/ui/attributes/malformed-attrs.stderr | 14 ++++++- .../issue-43106-gating-of-macro_use.rs | 2 +- .../issue-43106-gating-of-macro_use.stderr | 15 +++++++- 9 files changed, 64 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index e4209c3edd85c..7db87feb38f1d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -3,7 +3,7 @@ use rustc_hir::attrs::MacroUseArgs; use rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS; use super::prelude::*; -use crate::session_diagnostics::IllFormedAttributeInputLint; +use crate::session_diagnostics::{AttributeParseErrorReason, IllFormedAttributeInputLint}; pub(crate) struct MacroEscapeParser; impl NoArgsAttributeParser for MacroEscapeParser { @@ -102,14 +102,7 @@ impl AttributeParser for MacroUseParser { } } ArgParser::NameValue(_) => { - let suggestions = cx.suggestions(); - cx.emit_err(IllFormedAttributeInputLint { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - span, - }); + cx.expected_list_or_no_args(span); } } }, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 8712035957d0c..bd19eaf9ab397 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -468,6 +468,10 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedList) } + pub(crate) fn expected_list_or_no_args(&self, span: Span) -> ErrorGuaranteed { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedListOrNoArgs) + } + pub(crate) fn expected_no_args(&self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs) } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index f103faec80af4..4f10b7cabd4c4 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -519,6 +519,7 @@ pub(crate) enum AttributeParseErrorReason<'a> { ExpectedAtLeastOneArgument, ExpectedSingleArgument, ExpectedList, + ExpectedListOrNoArgs, UnexpectedLiteral, ExpectedNameValue(Option), DuplicateKey(Symbol), @@ -591,6 +592,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { AttributeParseErrorReason::ExpectedList => { diag.span_label(self.span, "expected this to be a list"); } + AttributeParseErrorReason::ExpectedListOrNoArgs => { + diag.span_label(self.span, "expected a list or no arguments here"); + } AttributeParseErrorReason::DuplicateKey(key) => { diag.span_label(self.span, format!("found `{key}` used as a key more than once")); diag.code(E0538); diff --git a/tests/ui/attributes/invalid-macro-use.rs b/tests/ui/attributes/invalid-macro-use.rs index 52e4608303f04..4d05e933647be 100644 --- a/tests/ui/attributes/invalid-macro-use.rs +++ b/tests/ui/attributes/invalid-macro-use.rs @@ -2,7 +2,9 @@ //~^ NOTE the lint level is defined here #[macro_use = 5] -//~^ ERROR valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` +//~^ ERROR malformed `macro_use` attribute input +//~| NOTE expected a list or no arguments here +//~| NOTE for more information, visit extern crate std as s1; #[macro_use(5)] diff --git a/tests/ui/attributes/invalid-macro-use.stderr b/tests/ui/attributes/invalid-macro-use.stderr index ff3ed6196d3d3..ade9c9a21b8ab 100644 --- a/tests/ui/attributes/invalid-macro-use.stderr +++ b/tests/ui/attributes/invalid-macro-use.stderr @@ -1,23 +1,33 @@ error[E0469]: imported macro not found - --> $DIR/invalid-macro-use.rs:51:13 + --> $DIR/invalid-macro-use.rs:53:13 | LL | #[macro_use(a)] | ^ error[E0469]: imported macro not found - --> $DIR/invalid-macro-use.rs:53:13 + --> $DIR/invalid-macro-use.rs:55:13 | LL | #[macro_use(b)] | ^ -error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` +error[E0539]: malformed `macro_use` attribute input --> $DIR/invalid-macro-use.rs:4:1 | LL | #[macro_use = 5] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ expected a list or no arguments here + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[macro_use = 5] +LL + #[macro_use(name1, name2, ...)] + | +LL - #[macro_use = 5] +LL + #[macro_use] + | error[E0539]: malformed `macro_use` attribute input - --> $DIR/invalid-macro-use.rs:8:1 + --> $DIR/invalid-macro-use.rs:10:1 | LL | #[macro_use(5)] | ^^^^^^^^^^^^-^^ @@ -35,7 +45,7 @@ LL + #[macro_use] | error[E0565]: malformed `macro_use` attribute input - --> $DIR/invalid-macro-use.rs:14:1 + --> $DIR/invalid-macro-use.rs:16:1 | LL | #[macro_use(a = "b")] | ^^^^^^^^^^^^^^-----^^ @@ -53,7 +63,7 @@ LL + #[macro_use] | error[E0565]: malformed `macro_use` attribute input - --> $DIR/invalid-macro-use.rs:20:1 + --> $DIR/invalid-macro-use.rs:22:1 | LL | #[macro_use(a(b))] | ^^^^^^^^^^^^^---^^ @@ -71,7 +81,7 @@ LL + #[macro_use] | error[E0539]: malformed `macro_use` attribute input - --> $DIR/invalid-macro-use.rs:26:1 + --> $DIR/invalid-macro-use.rs:28:1 | LL | #[macro_use(a::b)] | ^^^^^^^^^^^^----^^ @@ -89,13 +99,13 @@ LL + #[macro_use] | error: unused attribute - --> $DIR/invalid-macro-use.rs:32:1 + --> $DIR/invalid-macro-use.rs:34:1 | LL | #[macro_use(a)] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/invalid-macro-use.rs:34:1 + --> $DIR/invalid-macro-use.rs:36:1 | LL | #[macro_use] | ^^^^^^^^^^^^ @@ -106,25 +116,25 @@ LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/invalid-macro-use.rs:40:1 + --> $DIR/invalid-macro-use.rs:42:1 | LL | #[macro_use(a)] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/invalid-macro-use.rs:38:1 + --> $DIR/invalid-macro-use.rs:40:1 | LL | #[macro_use] | ^^^^^^^^^^^^ error: unused attribute - --> $DIR/invalid-macro-use.rs:46:1 + --> $DIR/invalid-macro-use.rs:48:1 | LL | #[macro_use] | ^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/invalid-macro-use.rs:44:1 + --> $DIR/invalid-macro-use.rs:46:1 | LL | #[macro_use] | ^^^^^^^^^^^^ diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index 26ee89dd7b3be..3e67242a14b2f 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -214,7 +214,7 @@ static mut TLS: u8 = 42; #[no_link()] //~^ ERROR malformed #[macro_use = 1] -//~^ ERROR valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` +//~^ ERROR malformed extern crate wloop; //~^ ERROR can't find crate for `wloop` [E0463] diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index e1ebe4ac9eab4..53446fb4da105 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -614,11 +614,21 @@ LL | #[non_exhaustive = 1] | | didn't expect any arguments here | help: must be of the form: `#[non_exhaustive]` -error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` +error[E0539]: malformed `macro_use` attribute input --> $DIR/malformed-attrs.rs:216:1 | LL | #[macro_use = 1] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ expected a list or no arguments here + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[macro_use = 1] +LL + #[macro_use(name1, name2, ...)] + | +LL - #[macro_use = 1] +LL + #[macro_use] + | error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` --> $DIR/malformed-attrs.rs:221:1 diff --git a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.rs b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.rs index 67959a3182977..274faa4495ef0 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.rs @@ -13,7 +13,7 @@ mod macro_escape { //~^ ERROR arguments to `macro_use` are not allowed here #[macro_use = "2700"] struct S; - //~^ ERROR valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` + //~^ ERROR malformed //~| WARN cannot be used on //~| WARN previously accepted diff --git a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr index 5be17e96fb15f..e8fcef8a1638f 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr @@ -16,11 +16,21 @@ error: arguments to `macro_use` are not allowed here LL | #![macro_use(my_macro)] | ^^^^^^^^^^^^^^^^^^^^^^^ -error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` +error[E0539]: malformed `macro_use` attribute input --> $DIR/issue-43106-gating-of-macro_use.rs:15:5 | LL | #[macro_use = "2700"] struct S; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ expected a list or no arguments here + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[macro_use = "2700"] struct S; +LL + #[macro_use(name1, name2, ...)] struct S; + | +LL - #[macro_use = "2700"] struct S; +LL + #[macro_use] struct S; + | warning: `#[macro_use]` attribute cannot be used on structs --> $DIR/issue-43106-gating-of-macro_use.rs:15:5 @@ -61,3 +71,4 @@ LL | #[macro_use] impl S { } error: aborting due to 4 previous errors; 4 warnings emitted +For more information about this error, try `rustc --explain E0539`. From 6a7ed1353d915f8ae9ce11d0caf0338c736424be Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sat, 13 Dec 2025 13:33:45 +0100 Subject: [PATCH 22/26] Stop using `IllFormedAttributeInputLint` for `macro_use` --- .../src/attributes/macro_attrs.rs | 12 +----------- tests/ui/attributes/malformed-attrs.rs | 2 +- tests/ui/attributes/malformed-attrs.stderr | 13 +++++++++++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 7db87feb38f1d..8a456778643ba 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,9 +1,7 @@ -use rustc_errors::DiagArgValue; use rustc_hir::attrs::MacroUseArgs; use rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS; use super::prelude::*; -use crate::session_diagnostics::{AttributeParseErrorReason, IllFormedAttributeInputLint}; pub(crate) struct MacroEscapeParser; impl NoArgsAttributeParser for MacroEscapeParser { @@ -158,15 +156,7 @@ impl SingleAttributeParser for MacroExportParser { } } ArgParser::NameValue(_) => { - let span = cx.attr_span; - let suggestions = cx.suggestions(); - cx.emit_err(IllFormedAttributeInputLint { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - span, - }); + cx.expected_list_or_no_args(cx.attr_span); return None; } }; diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index 3e67242a14b2f..8ffad3a9a2e94 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -219,7 +219,7 @@ extern crate wloop; //~^ ERROR can't find crate for `wloop` [E0463] #[macro_export = 18] -//~^ ERROR valid forms for the attribute are +//~^ ERROR malformed #[allow_internal_unsafe = 1] //~^ ERROR malformed //~| ERROR allow_internal_unsafe side-steps the unsafe_code lint diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 53446fb4da105..4c1bd9277bc75 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -630,11 +630,20 @@ LL - #[macro_use = 1] LL + #[macro_use] | -error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` +error[E0539]: malformed `macro_export` attribute input --> $DIR/malformed-attrs.rs:221:1 | LL | #[macro_export = 18] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ expected a list or no arguments here + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[macro_export = 18] +LL + #[macro_export(local_inner_macros)] + | +LL - #[macro_export = 18] +LL + #[macro_export] + | error[E0565]: malformed `allow_internal_unsafe` attribute input --> $DIR/malformed-attrs.rs:223:1 From 69a59e8e87ae6b506f94ee4ef4a37ba1af764d84 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sat, 13 Dec 2025 13:37:12 +0100 Subject: [PATCH 23/26] Stop using `IllFormedAttributeInputLint` for `must_use` --- compiler/rustc_attr_parsing/messages.ftl | 5 ----- .../rustc_attr_parsing/src/attributes/must_use.rs | 12 +----------- .../rustc_attr_parsing/src/session_diagnostics.rs | 9 --------- tests/ui/attributes/malformed-attrs.rs | 2 +- tests/ui/attributes/malformed-attrs.stderr | 12 +++++++++++- tests/ui/attributes/malformed-must_use.rs | 2 +- tests/ui/attributes/malformed-must_use.stderr | 13 ++++++++++++- 7 files changed, 26 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index d28b2aec4b687..fb9016ca4d86e 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -50,11 +50,6 @@ attr_parsing_expects_feature_list = attr_parsing_expects_features = `{$name}` expects feature names -attr_parsing_ill_formed_attribute_input = {$num_suggestions -> - [1] attribute must be of the form {$suggestions} - *[other] valid forms for the attribute are {$suggestions} - } - attr_parsing_import_name_type_raw = import name type can only be used with link kind `raw-dylib` diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index a27e1ecb707e3..2cbf6300ede48 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -1,7 +1,4 @@ -use rustc_errors::DiagArgValue; - use super::prelude::*; -use crate::session_diagnostics::IllFormedAttributeInputLint; pub(crate) struct MustUseParser; @@ -45,14 +42,7 @@ impl SingleAttributeParser for MustUseParser { Some(value_str) } ArgParser::List(_) => { - let suggestions = cx.suggestions(); - cx.emit_err(IllFormedAttributeInputLint { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - span: cx.attr_span, - }); + cx.expected_name_value(cx.attr_span, None); return None; } }, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 4f10b7cabd4c4..b93b2f737f6e3 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -380,15 +380,6 @@ pub(crate) struct UnusedMultiple { pub name: Symbol, } -#[derive(Diagnostic)] -#[diag(attr_parsing_ill_formed_attribute_input)] -pub(crate) struct IllFormedAttributeInputLint { - #[primary_span] - pub span: Span, - pub num_suggestions: usize, - pub suggestions: DiagArgValue, -} - #[derive(Diagnostic)] #[diag(attr_parsing_null_on_export, code = E0648)] pub(crate) struct NullOnExport { diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index 8ffad3a9a2e94..37ccf9faa1a3e 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -59,7 +59,7 @@ #[cold = 1] //~^ ERROR malformed #[must_use()] -//~^ ERROR valid forms for the attribute are +//~^ ERROR malformed #[no_mangle = 1] //~^ ERROR malformed #[unsafe(naked())] diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 4c1bd9277bc75..3e04a7e1a4e39 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -314,11 +314,21 @@ LL | #[cold = 1] | | didn't expect any arguments here | help: must be of the form: `#[cold]` -error: valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]` +error[E0539]: malformed `must_use` attribute input --> $DIR/malformed-attrs.rs:61:1 | LL | #[must_use()] | ^^^^^^^^^^^^^ + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[must_use()] +LL + #[must_use = "reason"] + | +LL - #[must_use()] +LL + #[must_use] + | error[E0565]: malformed `no_mangle` attribute input --> $DIR/malformed-attrs.rs:63:1 diff --git a/tests/ui/attributes/malformed-must_use.rs b/tests/ui/attributes/malformed-must_use.rs index 4b98affa8abd3..79a1c369f8385 100644 --- a/tests/ui/attributes/malformed-must_use.rs +++ b/tests/ui/attributes/malformed-must_use.rs @@ -1,4 +1,4 @@ -#[must_use()] //~ ERROR valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]` +#[must_use()] //~ ERROR malformed struct Test; fn main() {} diff --git a/tests/ui/attributes/malformed-must_use.stderr b/tests/ui/attributes/malformed-must_use.stderr index c948ba677444f..38855f24a0111 100644 --- a/tests/ui/attributes/malformed-must_use.stderr +++ b/tests/ui/attributes/malformed-must_use.stderr @@ -1,8 +1,19 @@ -error: valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]` +error[E0539]: malformed `must_use` attribute input --> $DIR/malformed-must_use.rs:1:1 | LL | #[must_use()] | ^^^^^^^^^^^^^ + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[must_use()] +LL + #[must_use = "reason"] + | +LL - #[must_use()] +LL + #[must_use] + | error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. From ae39d3d9ab3ef431d2e7aeedbd0b084089b0de84 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sat, 13 Dec 2025 15:43:01 +0100 Subject: [PATCH 24/26] Improve spans of malformed attribute errors --- .../rustc_attr_parsing/src/attributes/cfg.rs | 2 +- .../src/attributes/codegen_attrs.rs | 6 +++--- .../src/attributes/confusables.rs | 2 +- .../src/attributes/debugger.rs | 2 +- .../rustc_attr_parsing/src/attributes/doc.rs | 2 +- .../src/attributes/link_attrs.rs | 4 ++-- .../src/attributes/macro_attrs.rs | 8 ++++---- .../src/attributes/must_use.rs | 4 ++-- .../src/attributes/proc_macro_attrs.rs | 6 +++--- .../src/attributes/prototype.rs | 2 +- .../rustc_attr_parsing/src/attributes/repr.rs | 4 ++-- .../src/attributes/stability.rs | 4 ++-- .../rustc_attr_parsing/src/attributes/traits.rs | 2 +- .../rustc_attr_parsing/src/attributes/util.rs | 2 +- compiler/rustc_attr_parsing/src/context.rs | 11 ++++++++++- compiler/rustc_attr_parsing/src/parser.rs | 6 +++++- .../src/session_diagnostics.rs | 4 ++++ tests/rustdoc-ui/invalid-cfg.stderr | 16 ++++++++++++---- tests/ui/attributes/invalid-macro-use.stderr | 4 +++- tests/ui/attributes/malformed-attrs.stderr | 12 +++++++++--- tests/ui/attributes/malformed-fn-align.stderr | 6 +++--- tests/ui/attributes/malformed-must_use.stderr | 4 +++- .../ui/attributes/malformed-static-align.stderr | 6 +++--- .../rustc_skip_during_method_dispatch.stderr | 6 +++--- .../cfg-attr-syntax-validation.stderr | 6 +++--- .../issue-43106-gating-of-macro_use.stderr | 4 +++- .../link-attr-validation-early.stderr | 4 +++- .../link-attr-validation-late.stderr | 6 +++--- tests/ui/malformed/malformed-regressions.stderr | 4 +++- tests/ui/proc-macro/attribute.stderr | 4 +++- tests/ui/repr/repr.stderr | 8 ++++++-- tests/ui/sanitize-attr/invalid-sanitize.stderr | 4 +++- .../stability-attribute-sanity-4.stderr | 12 ++++++------ tests/ui/target-feature/invalid-attribute.stderr | 6 +++--- 34 files changed, 116 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 5b4786a64ef2d..798cc10765415 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -42,7 +42,7 @@ pub fn parse_cfg( args: &ArgParser, ) -> Option { let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; let Some(single) = list.single() else { diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 7d3a7418f06c3..17c748fa3e687 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -25,7 +25,7 @@ impl SingleAttributeParser for OptimizeParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; @@ -478,7 +478,7 @@ fn parse_tf_attribute( ) -> impl IntoIterator { let mut features = Vec::new(); let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return features; }; if list.is_empty() { @@ -601,7 +601,7 @@ impl SingleAttributeParser for SanitizeParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index 97e78dfb136b9..0b7ac989346a4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -13,7 +13,7 @@ impl AttributeParser for ConfusablesParser { template!(List: &[r#""name1", "name2", ..."#]), |this, cx, args| { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs index c88b795aab03d..52a66942cf939 100644 --- a/compiler/rustc_attr_parsing/src/attributes/debugger.rs +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -21,7 +21,7 @@ impl CombineAttributeParser for DebuggerViualizerParser { args: &ArgParser, ) -> impl IntoIterator { let Some(l) = args.list() else { - cx.expected_list(args.span().unwrap_or(cx.attr_span)); + cx.expected_list(cx.attr_span, args); return None; }; let Some(single) = l.single() else { diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index b6fea37c92aa2..16dbb04b48ebd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -106,7 +106,7 @@ impl DocParser { } Some(sym::attr) => { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index fe8f3578fe145..388553c8fd9b6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -76,7 +76,7 @@ impl CombineAttributeParser for LinkParser { return None; } _ => { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; } }; @@ -379,7 +379,7 @@ impl LinkParser { return true; } let Some(link_cfg) = item.args().list() else { - cx.expected_list(item.span()); + cx.expected_list(item.span(), item.args()); return true; }; let Some(link_cfg) = link_cfg.single() else { diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 8a456778643ba..0f1ab02fca251 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -99,8 +99,8 @@ impl AttributeParser for MacroUseParser { } } } - ArgParser::NameValue(_) => { - cx.expected_list_or_no_args(span); + ArgParser::NameValue(nv) => { + cx.expected_list_or_no_args(nv.args_span()); } } }, @@ -155,8 +155,8 @@ impl SingleAttributeParser for MacroExportParser { } } } - ArgParser::NameValue(_) => { - cx.expected_list_or_no_args(cx.attr_span); + ArgParser::NameValue(nv) => { + cx.expected_list_or_no_args(nv.args_span()); return None; } }; diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index 2cbf6300ede48..673e2c902da0b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -41,8 +41,8 @@ impl SingleAttributeParser for MustUseParser { }; Some(value_str) } - ArgParser::List(_) => { - cx.expected_name_value(cx.attr_span, None); + ArgParser::List(list) => { + cx.expected_nv_or_no_args(list.span); return None; } }, diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs index e1762005d4c4a..3674aa7124abb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -65,7 +65,7 @@ fn parse_derive_like( if args.no_args().is_ok() && !trait_name_mandatory { return Some((None, ThinVec::new())); } - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; let mut items = list.mixed(); @@ -96,7 +96,7 @@ fn parse_derive_like( let mut attributes = ThinVec::new(); if let Some(attrs) = items.next() { let Some(attr_list) = attrs.meta_item() else { - cx.expected_list(attrs.span()); + cx.unexpected_literal(attrs.span()); return None; }; if !attr_list.path().word_is(sym::attributes) { @@ -104,7 +104,7 @@ fn parse_derive_like( return None; } let Some(attr_list) = attr_list.args().list() else { - cx.expected_list(attrs.span()); + cx.expected_list(attrs.span(), attr_list.args()); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs index aef983df02049..ac50fe33839d2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -27,7 +27,7 @@ impl SingleAttributeParser for CustomMirParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 4520e4f5dbac1..9ad103f3bb8ee 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -33,7 +33,7 @@ impl CombineAttributeParser for ReprParser { let mut reprs = Vec::new(); let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return reprs; }; @@ -278,7 +278,7 @@ impl AlignParser { fn parse(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) { match args { ArgParser::NoArgs | ArgParser::NameValue(_) => { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); } ArgParser::List(list) => { let Some(align) = list.single() else { diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 383d727666866..6d4f77ef1751b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -295,7 +295,7 @@ pub(crate) fn parse_stability( let mut since = None; let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; @@ -367,7 +367,7 @@ pub(crate) fn parse_unstability( let mut old_name = None; let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index a9b76021a989d..ee5895a6efd0f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -22,7 +22,7 @@ impl SingleAttributeParser for SkipDuringMethodDispatchParser { let mut array = false; let mut boxed_slice = false; let Some(args) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; if args.is_empty() { diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 4e3478abbf4fd..431ba539b2ba2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -43,7 +43,7 @@ pub(crate) fn parse_single_integer( args: &ArgParser, ) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; let Some(single) = list.single() else { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index bd19eaf9ab397..ec9f62bf1eb62 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -464,7 +464,12 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral) } - pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed { + pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed { + let span = match args { + ArgParser::NoArgs => span, + ArgParser::List(list) => list.span, + ArgParser::NameValue(nv) => nv.args_span(), + }; self.emit_parse_error(span, AttributeParseErrorReason::ExpectedList) } @@ -472,6 +477,10 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedListOrNoArgs) } + pub(crate) fn expected_nv_or_no_args(&self, span: Span) -> ErrorGuaranteed { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValueOrNoArgs) + } + pub(crate) fn expected_no_args(&self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs) } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 09ecfaedb5ed2..9551744d5ec53 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -177,7 +177,7 @@ impl ArgParser { match self { Self::NoArgs => Ok(()), Self::List(args) => Err(args.span), - Self::NameValue(args) => Err(args.eq_span.to(args.value_span)), + Self::NameValue(args) => Err(args.args_span()), } } } @@ -314,6 +314,10 @@ impl NameValueParser { pub fn value_as_str(&self) -> Option { self.value_as_lit().kind.str() } + + pub fn args_span(&self) -> Span { + self.eq_span.to(self.value_span) + } } fn expr_to_lit( diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index b93b2f737f6e3..4aea4064b1c4b 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -511,6 +511,7 @@ pub(crate) enum AttributeParseErrorReason<'a> { ExpectedSingleArgument, ExpectedList, ExpectedListOrNoArgs, + ExpectedNameValueOrNoArgs, UnexpectedLiteral, ExpectedNameValue(Option), DuplicateKey(Symbol), @@ -586,6 +587,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { AttributeParseErrorReason::ExpectedListOrNoArgs => { diag.span_label(self.span, "expected a list or no arguments here"); } + AttributeParseErrorReason::ExpectedNameValueOrNoArgs => { + diag.span_label(self.span, "didn't expect a list here"); + } AttributeParseErrorReason::DuplicateKey(key) => { diag.span_label(self.span, format!("found `{key}` used as a key more than once")); diag.code(E0538); diff --git a/tests/rustdoc-ui/invalid-cfg.stderr b/tests/rustdoc-ui/invalid-cfg.stderr index 84f8cea543145..5396110709692 100644 --- a/tests/rustdoc-ui/invalid-cfg.stderr +++ b/tests/rustdoc-ui/invalid-cfg.stderr @@ -2,7 +2,9 @@ error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:2:1 | LL | #[doc(cfg = "x")] - | ^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^-----^^ + | | + | expected this to be a list error[E0805]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:3:1 @@ -16,7 +18,9 @@ error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:7:1 | LL | #[doc(cfg = "x")] - | ^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^-----^^ + | | + | expected this to be a list error[E0805]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:8:1 @@ -30,7 +34,9 @@ error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:12:1 | LL | #[doc(cfg = "x")] - | ^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^-----^^ + | | + | expected this to be a list error[E0805]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:13:1 @@ -44,7 +50,9 @@ error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:18:1 | LL | #[doc(cfg = "x")] - | ^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^-----^^ + | | + | expected this to be a list error[E0805]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:19:1 diff --git a/tests/ui/attributes/invalid-macro-use.stderr b/tests/ui/attributes/invalid-macro-use.stderr index ade9c9a21b8ab..fe235ab209f33 100644 --- a/tests/ui/attributes/invalid-macro-use.stderr +++ b/tests/ui/attributes/invalid-macro-use.stderr @@ -14,7 +14,9 @@ error[E0539]: malformed `macro_use` attribute input --> $DIR/invalid-macro-use.rs:4:1 | LL | #[macro_use = 5] - | ^^^^^^^^^^^^^^^^ expected a list or no arguments here + | ^^^^^^^^^^^^---^ + | | + | expected a list or no arguments here | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 3e04a7e1a4e39..0cd88e2541949 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -318,7 +318,9 @@ error[E0539]: malformed `must_use` attribute input --> $DIR/malformed-attrs.rs:61:1 | LL | #[must_use()] - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^--^ + | | + | didn't expect a list here | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute @@ -628,7 +630,9 @@ error[E0539]: malformed `macro_use` attribute input --> $DIR/malformed-attrs.rs:216:1 | LL | #[macro_use = 1] - | ^^^^^^^^^^^^^^^^ expected a list or no arguments here + | ^^^^^^^^^^^^---^ + | | + | expected a list or no arguments here | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute @@ -644,7 +648,9 @@ error[E0539]: malformed `macro_export` attribute input --> $DIR/malformed-attrs.rs:221:1 | LL | #[macro_export = 18] - | ^^^^^^^^^^^^^^^^^^^^ expected a list or no arguments here + | ^^^^^^^^^^^^^^^----^ + | | + | expected a list or no arguments here | help: try changing it to one of the following valid forms of the attribute | diff --git a/tests/ui/attributes/malformed-fn-align.stderr b/tests/ui/attributes/malformed-fn-align.stderr index b419df8ea2d18..ad01457d063b9 100644 --- a/tests/ui/attributes/malformed-fn-align.stderr +++ b/tests/ui/attributes/malformed-fn-align.stderr @@ -20,9 +20,9 @@ error[E0539]: malformed `rustc_align` attribute input --> $DIR/malformed-fn-align.rs:17:1 | LL | #[rustc_align = 16] - | ^^^^^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^^^^^^----^ + | | | + | | expected this to be a list | help: must be of the form: `#[rustc_align()]` error[E0589]: invalid alignment value: not an unsuffixed integer diff --git a/tests/ui/attributes/malformed-must_use.stderr b/tests/ui/attributes/malformed-must_use.stderr index 38855f24a0111..d4797baa1b0b9 100644 --- a/tests/ui/attributes/malformed-must_use.stderr +++ b/tests/ui/attributes/malformed-must_use.stderr @@ -2,7 +2,9 @@ error[E0539]: malformed `must_use` attribute input --> $DIR/malformed-must_use.rs:1:1 | LL | #[must_use()] - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^--^ + | | + | didn't expect a list here | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute diff --git a/tests/ui/attributes/malformed-static-align.stderr b/tests/ui/attributes/malformed-static-align.stderr index e618ca8acd75b..6f5225f7278d0 100644 --- a/tests/ui/attributes/malformed-static-align.stderr +++ b/tests/ui/attributes/malformed-static-align.stderr @@ -2,9 +2,9 @@ error[E0539]: malformed `rustc_align_static` attribute input --> $DIR/malformed-static-align.rs:4:1 | LL | #[rustc_align_static = 16] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^^^^^^^^^^^^^----^ + | | | + | | expected this to be a list | help: must be of the form: `#[rustc_align_static()]` error[E0589]: invalid alignment value: not an unsuffixed integer diff --git a/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr b/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr index 094987e944fdf..04907f5d638ef 100644 --- a/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr +++ b/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr @@ -11,9 +11,9 @@ error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input --> $DIR/rustc_skip_during_method_dispatch.rs:7:1 | LL | #[rustc_skip_during_method_dispatch = "array"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------^ + | | | + | | expected this to be a list | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]` error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr index e73b20f2d5d31..1be52de708e5b 100644 --- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr @@ -13,9 +13,9 @@ error[E0539]: malformed `cfg` attribute input --> $DIR/cfg-attr-syntax-validation.rs:7:1 | LL | #[cfg = 10] - | ^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^----^ + | | | + | | expected this to be a list | help: must be of the form: `#[cfg(predicate)]` | = note: for more information, visit diff --git a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr index e8fcef8a1638f..1aa0e8fc2830c 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr @@ -20,7 +20,9 @@ error[E0539]: malformed `macro_use` attribute input --> $DIR/issue-43106-gating-of-macro_use.rs:15:5 | LL | #[macro_use = "2700"] struct S; - | ^^^^^^^^^^^^^^^^^^^^^ expected a list or no arguments here + | ^^^^^^^^^^^^--------^ + | | + | expected a list or no arguments here | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute diff --git a/tests/ui/link-native-libs/link-attr-validation-early.stderr b/tests/ui/link-native-libs/link-attr-validation-early.stderr index 101df0371b542..4bf88e150f45e 100644 --- a/tests/ui/link-native-libs/link-attr-validation-early.stderr +++ b/tests/ui/link-native-libs/link-attr-validation-early.stderr @@ -10,7 +10,9 @@ error[E0539]: malformed `link` attribute input --> $DIR/link-attr-validation-early.rs:3:1 | LL | #[link = "foo"] - | ^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^-------^ + | | + | expected this to be a list | = note: for more information, visit diff --git a/tests/ui/link-native-libs/link-attr-validation-late.stderr b/tests/ui/link-native-libs/link-attr-validation-late.stderr index a5f654ca0aeb5..b09431f923aaf 100644 --- a/tests/ui/link-native-libs/link-attr-validation-late.stderr +++ b/tests/ui/link-native-libs/link-attr-validation-late.stderr @@ -142,9 +142,9 @@ error[E0539]: malformed `link` attribute input --> $DIR/link-attr-validation-late.rs:24:1 | LL | #[link(name = "...", cfg = "literal")] - | ^^^^^^^^^^^^^^^^^^^^^---------------^^ - | | - | expected this to be a list + | ^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^ + | | + | expected this to be a list | = note: for more information, visit diff --git a/tests/ui/malformed/malformed-regressions.stderr b/tests/ui/malformed/malformed-regressions.stderr index f46afda1e4772..2bf6ff3a9e7a9 100644 --- a/tests/ui/malformed/malformed-regressions.stderr +++ b/tests/ui/malformed/malformed-regressions.stderr @@ -10,7 +10,9 @@ error[E0539]: malformed `link` attribute input --> $DIR/malformed-regressions.rs:10:1 | LL | #[link = ""] - | ^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^----^ + | | + | expected this to be a list | = note: for more information, visit diff --git a/tests/ui/proc-macro/attribute.stderr b/tests/ui/proc-macro/attribute.stderr index e7127c8ef1d2e..24962cf270a4e 100644 --- a/tests/ui/proc-macro/attribute.stderr +++ b/tests/ui/proc-macro/attribute.stderr @@ -16,7 +16,9 @@ error[E0539]: malformed `proc_macro_derive` attribute input --> $DIR/attribute.rs:15:1 | LL | #[proc_macro_derive = ""] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^^^^^^^^^^^----^ + | | + | expected this to be a list | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute diff --git a/tests/ui/repr/repr.stderr b/tests/ui/repr/repr.stderr index e8168f8f9a582..a842590c9639e 100644 --- a/tests/ui/repr/repr.stderr +++ b/tests/ui/repr/repr.stderr @@ -10,7 +10,9 @@ error[E0539]: malformed `repr` attribute input --> $DIR/repr.rs:4:1 | LL | #[repr = "B"] - | ^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^-----^ + | | + | expected this to be a list | = note: for more information, visit @@ -18,7 +20,9 @@ error[E0539]: malformed `repr` attribute input --> $DIR/repr.rs:7:1 | LL | #[repr = "C"] - | ^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^-----^ + | | + | expected this to be a list | = note: for more information, visit diff --git a/tests/ui/sanitize-attr/invalid-sanitize.stderr b/tests/ui/sanitize-attr/invalid-sanitize.stderr index 2a3497678bdca..26ef31603d887 100644 --- a/tests/ui/sanitize-attr/invalid-sanitize.stderr +++ b/tests/ui/sanitize-attr/invalid-sanitize.stderr @@ -42,7 +42,9 @@ error[E0539]: malformed `sanitize` attribute input --> $DIR/invalid-sanitize.rs:18:1 | LL | #[sanitize = "off"] - | ^^^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^^-------^ + | | + | expected this to be a list error[E0539]: malformed `sanitize` attribute input --> $DIR/invalid-sanitize.rs:21:1 diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-4.stderr b/tests/ui/stability-attribute/stability-attribute-sanity-4.stderr index f656aeaa16c7f..9b3f540198ce4 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity-4.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity-4.stderr @@ -11,9 +11,9 @@ error[E0539]: malformed `unstable` attribute input --> $DIR/stability-attribute-sanity-4.rs:11:5 | LL | #[unstable = "b"] - | ^^^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^^^-----^ + | | | + | | expected this to be a list | help: must be of the form: `#[unstable(feature = "name", reason = "...", issue = "N")]` error[E0539]: malformed `stable` attribute input @@ -29,9 +29,9 @@ error[E0539]: malformed `stable` attribute input --> $DIR/stability-attribute-sanity-4.rs:17:5 | LL | #[stable = "a"] - | ^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^-----^ + | | | + | | expected this to be a list | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0542]: missing 'since' diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index eaa26aa3ecafe..05a836b01af5d 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -26,9 +26,9 @@ error[E0539]: malformed `target_feature` attribute input --> $DIR/invalid-attribute.rs:17:1 | LL | #[target_feature = "+sse2"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^^^^^^^^^---------^ + | | | + | | expected this to be a list | help: must be of the form: `#[target_feature(enable = "feat1, feat2")]` error[E0539]: malformed `target_feature` attribute input From cab911449aa0921005f48c859dcf4e78888643f9 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Tue, 4 Nov 2025 17:54:46 +0000 Subject: [PATCH 25/26] dangling pointer from temp cleanup --- compiler/rustc_lint/messages.ftl | 20 +- compiler/rustc_lint/src/lints.rs | 9 +- .../lint/dangling-pointers-from-locals.stderr | 19 ++ .../allow.rs | 4 +- .../allow.stderr | 28 +-- .../calls.rs | 10 +- .../calls.stderr | 70 +++--- .../cstring-as-ptr.rs | 4 +- .../cstring-as-ptr.stderr | 28 +-- .../example-from-issue123613.rs | 4 +- .../example-from-issue123613.stderr | 28 +-- .../dangling-pointers-from-temporaries/ext.rs | 4 +- .../ext.stderr | 28 +-- .../methods.rs | 4 +- .../methods.stderr | 28 +-- .../temporaries.rs | 16 +- .../temporaries.stderr | 112 ++++----- .../types.rs | 34 +-- .../types.stderr | 238 +++++++++--------- 19 files changed, 355 insertions(+), 333 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 8aa90c070acd3..ad9e5b0fbd94e 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -197,15 +197,17 @@ lint_dangling_pointers_from_locals = {$fn_kind} returns a dangling pointer to dr .ret_ty = return type is `{$ret_ty}` .local_var = local variable `{$local_var_name}` is dropped at the end of the {$fn_kind} .created_at = dangling pointer created here - .note = a dangling pointer is safe, but dereferencing one is undefined behavior - -lint_dangling_pointers_from_temporaries = a dangling pointer will be produced because the temporary `{$ty}` will be dropped - .label_ptr = this pointer will immediately be invalid - .label_temporary = this `{$ty}` is deallocated at the end of the statement, bind it to a variable to extend its lifetime - .note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - .help_bind = you must make sure that the variable you bind the `{$ty}` to lives at least as long as the pointer returned by the call to `{$callee}` - .help_returned = in particular, if this pointer is returned from the current function, binding the `{$ty}` inside the function will not suffice - .help_visit = for more information, see + .note_safe = a dangling pointer is safe, but dereferencing one is undefined behavior + .note_more_info = for more information, see + +lint_dangling_pointers_from_temporaries = this creates a dangling pointer because temporary `{$ty}` is dropped at end of statement + .label_ptr = pointer created here + .label_temporary = this `{$ty}` is dropped at end of statement + .help_bind = bind the `{$ty}` to a variable such that it outlives the pointer returned by `{$callee}` + .note_safe = a dangling pointer is safe, but dereferencing one is undefined behavior + .note_return = returning a pointer to a local variable will always result in a dangling pointer + .note_more_info = for more information, see + lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index c55f2b9dd6f24..54eea5b41a16c 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1106,10 +1106,10 @@ pub(crate) struct IgnoredUnlessCrateSpecified<'a> { // dangling.rs #[derive(LintDiagnostic)] #[diag(lint_dangling_pointers_from_temporaries)] -#[note] #[help(lint_help_bind)] -#[help(lint_help_returned)] -#[help(lint_help_visit)] +#[note(lint_note_safe)] +#[note(lint_note_return)] +#[note(lint_note_more_info)] // FIXME: put #[primary_span] on `ptr_span` once it does not cause conflicts pub(crate) struct DanglingPointersFromTemporaries<'tcx> { pub callee: Ident, @@ -1122,7 +1122,8 @@ pub(crate) struct DanglingPointersFromTemporaries<'tcx> { #[derive(LintDiagnostic)] #[diag(lint_dangling_pointers_from_locals)] -#[note] +#[note(lint_note_safe)] +#[note(lint_note_more_info)] pub(crate) struct DanglingPointersFromLocals<'tcx> { pub ret_ty: Ty<'tcx>, #[label(lint_ret_ty)] diff --git a/tests/ui/lint/dangling-pointers-from-locals.stderr b/tests/ui/lint/dangling-pointers-from-locals.stderr index 45acc74ac34e5..05ced1cdc0042 100644 --- a/tests/ui/lint/dangling-pointers-from-locals.stderr +++ b/tests/ui/lint/dangling-pointers-from-locals.stderr @@ -9,6 +9,7 @@ LL | &x | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see = note: `#[warn(dangling_pointers_from_locals)]` on by default warning: function returns a dangling pointer to dropped local variable `x` @@ -24,6 +25,7 @@ LL | x | ^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:24:12 @@ -38,6 +40,7 @@ LL | return y; | ^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:30:5 @@ -52,6 +55,7 @@ LL | &x as *const u8 | dangling pointer created here | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:37:5 @@ -66,6 +70,7 @@ LL | x as *const u8 | ^^^^^^^^^^^^^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:43:12 @@ -80,6 +85,7 @@ LL | return &mut x as *mut u8 as *const u8 as *mut u8; | dangling pointer created here | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:49:5 @@ -92,6 +98,7 @@ LL | &{ x } | ^^^^^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:57:13 @@ -108,6 +115,7 @@ LL | | } | |_____________^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:67:12 @@ -120,6 +128,7 @@ LL | return &x; | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:73:12 @@ -132,6 +141,7 @@ LL | return &mut x; | ^^^^^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:80:16 @@ -145,6 +155,7 @@ LL | return &x; | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:88:5 @@ -157,6 +168,7 @@ LL | &x | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:94:12 @@ -169,6 +181,7 @@ LL | return &x; | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: closure returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:101:16 @@ -181,6 +194,7 @@ LL | return &x; | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:113:5 @@ -194,6 +208,7 @@ LL | &x | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `a` --> $DIR/dangling-pointers-from-locals.rs:118:5 @@ -206,6 +221,7 @@ LL | &a | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `a` --> $DIR/dangling-pointers-from-locals.rs:123:5 @@ -218,6 +234,7 @@ LL | &a | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `a` --> $DIR/dangling-pointers-from-locals.rs:128:5 @@ -230,6 +247,7 @@ LL | &a | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `a` --> $DIR/dangling-pointers-from-locals.rs:133:5 @@ -242,6 +260,7 @@ LL | &a | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: 19 warnings emitted diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs b/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs index d892ebdf6069a..7ecee90479d2b 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs @@ -7,7 +7,7 @@ fn main() { #[deny(dangling_pointers_from_temporaries)] { dbg!(String::new().as_ptr()); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer } S.foo() } @@ -18,6 +18,6 @@ impl S { #[warn(dangling_pointers_from_temporaries)] fn foo(self) { dbg!(String::new().as_ptr()); - //~^ WARNING a dangling pointer will be produced because the temporary `String` will be dropped + //~^ WARNING dangling pointer } } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr index e1c12cfd1a501..a235af144e484 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr @@ -1,33 +1,33 @@ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/allow.rs:9:28 | LL | dbg!(String::new().as_ptr()); - | ------------- ^^^^^^ this pointer will immediately be invalid + | ------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/allow.rs:7:12 | LL | #[deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: a dangling pointer will be produced because the temporary `String` will be dropped +warning: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/allow.rs:20:28 | LL | dbg!(String::new().as_ptr()); - | ------------- ^^^^^^ this pointer will immediately be invalid + | ------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/allow.rs:18:12 | diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs b/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs index b376582a88677..80787390176fc 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs @@ -25,12 +25,12 @@ fn ok() { fn not_ok() { { let ptr = cstring().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer consume(ptr); } consume({ let ptr = cstring().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer ptr }); consume({ @@ -39,11 +39,11 @@ fn not_ok() { //^ FIXME: should error }); let _ptr: *const u8 = cstring().as_ptr().cast(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer let _ptr: *const u8 = { cstring() }.as_ptr().cast(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer let _ptr: *const u8 = { cstring().as_ptr() }.cast(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer } fn main() { diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr index 41c6cdd0e3ef4..4e302dcc99422 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr @@ -1,72 +1,72 @@ -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/calls.rs:27:29 | LL | let ptr = cstring().as_ptr(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/calls.rs:1:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/calls.rs:32:29 | LL | let ptr = cstring().as_ptr(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/calls.rs:41:37 | LL | let _ptr: *const u8 = cstring().as_ptr().cast(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/calls.rs:43:41 | LL | let _ptr: *const u8 = { cstring() }.as_ptr().cast(); - | ------------- ^^^^^^ this pointer will immediately be invalid + | ------------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/calls.rs:45:39 | LL | let _ptr: *const u8 = { cstring().as_ptr() }.cast(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 5 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs index a98378794abc4..7036019362cdd 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs @@ -7,12 +7,12 @@ use std::ffi::CString; macro_rules! mymacro { () => { let s = CString::new("some text").unwrap().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer } } fn main() { let s = CString::new("some text").unwrap().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer mymacro!(); } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr index d4126ba231f76..be9f8b19545b5 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr @@ -6,39 +6,39 @@ LL | #![deny(temporary_cstring_as_ptr)] | = note: `#[warn(renamed_and_removed_lints)]` on by default -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/cstring-as-ptr.rs:15:48 | LL | let s = CString::new("some text").unwrap().as_ptr(); - | ---------------------------------- ^^^^^^ this pointer will immediately be invalid + | ---------------------------------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/cstring-as-ptr.rs:2:9 | LL | #![deny(temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/cstring-as-ptr.rs:9:52 | LL | let s = CString::new("some text").unwrap().as_ptr(); - | ---------------------------------- ^^^^^^ this pointer will immediately be invalid + | ---------------------------------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement ... LL | mymacro!(); | ---------- in this macro invocation | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see = note: this error originates in the macro `mymacro` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs index 0fb07a3f3bc97..771ddb3493449 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs @@ -3,9 +3,9 @@ const MAX_PATH: usize = 260; fn main() { let str1 = String::with_capacity(MAX_PATH).as_mut_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer let str2 = String::from("TotototototototototototototototototoT").as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer unsafe { std::ptr::copy_nonoverlapping(str2, str1, 30); println!("{:?}", String::from_raw_parts(str1, 30, 30)); diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr index aace55e92cf16..b8baa2bf5601a 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr @@ -1,33 +1,33 @@ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/example-from-issue123613.rs:5:48 | LL | let str1 = String::with_capacity(MAX_PATH).as_mut_ptr(); - | ------------------------------- ^^^^^^^^^^ this pointer will immediately be invalid + | ------------------------------- ^^^^^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_mut_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_mut_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_mut_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/example-from-issue123613.rs:1:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/example-from-issue123613.rs:7:70 | LL | let str2 = String::from("TotototototototototototototototototoT").as_ptr(); - | ----------------------------------------------------- ^^^^^^ this pointer will immediately be invalid + | ----------------------------------------------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 2 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs b/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs index a5e84d36090fc..e56bc0f433e4c 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs @@ -26,7 +26,7 @@ impl Ext2 for *const u32 { fn main() { let _ptr1 = Vec::::new().as_ptr().dbg(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer let _ptr2 = vec![0].as_ptr().foo(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr index 976334ddef9c8..f780f88134f27 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr @@ -1,33 +1,33 @@ -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/ext.rs:28:35 | LL | let _ptr1 = Vec::::new().as_ptr().dbg(); - | ----------------- ^^^^^^ this pointer will immediately be invalid + | ----------------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/ext.rs:1:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/ext.rs:30:25 | LL | let _ptr2 = vec![0].as_ptr().foo(); - | ------- ^^^^^^ this pointer will immediately be invalid + | ------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 2 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs b/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs index 26019b376d3a4..10666e0a82823 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs @@ -2,7 +2,7 @@ fn main() { vec![0u8].as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer vec![0u8].as_mut_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr index a86a69bc39a28..367dfae9e65d4 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr @@ -1,33 +1,33 @@ -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/methods.rs:4:15 | LL | vec![0u8].as_ptr(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/methods.rs:1:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/methods.rs:6:15 | LL | vec![0u8].as_mut_ptr(); - | --------- ^^^^^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_mut_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_mut_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_mut_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 2 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs index 1f216586ae81d..1f816996d5149 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs @@ -19,18 +19,18 @@ fn main() { // Call string().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer // MethodCall "hello".to_string().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer // Tup // impossible // Binary (string() + "hello").as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer // Path { @@ -66,7 +66,7 @@ fn main() { // If { (if true { String::new() } else { "hello".into() }).as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer } // Loop @@ -75,7 +75,7 @@ fn main() { break String::new(); }) .as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer } // Match @@ -84,7 +84,7 @@ fn main() { s => s, } .as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer } // Closure @@ -92,7 +92,7 @@ fn main() { // Block { string() }.as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer // Assign, AssignOp // impossible @@ -132,5 +132,5 @@ fn main() { // Macro vec![0u8].as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr index e8994703cabfa..be001acad7472 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr @@ -1,115 +1,115 @@ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:21:14 | LL | string().as_ptr(); - | -------- ^^^^^^ this pointer will immediately be invalid + | -------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/temporaries.rs:2:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:25:25 | LL | "hello".to_string().as_ptr(); - | ------------------- ^^^^^^ this pointer will immediately be invalid + | ------------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:32:26 | LL | (string() + "hello").as_ptr(); - | -------------------- ^^^^^^ this pointer will immediately be invalid + | -------------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:68:61 | LL | (if true { String::new() } else { "hello".into() }).as_ptr(); - | --------------------------------------------------- ^^^^^^ this pointer will immediately be invalid + | --------------------------------------------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:77:10 | LL | / (loop { LL | | break String::new(); LL | | }) - | |__________- this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | |__________- this `String` is dropped at end of statement LL | .as_ptr(); - | ^^^^^^ this pointer will immediately be invalid + | ^^^^^^ pointer created here | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:86:10 | LL | / match string() { LL | | s => s, LL | | } - | |_________- this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | |_________- this `String` is dropped at end of statement LL | .as_ptr(); - | ^^^^^^ this pointer will immediately be invalid + | ^^^^^^ pointer created here | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:94:18 | LL | { string() }.as_ptr(); - | ------------ ^^^^^^ this pointer will immediately be invalid + | ------------ ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/temporaries.rs:134:15 | LL | vec![0u8].as_ptr(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 8 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/types.rs b/tests/ui/lint/dangling-pointers-from-temporaries/types.rs index 17c3eca89e273..072ec25f4d830 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/types.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/types.rs @@ -19,39 +19,39 @@ fn declval() -> T { fn main() { declval::().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer because temporary `CString` declval::().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer because temporary `String` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer because temporary `Vec` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + //~^ ERROR dangling pointer because temporary `Box` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box<[u8]>` will be dropped + //~^ ERROR dangling pointer because temporary `Box<[u8]>` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + //~^ ERROR dangling pointer because temporary `Box` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + //~^ ERROR dangling pointer because temporary `Box` declval::<[u8; 10]>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `[u8; 10]` will be dropped + //~^ ERROR dangling pointer because temporary `[u8; 10]` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box<[u8; 10]>` will be dropped + //~^ ERROR dangling pointer because temporary `Box<[u8; 10]>` declval::>>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box>` will be dropped + //~^ ERROR dangling pointer because temporary `Box>` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + //~^ ERROR dangling pointer because temporary `Box` declval::>>>>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box>>>` will be dropped + //~^ ERROR dangling pointer because temporary `Box>>>` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Cell` will be dropped + //~^ ERROR dangling pointer because temporary `Cell` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `MaybeUninit` will be dropped + //~^ ERROR dangling pointer because temporary `MaybeUninit` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer because temporary `Vec` declval::>().get(); - //~^ ERROR a dangling pointer will be produced because the temporary `UnsafeCell` will be dropped + //~^ ERROR dangling pointer because temporary `UnsafeCell` declval::>().get(); - //~^ ERROR a dangling pointer will be produced because the temporary `SyncUnsafeCell` will be dropped + //~^ ERROR dangling pointer because temporary `SyncUnsafeCell` declval::>().as_ptr(); declval::().as_ptr(); } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr index fab2459b53f6f..bb37c8ad211bd 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr @@ -1,228 +1,228 @@ -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/types.rs:21:26 | LL | declval::().as_ptr(); - | -------------------- ^^^^^^ this pointer will immediately be invalid + | -------------------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/types.rs:1:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/types.rs:23:25 | LL | declval::().as_ptr(); - | ------------------- ^^^^^^ this pointer will immediately be invalid + | ------------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/types.rs:25:26 | LL | declval::>().as_ptr(); - | -------------------- ^^^^^^ this pointer will immediately be invalid + | -------------------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box` will be dropped +error: this creates a dangling pointer because temporary `Box` is dropped at end of statement --> $DIR/types.rs:27:31 | LL | declval::>().as_ptr(); - | ------------------------- ^^^^^^ this pointer will immediately be invalid + | ------------------------- ^^^^^^ pointer created here | | - | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice - = help: for more information, see + = help: bind the `Box` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box<[u8]>` will be dropped +error: this creates a dangling pointer because temporary `Box<[u8]>` is dropped at end of statement --> $DIR/types.rs:29:28 | LL | declval::>().as_ptr(); - | ---------------------- ^^^^^^ this pointer will immediately be invalid + | ---------------------- ^^^^^^ pointer created here | | - | this `Box<[u8]>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box<[u8]>` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box<[u8]>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box<[u8]>` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box<[u8]>` inside the function will not suffice - = help: for more information, see + = help: bind the `Box<[u8]>` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box` will be dropped +error: this creates a dangling pointer because temporary `Box` is dropped at end of statement --> $DIR/types.rs:31:27 | LL | declval::>().as_ptr(); - | --------------------- ^^^^^^ this pointer will immediately be invalid + | --------------------- ^^^^^^ pointer created here | | - | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice - = help: for more information, see + = help: bind the `Box` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box` will be dropped +error: this creates a dangling pointer because temporary `Box` is dropped at end of statement --> $DIR/types.rs:33:28 | LL | declval::>().as_ptr(); - | ---------------------- ^^^^^^ this pointer will immediately be invalid + | ---------------------- ^^^^^^ pointer created here | | - | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice - = help: for more information, see + = help: bind the `Box` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `[u8; 10]` will be dropped +error: this creates a dangling pointer because temporary `[u8; 10]` is dropped at end of statement --> $DIR/types.rs:35:27 | LL | declval::<[u8; 10]>().as_ptr(); - | --------------------- ^^^^^^ this pointer will immediately be invalid + | --------------------- ^^^^^^ pointer created here | | - | this `[u8; 10]` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `[u8; 10]` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `[u8; 10]` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `[u8; 10]` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `[u8; 10]` inside the function will not suffice - = help: for more information, see + = help: bind the `[u8; 10]` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box<[u8; 10]>` will be dropped +error: this creates a dangling pointer because temporary `Box<[u8; 10]>` is dropped at end of statement --> $DIR/types.rs:37:32 | LL | declval::>().as_ptr(); - | -------------------------- ^^^^^^ this pointer will immediately be invalid + | -------------------------- ^^^^^^ pointer created here | | - | this `Box<[u8; 10]>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box<[u8; 10]>` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box<[u8; 10]>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box<[u8; 10]>` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box<[u8; 10]>` inside the function will not suffice - = help: for more information, see + = help: bind the `Box<[u8; 10]>` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box>` will be dropped +error: this creates a dangling pointer because temporary `Box>` is dropped at end of statement --> $DIR/types.rs:39:31 | LL | declval::>>().as_ptr(); - | ------------------------- ^^^^^^ this pointer will immediately be invalid + | ------------------------- ^^^^^^ pointer created here | | - | this `Box>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box>` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box>` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box>` inside the function will not suffice - = help: for more information, see + = help: bind the `Box>` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box` will be dropped +error: this creates a dangling pointer because temporary `Box` is dropped at end of statement --> $DIR/types.rs:41:30 | LL | declval::>().as_ptr(); - | ------------------------ ^^^^^^ this pointer will immediately be invalid + | ------------------------ ^^^^^^ pointer created here | | - | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice - = help: for more information, see + = help: bind the `Box` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box>>>` will be dropped +error: this creates a dangling pointer because temporary `Box>>>` is dropped at end of statement --> $DIR/types.rs:43:43 | LL | declval::>>>>().as_ptr(); - | ------------------------------------- ^^^^^^ this pointer will immediately be invalid + | ------------------------------------- ^^^^^^ pointer created here | | - | this `Box>>>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box>>>` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box>>>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box>>>` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box>>>` inside the function will not suffice - = help: for more information, see + = help: bind the `Box>>>` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Cell` will be dropped +error: this creates a dangling pointer because temporary `Cell` is dropped at end of statement --> $DIR/types.rs:45:27 | LL | declval::>().as_ptr(); - | --------------------- ^^^^^^ this pointer will immediately be invalid + | --------------------- ^^^^^^ pointer created here | | - | this `Cell` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Cell` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Cell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Cell` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Cell` inside the function will not suffice - = help: for more information, see + = help: bind the `Cell` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `MaybeUninit` will be dropped +error: this creates a dangling pointer because temporary `MaybeUninit` is dropped at end of statement --> $DIR/types.rs:47:34 | LL | declval::>().as_ptr(); - | ---------------------------- ^^^^^^ this pointer will immediately be invalid + | ---------------------------- ^^^^^^ pointer created here | | - | this `MaybeUninit` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `MaybeUninit` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `MaybeUninit` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `MaybeUninit` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `MaybeUninit` inside the function will not suffice - = help: for more information, see + = help: bind the `MaybeUninit` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/types.rs:49:33 | LL | declval::>().as_ptr(); - | --------------------------- ^^^^^^ this pointer will immediately be invalid + | --------------------------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `UnsafeCell` will be dropped +error: this creates a dangling pointer because temporary `UnsafeCell` is dropped at end of statement --> $DIR/types.rs:51:33 | LL | declval::>().get(); - | --------------------------- ^^^ this pointer will immediately be invalid + | --------------------------- ^^^ pointer created here | | - | this `UnsafeCell` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `UnsafeCell` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `get` the `UnsafeCell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `UnsafeCell` to lives at least as long as the pointer returned by the call to `get` - = help: in particular, if this pointer is returned from the current function, binding the `UnsafeCell` inside the function will not suffice - = help: for more information, see + = help: bind the `UnsafeCell` to a variable such that it outlives the pointer returned by `get` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `SyncUnsafeCell` will be dropped +error: this creates a dangling pointer because temporary `SyncUnsafeCell` is dropped at end of statement --> $DIR/types.rs:53:37 | LL | declval::>().get(); - | ------------------------------- ^^^ this pointer will immediately be invalid + | ------------------------------- ^^^ pointer created here | | - | this `SyncUnsafeCell` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `SyncUnsafeCell` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `get` the `SyncUnsafeCell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `SyncUnsafeCell` to lives at least as long as the pointer returned by the call to `get` - = help: in particular, if this pointer is returned from the current function, binding the `SyncUnsafeCell` inside the function will not suffice - = help: for more information, see + = help: bind the `SyncUnsafeCell` to a variable such that it outlives the pointer returned by `get` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 17 previous errors From 309e9ec576099d472fc07b94f1839b31a487cbe5 Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 13 Dec 2025 20:23:20 -0500 Subject: [PATCH 26/26] don't use no_main and no_core to test IBT also assert that the property is IBT and not some random other property. --- tests/run-make/branch-protection-check-IBT/lib.rs | 1 + tests/run-make/branch-protection-check-IBT/main.rs | 5 ----- .../run-make/branch-protection-check-IBT/rmake.rs | 14 ++++++++++---- 3 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 tests/run-make/branch-protection-check-IBT/lib.rs delete mode 100644 tests/run-make/branch-protection-check-IBT/main.rs diff --git a/tests/run-make/branch-protection-check-IBT/lib.rs b/tests/run-make/branch-protection-check-IBT/lib.rs new file mode 100644 index 0000000000000..0c9ac1ac8e4bd --- /dev/null +++ b/tests/run-make/branch-protection-check-IBT/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/tests/run-make/branch-protection-check-IBT/main.rs b/tests/run-make/branch-protection-check-IBT/main.rs deleted file mode 100644 index 445b8795134c5..0000000000000 --- a/tests/run-make/branch-protection-check-IBT/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![feature(no_core)] -#![allow(internal_features)] -#![no_core] -#![no_std] -#![no_main] diff --git a/tests/run-make/branch-protection-check-IBT/rmake.rs b/tests/run-make/branch-protection-check-IBT/rmake.rs index 73109df12ae9d..34f0693562c0b 100644 --- a/tests/run-make/branch-protection-check-IBT/rmake.rs +++ b/tests/run-make/branch-protection-check-IBT/rmake.rs @@ -41,13 +41,19 @@ use run_make_support::{bare_rustc, llvm_readobj}; fn main() { - // `main.rs` is `#![no_std]` to not pull in the currently not-compiled-with-IBT precompiled std. + // `lib.rs` is `#![no_std]` to not pull in the currently not-compiled-with-IBT precompiled std. bare_rustc() - .input("main.rs") + .input("lib.rs") + .crate_type("lib") + .emit("obj=lib.o") .target("x86_64-unknown-linux-gnu") .arg("-Zcf-protection=branch") - .arg("-Clink-args=-nostartfiles") .run(); - llvm_readobj().arg("-nW").input("main").run().assert_stdout_contains(".note.gnu.property"); + llvm_readobj() + .arg("-nW") + .input("lib.o") + .run() + .assert_stdout_contains(".note.gnu.property") + .assert_stdout_contains("feature: IBT"); }