From dee1c260b8ab3d093dc571a0954f645a43b822bc Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Thu, 7 Dec 2023 17:36:00 +0800 Subject: [PATCH 01/23] xous: ffi: fix lend_impl() return values The `ret1` and `ret2` return values from lend operations are returned in $a1 and $a2. This function incorrectly pulled them from $a6 and $a7, causing them to always be `0`. Signed-off-by: Sean Cross --- library/std/src/os/xous/ffi.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index 8be7fbb102f..0a7f9a10d4d 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -88,29 +88,31 @@ fn lend_impl( let a3 = opcode; let a4 = data.as_ptr() as usize; let a5 = data.len(); - let mut a6 = arg1; - let mut a7 = arg2; + let a6 = arg1; + let a7 = arg2; + let mut ret1; + let mut ret2; unsafe { core::arch::asm!( "ecall", inlateout("a0") a0, - inlateout("a1") a1 => _, - inlateout("a2") a2 => _, + inlateout("a1") a1 => ret1, + inlateout("a2") a2 => ret2, inlateout("a3") a3 => _, inlateout("a4") a4 => _, inlateout("a5") a5 => _, - inlateout("a6") a6, - inlateout("a7") a7, + inlateout("a6") a6 => _, + inlateout("a7") a7 => _, ) }; let result = a0; if result == SyscallResult::MemoryReturned as usize { - Ok((a6, a7)) + Ok((ret1, ret2)) } else if result == SyscallResult::Error as usize { - Err(a1.into()) + Err(ret1.into()) } else { Err(Error::InternalError) } From 944dc212685a4f17db144d14459ee4bc3ffd3423 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Sun, 24 Dec 2023 14:51:28 +0800 Subject: [PATCH 02/23] xous: ffi: correct size of freed memory The amount of memory allocated was multiplied by sizeof::(), so the amount of memory to be freed should also be multiplied by sizeof::(). Signed-off-by: Sean Cross --- library/std/src/os/xous/ffi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index 0a7f9a10d4d..1df4fbab7f7 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -407,7 +407,7 @@ pub(crate) unsafe fn map_memory( pub(crate) unsafe fn unmap_memory(range: *mut [T]) -> Result<(), Error> { let mut a0 = Syscall::UnmapMemory as usize; let mut a1 = range.as_mut_ptr() as usize; - let a2 = range.len(); + let a2 = range.len() * core::mem::size_of::(); let a3 = 0; let a4 = 0; let a5 = 0; From 118e8f78402a584becebf0c96082537fbaa8b9e6 Mon Sep 17 00:00:00 2001 From: bunnie Date: Fri, 22 Dec 2023 09:27:30 +0800 Subject: [PATCH 03/23] xous: std: thread_parking: fix deadlocks Fix a deadlock condition that can occur when a thread is awoken in between the point at which it checks its wake state and the point where it actually waits. This change will cause the waker to continuously send Notify messages until it actually wakes up the target thread. Signed-off-by: Sean Cross --- .../std/src/sys/pal/xous/thread_parking.rs | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/library/std/src/sys/pal/xous/thread_parking.rs b/library/std/src/sys/pal/xous/thread_parking.rs index aa39c6d2718..0bd0462d77d 100644 --- a/library/std/src/sys/pal/xous/thread_parking.rs +++ b/library/std/src/sys/pal/xous/thread_parking.rs @@ -29,31 +29,40 @@ impl Parker { // Change NOTIFIED to EMPTY and EMPTY to PARKED. let state = self.state.fetch_sub(1, Acquire); if state == NOTIFIED { + // The state has gone from NOTIFIED (1) to EMPTY (0) return; } + // The state has gone from EMPTY (0) to PARKED (-1) + assert!(state == EMPTY); - // The state was set to PARKED. Wait until the `unpark` wakes us up. + // The state is now PARKED (-1). Wait until the `unpark` wakes us up. blocking_scalar( ticktimer_server(), TicktimerScalar::WaitForCondition(self.index(), 0).into(), ) .expect("failed to send WaitForCondition command"); - self.state.swap(EMPTY, Acquire); + let state = self.state.swap(EMPTY, Acquire); + assert!(state == NOTIFIED || state == PARKED); } pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) { // Change NOTIFIED to EMPTY and EMPTY to PARKED. let state = self.state.fetch_sub(1, Acquire); if state == NOTIFIED { + // The state has gone from NOTIFIED (1) to EMPTY (0) return; } + // The state has gone from EMPTY (0) to PARKED (-1) + assert!(state == EMPTY); // A value of zero indicates an indefinite wait. Clamp the number of // milliseconds to the allowed range. let millis = usize::max(timeout.as_millis().try_into().unwrap_or(usize::MAX), 1); - let was_timeout = blocking_scalar( + // The state is now PARKED (-1). Wait until the `unpark` wakes us up, + // or things time out. + let _was_timeout = blocking_scalar( ticktimer_server(), TicktimerScalar::WaitForCondition(self.index(), millis).into(), ) @@ -61,28 +70,37 @@ impl Parker { != 0; let state = self.state.swap(EMPTY, Acquire); - if was_timeout && state == NOTIFIED { - // The state was set to NOTIFIED after we returned from the wait - // but before we reset the state. Therefore, a wakeup is on its - // way, which we need to consume here. - // NOTICE: this is a priority hole. - blocking_scalar( - ticktimer_server(), - TicktimerScalar::WaitForCondition(self.index(), 0).into(), - ) - .expect("failed to send WaitForCondition command"); - } + assert!(state == PARKED || state == NOTIFIED); } pub fn unpark(self: Pin<&Self>) { - let state = self.state.swap(NOTIFIED, Release); - if state == PARKED { - // The thread is parked, wake it up. - blocking_scalar( - ticktimer_server(), - TicktimerScalar::NotifyCondition(self.index(), 1).into(), - ) - .expect("failed to send NotifyCondition command"); + // If the state is already `NOTIFIED`, then another thread has + // indicated it wants to wake up the target thread. + // + // If the state is `EMPTY` then there is nothing to wake up, and + // the target thread will immediately exit from `park()` the + // next time that function is called. + if self.state.swap(NOTIFIED, Release) != PARKED { + return; + } + + // The thread is parked, wake it up. Keep trying until we wake something up. + // This will happen when the `NotifyCondition` call returns the fact that + // 1 condition was notified. + // Alternately, keep going until the state is seen as `EMPTY`, indicating + // the thread woke up and kept going. This can happen when the Park + // times out before we can send the NotifyCondition message. + while blocking_scalar( + ticktimer_server(), + TicktimerScalar::NotifyCondition(self.index(), 1).into(), + ) + .expect("failed to send NotifyCondition command")[0] + == 0 + && self.state.load(Acquire) != EMPTY + { + // The target thread hasn't yet hit the `WaitForCondition` call. + // Yield to let the target thread run some more. + crate::thread::yield_now(); } } } From 626926f073997af4c9bd971c827eece6cfa26bb2 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Sun, 24 Dec 2023 15:05:11 +0800 Subject: [PATCH 04/23] std: xous: rework condvar to fix soundness issues Rework the Condvar implementation on Xous to ensure notifications are not missed. This involves keeping track of how many times a Condvar timed out and synchronizing based on that. Signed-off-by: Sean Cross --- library/std/src/sys/pal/xous/locks/condvar.rs | 173 +++++++++++------- 1 file changed, 105 insertions(+), 68 deletions(-) diff --git a/library/std/src/sys/pal/xous/locks/condvar.rs b/library/std/src/sys/pal/xous/locks/condvar.rs index 1bb38dfa341..510235046e1 100644 --- a/library/std/src/sys/pal/xous/locks/condvar.rs +++ b/library/std/src/sys/pal/xous/locks/condvar.rs @@ -1,14 +1,17 @@ use super::mutex::Mutex; use crate::os::xous::ffi::{blocking_scalar, scalar}; -use crate::os::xous::services::ticktimer_server; -use crate::sync::Mutex as StdMutex; +use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; use crate::time::Duration; +use core::sync::atomic::{AtomicUsize, Ordering}; // The implementation is inspired by Andrew D. Birrell's paper // "Implementing Condition Variables with Semaphores" +const NOTIFY_TRIES: usize = 3; + pub struct Condvar { - counter: StdMutex, + counter: AtomicUsize, + timed_out: AtomicUsize, } unsafe impl Send for Condvar {} @@ -18,94 +21,128 @@ impl Condvar { #[inline] #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] pub const fn new() -> Condvar { - Condvar { counter: StdMutex::new(0) } + Condvar { counter: AtomicUsize::new(0), timed_out: AtomicUsize::new(0) } + } + + fn notify_some(&self, to_notify: usize) { + // Assumption: The Mutex protecting this condvar is locked throughout the + // entirety of this call, preventing calls to `wait` and `wait_timeout`. + + // Logic check: Ensure that there aren't any missing waiters. Remove any that + // timed-out, ensuring the counter doesn't underflow. + assert!(self.timed_out.load(Ordering::Relaxed) <= self.counter.load(Ordering::Relaxed)); + self.counter.fetch_sub(self.timed_out.swap(0, Ordering::Relaxed), Ordering::Relaxed); + + // Figure out how many threads to notify. Note that it is impossible for `counter` + // to increase during this operation because Mutex is locked. However, it is + // possible for `counter` to decrease due to a condvar timing out, in which + // case the corresponding `timed_out` will increase accordingly. + let Ok(waiter_count) = + self.counter.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |counter| { + if counter == 0 { + return None; + } else { + Some(counter - counter.min(to_notify)) + } + }) + else { + // No threads are waiting on this condvar + return; + }; + + let mut remaining_to_wake = waiter_count.min(to_notify); + if remaining_to_wake == 0 { + return; + } + for _wake_tries in 0..NOTIFY_TRIES { + let result = blocking_scalar( + ticktimer_server(), + TicktimerScalar::NotifyCondition(self.index(), remaining_to_wake).into(), + ) + .expect("failure to send NotifyCondition command"); + + // Remove the list of waiters that were notified + remaining_to_wake -= result[0]; + + // Also remove the number of waiters that timed out. Clamp it to 0 in order to + // ensure we don't wait forever in case the waiter woke up between the time + // we counted the remaining waiters and now. + remaining_to_wake = + remaining_to_wake.saturating_sub(self.timed_out.swap(0, Ordering::Relaxed)); + if remaining_to_wake == 0 { + return; + } + crate::thread::yield_now(); + } } pub fn notify_one(&self) { - let mut counter = self.counter.lock().unwrap(); - if *counter <= 0 { - return; - } else { - *counter -= 1; - } - let result = blocking_scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), 1).into(), - ); - drop(counter); - result.expect("failure to send NotifyCondition command"); + self.notify_some(1) } pub fn notify_all(&self) { - let mut counter = self.counter.lock().unwrap(); - if *counter <= 0 { - return; - } - let result = blocking_scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), *counter) - .into(), - ); - *counter = 0; - drop(counter); - - result.expect("failure to send NotifyCondition command"); + self.notify_some(self.counter.load(Ordering::Relaxed)) } fn index(&self) -> usize { - self as *const Condvar as usize + core::ptr::from_ref(self).addr() + } + + /// Unlock the given Mutex and wait for the notification. Wait at most + /// `ms` milliseconds, or pass `0` to wait forever. + /// + /// Returns `true` if the condition was received, `false` if it timed out + fn wait_ms(&self, mutex: &Mutex, ms: usize) -> bool { + self.counter.fetch_add(1, Ordering::Relaxed); + unsafe { mutex.unlock() }; + + // Threading concern: There is a chance that the `notify` thread wakes up here before + // we have a chance to wait for the condition. This is fine because we've recorded + // the fact that we're waiting by incrementing the counter. + let result = blocking_scalar( + ticktimer_server(), + TicktimerScalar::WaitForCondition(self.index(), ms).into(), + ); + let awoken = result.expect("Ticktimer: failure to send WaitForCondition command")[0] == 0; + + // If we awoke due to a timeout, increment the `timed_out` counter so that the + // main loop of `notify` knows there's a timeout. + // + // This is done with the Mutex still unlocked, because the Mutex might still + // be locked by the `notify` process above. + if !awoken { + self.timed_out.fetch_add(1, Ordering::Relaxed); + } + + unsafe { mutex.lock() }; + awoken } pub unsafe fn wait(&self, mutex: &Mutex) { - let mut counter = self.counter.lock().unwrap(); - *counter += 1; - unsafe { mutex.unlock() }; - drop(counter); - - let result = blocking_scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), 0).into(), - ); - unsafe { mutex.lock() }; - - result.expect("Ticktimer: failure to send WaitForCondition command"); + // Wait for 0 ms, which is a special case to "wait forever" + self.wait_ms(mutex, 0); } pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let mut counter = self.counter.lock().unwrap(); - *counter += 1; - unsafe { mutex.unlock() }; - drop(counter); - let mut millis = dur.as_millis() as usize; + // Ensure we don't wait for 0 ms, which would cause us to wait forever if millis == 0 { millis = 1; } - - let result = blocking_scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), millis) - .into(), - ); - unsafe { mutex.lock() }; - - let result = result.expect("Ticktimer: failure to send WaitForCondition command")[0] == 0; - - // If we awoke due to a timeout, decrement the wake count, as that would not have - // been done in the `notify()` call. - if !result { - *self.counter.lock().unwrap() -= 1; - } - result + self.wait_ms(mutex, millis) } } impl Drop for Condvar { fn drop(&mut self) { - scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::FreeCondition(self.index()).into(), - ) - .ok(); + let remaining_count = self.counter.load(Ordering::Relaxed); + let timed_out = self.timed_out.load(Ordering::Relaxed); + assert!( + remaining_count - timed_out == 0, + "counter was {} and timed_out was {} not 0", + remaining_count, + timed_out + ); + scalar(ticktimer_server(), TicktimerScalar::FreeCondition(self.index()).into()).ok(); } } From f732d2bfe293349bd3a7b7d8a2211a8058d3fb31 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 29 Dec 2023 19:06:46 +0800 Subject: [PATCH 05/23] std: xous: pass entire memory range to flag updater When updating memory flags via `update_memory_flags()`, ensure we multiply the slice length by the element size to get the full memory size. Signed-off-by: Sean Cross --- library/std/src/os/xous/ffi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index 1df4fbab7f7..7fe84db515c 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -452,7 +452,7 @@ pub(crate) unsafe fn update_memory_flags( ) -> Result<(), Error> { let mut a0 = Syscall::UpdateMemoryFlags as usize; let mut a1 = range.as_mut_ptr() as usize; - let a2 = range.len(); + let a2 = range.len() * core::mem::size_of::(); let a3 = new_flags.bits(); let a4 = 0; // Process ID is currently None let a5 = 0; From b5c1c47990b7a32dbc0fd93f4c807b565814f1c2 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 29 Dec 2023 18:07:53 +0800 Subject: [PATCH 06/23] std: xous: use blocking_scalars for mutex unlock Use blocking scalars when unlocking a mutex. This ensures that mutexes are unlocked immediately rather than dangling. Signed-off-by: Sean Cross --- library/std/src/sys/pal/xous/locks/mutex.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/library/std/src/sys/pal/xous/locks/mutex.rs b/library/std/src/sys/pal/xous/locks/mutex.rs index ea51776d54e..a8c9518ff0b 100644 --- a/library/std/src/sys/pal/xous/locks/mutex.rs +++ b/library/std/src/sys/pal/xous/locks/mutex.rs @@ -1,5 +1,5 @@ -use crate::os::xous::ffi::{blocking_scalar, do_yield, scalar}; -use crate::os::xous::services::ticktimer_server; +use crate::os::xous::ffi::{blocking_scalar, do_yield}; +use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed, Ordering::SeqCst}; pub struct Mutex { @@ -29,7 +29,7 @@ impl Mutex { } fn index(&self) -> usize { - self as *const Mutex as usize + core::ptr::from_ref(self).addr() } #[inline] @@ -83,11 +83,8 @@ impl Mutex { } // Unblock one thread that is waiting on this message. - scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::UnlockMutex(self.index()).into(), - ) - .expect("failure to send UnlockMutex command"); + blocking_scalar(ticktimer_server(), TicktimerScalar::UnlockMutex(self.index()).into()) + .expect("failure to send UnlockMutex command"); } #[inline] @@ -106,11 +103,8 @@ impl Drop for Mutex { // If there was Mutex contention, then we involved the ticktimer. Free // the resources associated with this Mutex as it is deallocated. if self.contended.load(Relaxed) { - scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::FreeMutex(self.index()).into(), - ) - .ok(); + blocking_scalar(ticktimer_server(), TicktimerScalar::FreeMutex(self.index()).into()) + .ok(); } } } From eabd445053ddf9217ab9b464b5ec239fcaf416d3 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Sun, 24 Dec 2023 15:07:05 +0800 Subject: [PATCH 07/23] std: xous: rewrite rwlock to be more robust Add more checks to RwLock on Xous. As part of this, ensure the variable is in a good state when unlocking. Additionally, use the global `yield_now()` rather than platform-specific `do_yield()`. Signed-off-by: Sean Cross --- library/std/src/sys/pal/xous/locks/rwlock.rs | 44 ++++++++++---------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/library/std/src/sys/pal/xous/locks/rwlock.rs b/library/std/src/sys/pal/xous/locks/rwlock.rs index 618da758adf..ab45b33e1f6 100644 --- a/library/std/src/sys/pal/xous/locks/rwlock.rs +++ b/library/std/src/sys/pal/xous/locks/rwlock.rs @@ -1,5 +1,5 @@ -use crate::os::xous::ffi::do_yield; -use crate::sync::atomic::{AtomicIsize, Ordering::SeqCst}; +use crate::sync::atomic::{AtomicIsize, Ordering::Acquire}; +use crate::thread::yield_now; pub struct RwLock { /// The "mode" value indicates how many threads are waiting on this @@ -14,6 +14,9 @@ pub struct RwLock { mode: AtomicIsize, } +const RWLOCK_WRITING: isize = -1; +const RWLOCK_FREE: isize = 0; + unsafe impl Send for RwLock {} unsafe impl Sync for RwLock {} @@ -21,52 +24,51 @@ impl RwLock { #[inline] #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] pub const fn new() -> RwLock { - RwLock { mode: AtomicIsize::new(0) } + RwLock { mode: AtomicIsize::new(RWLOCK_FREE) } } #[inline] pub unsafe fn read(&self) { while !unsafe { self.try_read() } { - do_yield(); + yield_now(); } } #[inline] pub unsafe fn try_read(&self) -> bool { - // Non-atomically determine the current value. - let current = self.mode.load(SeqCst); - - // If it's currently locked for writing, then we cannot read. - if current < 0 { - return false; - } - - // Attempt to lock. If the `current` value has changed, then this - // operation will fail and we will not obtain the lock even if we - // could potentially keep it. - let new = current + 1; - self.mode.compare_exchange(current, new, SeqCst, SeqCst).is_ok() + self.mode + .fetch_update( + Acquire, + Acquire, + |v| if v == RWLOCK_WRITING { None } else { Some(v + 1) }, + ) + .is_ok() } #[inline] pub unsafe fn write(&self) { while !unsafe { self.try_write() } { - do_yield(); + yield_now(); } } #[inline] pub unsafe fn try_write(&self) -> bool { - self.mode.compare_exchange(0, -1, SeqCst, SeqCst).is_ok() + self.mode.compare_exchange(RWLOCK_FREE, RWLOCK_WRITING, Acquire, Acquire).is_ok() } #[inline] pub unsafe fn read_unlock(&self) { - self.mode.fetch_sub(1, SeqCst); + let previous = self.mode.fetch_sub(1, Acquire); + assert!(previous != RWLOCK_FREE); + assert!(previous != RWLOCK_WRITING); } #[inline] pub unsafe fn write_unlock(&self) { - assert_eq!(self.mode.compare_exchange(-1, 0, SeqCst, SeqCst), Ok(-1)); + assert_eq!( + self.mode.compare_exchange(RWLOCK_WRITING, RWLOCK_FREE, Acquire, Acquire), + Ok(RWLOCK_WRITING) + ); } } From 762e58a218906e2ba403bcc9916b4f4378a6c1de Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Sun, 24 Dec 2023 15:09:52 +0800 Subject: [PATCH 08/23] std: once: use queue implementation on Xous Use the global queue implementation of Once when running on Xous. This gets us a thread-safe implementation, rather than using the non-threadsafe `unsupported` implementation. Signed-off-by: Sean Cross --- library/std/src/sys/pal/xous/mod.rs | 2 -- library/std/src/sys_common/once/mod.rs | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index c2550dcfd83..aa96925d859 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -14,8 +14,6 @@ pub mod io; pub mod locks; #[path = "../unsupported/net.rs"] pub mod net; -#[path = "../unsupported/once.rs"] -pub mod once; pub mod os; #[path = "../unix/os_str.rs"] pub mod os_str; diff --git a/library/std/src/sys_common/once/mod.rs b/library/std/src/sys_common/once/mod.rs index 359697d8313..ec57568c54c 100644 --- a/library/std/src/sys_common/once/mod.rs +++ b/library/std/src/sys_common/once/mod.rs @@ -25,6 +25,7 @@ cfg_if::cfg_if! { target_family = "unix", all(target_vendor = "fortanix", target_env = "sgx"), target_os = "solid_asp3", + target_os = "xous", ))] { mod queue; pub use queue::{Once, OnceState}; From 007bf7a05a834ace889589e3e73ab6bc91f6f491 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Sun, 24 Dec 2023 15:11:20 +0800 Subject: [PATCH 09/23] std: xous: fix thread_local_key under tests When running tests, libstd gets implemented as a second library. Due to this fact, the `create()` and `destroy()` functions come from different libraries. To work around this, stash the `destroy_tls()` pointer in the first unused slot in the thread local storage pool. That way even if the destruction comes from a different version of libstd, the correct `DTORS` list will be consulted. Signed-off-by: Sean Cross --- .../std/src/sys/pal/xous/thread_local_key.rs | 101 +++++++++++------- 1 file changed, 63 insertions(+), 38 deletions(-) diff --git a/library/std/src/sys/pal/xous/thread_local_key.rs b/library/std/src/sys/pal/xous/thread_local_key.rs index 3771ea65700..59a668c3df6 100644 --- a/library/std/src/sys/pal/xous/thread_local_key.rs +++ b/library/std/src/sys/pal/xous/thread_local_key.rs @@ -23,10 +23,25 @@ pub type Dtor = unsafe extern "C" fn(*mut u8); const TLS_MEMORY_SIZE: usize = 4096; -/// TLS keys start at `1` to mimic POSIX. +/// TLS keys start at `1`. Index `0` is unused +#[cfg(not(test))] +#[export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE"] static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1); -fn tls_ptr_addr() -> *mut usize { +#[cfg(not(test))] +#[export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE"] +static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +#[cfg(test)] +extern "Rust" { + #[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE"] + static TLS_KEY_INDEX: AtomicUsize; + + #[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE"] + static DTORS: AtomicPtr; +} + +fn tls_ptr_addr() -> *mut *mut u8 { let mut tp: usize; unsafe { asm!( @@ -34,50 +49,50 @@ fn tls_ptr_addr() -> *mut usize { out(reg) tp, ); } - core::ptr::from_exposed_addr_mut::(tp) + core::ptr::from_exposed_addr_mut::<*mut u8>(tp) } /// Create an area of memory that's unique per thread. This area will /// contain all thread local pointers. -fn tls_ptr() -> *mut usize { - let mut tp = tls_ptr_addr(); +fn tls_table() -> &'static mut [*mut u8] { + let tp = tls_ptr_addr(); + if !tp.is_null() { + return unsafe { + core::slice::from_raw_parts_mut(tp, TLS_MEMORY_SIZE / core::mem::size_of::<*mut u8>()) + }; + } // If the TP register is `0`, then this thread hasn't initialized // its TLS yet. Allocate a new page to store this memory. - if tp.is_null() { - tp = unsafe { - map_memory( - None, - None, - TLS_MEMORY_SIZE / core::mem::size_of::(), - MemoryFlags::R | MemoryFlags::W, - ) - } + let tp = unsafe { + map_memory( + None, + None, + TLS_MEMORY_SIZE / core::mem::size_of::<*mut u8>(), + MemoryFlags::R | MemoryFlags::W, + ) .expect("Unable to allocate memory for thread local storage") - .as_mut_ptr(); + }; - unsafe { - // Key #0 is currently unused. - (tp).write_volatile(0); + for val in tp.iter() { + assert!(*val as usize == 0); + } - // Set the thread's `$tp` register - asm!( - "mv tp, {}", - in(reg) tp as usize, - ); - } + unsafe { + // Set the thread's `$tp` register + asm!( + "mv tp, {}", + in(reg) tp.as_mut_ptr() as usize, + ); } tp } -/// Allocate a new TLS key. These keys are shared among all threads. -fn tls_alloc() -> usize { - TLS_KEY_INDEX.fetch_add(1, SeqCst) -} - #[inline] pub unsafe fn create(dtor: Option) -> Key { - let key = tls_alloc(); + // Allocate a new TLS key. These keys are shared among all threads. + #[allow(unused_unsafe)] + let key = unsafe { TLS_KEY_INDEX.fetch_add(1, SeqCst) }; if let Some(f) = dtor { unsafe { register_dtor(key, f) }; } @@ -87,18 +102,20 @@ pub unsafe fn create(dtor: Option) -> Key { #[inline] pub unsafe fn set(key: Key, value: *mut u8) { assert!((key < 1022) && (key >= 1)); - unsafe { tls_ptr().add(key).write_volatile(value as usize) }; + let table = tls_table(); + table[key] = value; } #[inline] pub unsafe fn get(key: Key) -> *mut u8 { assert!((key < 1022) && (key >= 1)); - core::ptr::from_exposed_addr_mut::(unsafe { tls_ptr().add(key).read_volatile() }) + tls_table()[key] } #[inline] pub unsafe fn destroy(_key: Key) { - panic!("can't destroy keys on Xous"); + // Just leak the key. Probably not great on long-running systems that create + // lots of TLS variables, but in practice that's not an issue. } // ------------------------------------------------------------------------- @@ -127,8 +144,6 @@ pub unsafe fn destroy(_key: Key) { // key but also a slot for the destructor queue on windows. An optimization for // another day! -static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - struct Node { dtor: Dtor, key: Key, @@ -138,10 +153,12 @@ struct Node { unsafe fn register_dtor(key: Key, dtor: Dtor) { let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() })); - let mut head = DTORS.load(SeqCst); + #[allow(unused_unsafe)] + let mut head = unsafe { DTORS.load(SeqCst) }; loop { node.next = head; - match DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) { + #[allow(unused_unsafe)] + match unsafe { DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) } { Ok(_) => return, // nothing to drop, we successfully added the node to the list Err(cur) => head = cur, } @@ -155,6 +172,7 @@ pub unsafe fn destroy_tls() { if tp.is_null() { return; } + unsafe { run_dtors() }; // Finally, free the TLS array @@ -169,12 +187,19 @@ pub unsafe fn destroy_tls() { unsafe fn run_dtors() { let mut any_run = true; + + // Run the destructor "some" number of times. This is 5x on Windows, + // so we copy it here. This allows TLS variables to create new + // TLS variables upon destruction that will also get destroyed. + // Keep going until we run out of tries or until we have nothing + // left to destroy. for _ in 0..5 { if !any_run { break; } any_run = false; - let mut cur = DTORS.load(SeqCst); + #[allow(unused_unsafe)] + let mut cur = unsafe { DTORS.load(SeqCst) }; while !cur.is_null() { let ptr = unsafe { get((*cur).key) }; From ef4f722835ccf11dd002606e3ad4adb342ea4435 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 29 Dec 2023 17:54:26 +0800 Subject: [PATCH 10/23] std: xous: share allocator symbol in tests When using the testing framework, a second copy of libstd is built and linked. Use a global symbol for the `DLMALLOC` variable and mark it as `extern` when building as a test. This ensures we only have a single allocator even when running tests. Signed-off-by: Sean Cross --- library/std/src/sys/pal/xous/alloc.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/std/src/sys/pal/xous/alloc.rs b/library/std/src/sys/pal/xous/alloc.rs index b3a3e691e0d..0d540e95520 100644 --- a/library/std/src/sys/pal/xous/alloc.rs +++ b/library/std/src/sys/pal/xous/alloc.rs @@ -1,7 +1,15 @@ use crate::alloc::{GlobalAlloc, Layout, System}; +#[cfg(not(test))] +#[export_name = "_ZN16__rust_internals3std3sys4xous5alloc8DLMALLOCE"] static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new(); +#[cfg(test)] +extern "Rust" { + #[link_name = "_ZN16__rust_internals3std3sys4xous5alloc8DLMALLOCE"] + static mut DLMALLOC: dlmalloc::Dlmalloc; +} + #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { #[inline] From aa8acc2215f625db898c0f9feab3a1702f38a190 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Tue, 7 Nov 2023 12:32:04 +0800 Subject: [PATCH 11/23] xous: net: initial commit of network support This is an initial commit of network support for Xous. On hardware, is backed by smoltcp running via a Xous server in a separate process space. This patch adds TCP and UDP client and server support as well as DNS resolution support using the dns Xous server. Signed-off-by: Sean Cross --- library/std/src/os/xous/services.rs | 6 + library/std/src/os/xous/services/dns.rs | 28 ++ library/std/src/os/xous/services/net.rs | 95 ++++ library/std/src/sys/pal/xous/mod.rs | 1 - library/std/src/sys/pal/xous/net/dns.rs | 127 +++++ library/std/src/sys/pal/xous/net/mod.rs | 84 ++++ .../std/src/sys/pal/xous/net/tcplistener.rs | 247 +++++++++ library/std/src/sys/pal/xous/net/tcpstream.rs | 435 ++++++++++++++++ library/std/src/sys/pal/xous/net/udp.rs | 471 ++++++++++++++++++ 9 files changed, 1493 insertions(+), 1 deletion(-) create mode 100644 library/std/src/os/xous/services/dns.rs create mode 100644 library/std/src/os/xous/services/net.rs create mode 100644 library/std/src/sys/pal/xous/net/dns.rs create mode 100644 library/std/src/sys/pal/xous/net/mod.rs create mode 100644 library/std/src/sys/pal/xous/net/tcplistener.rs create mode 100644 library/std/src/sys/pal/xous/net/tcpstream.rs create mode 100644 library/std/src/sys/pal/xous/net/udp.rs diff --git a/library/std/src/os/xous/services.rs b/library/std/src/os/xous/services.rs index 5c219f1fbb9..a75be1b8570 100644 --- a/library/std/src/os/xous/services.rs +++ b/library/std/src/os/xous/services.rs @@ -1,9 +1,15 @@ use crate::os::xous::ffi::Connection; use core::sync::atomic::{AtomicU32, Ordering}; +mod dns; +pub(crate) use dns::*; + mod log; pub(crate) use log::*; +mod net; +pub(crate) use net::*; + mod systime; pub(crate) use systime::*; diff --git a/library/std/src/os/xous/services/dns.rs b/library/std/src/os/xous/services/dns.rs new file mode 100644 index 00000000000..a7d88f4892c --- /dev/null +++ b/library/std/src/os/xous/services/dns.rs @@ -0,0 +1,28 @@ +use crate::os::xous::ffi::Connection; +use crate::os::xous::services::connect; +use core::sync::atomic::{AtomicU32, Ordering}; + +#[repr(usize)] +pub(crate) enum DnsLendMut { + RawLookup = 6, +} + +impl Into for DnsLendMut { + fn into(self) -> usize { + self as usize + } +} + +/// Return a `Connection` to the DNS lookup server. This server is used for +/// querying domain name values. +pub(crate) fn dns_server() -> Connection { + static DNS_CONNECTION: AtomicU32 = AtomicU32::new(0); + let cid = DNS_CONNECTION.load(Ordering::Relaxed); + if cid != 0 { + return cid.into(); + } + + let cid = connect("_DNS Resolver Middleware_").unwrap(); + DNS_CONNECTION.store(cid.into(), Ordering::Relaxed); + cid +} diff --git a/library/std/src/os/xous/services/net.rs b/library/std/src/os/xous/services/net.rs new file mode 100644 index 00000000000..26d337dcef1 --- /dev/null +++ b/library/std/src/os/xous/services/net.rs @@ -0,0 +1,95 @@ +use crate::os::xous::ffi::Connection; +use crate::os::xous::services::connect; +use core::sync::atomic::{AtomicU32, Ordering}; + +pub(crate) enum NetBlockingScalar { + StdGetTtlUdp(u16 /* fd */), /* 36 */ + StdSetTtlUdp(u16 /* fd */, u32 /* ttl */), /* 37 */ + StdGetTtlTcp(u16 /* fd */), /* 36 */ + StdSetTtlTcp(u16 /* fd */, u32 /* ttl */), /* 37 */ + StdGetNodelay(u16 /* fd */), /* 38 */ + StdSetNodelay(u16 /* fd */, bool), /* 39 */ + StdTcpClose(u16 /* fd */), /* 34 */ + StdUdpClose(u16 /* fd */), /* 41 */ + StdTcpStreamShutdown(u16 /* fd */, crate::net::Shutdown /* how */), /* 46 */ +} + +pub(crate) enum NetLendMut { + StdTcpConnect, /* 30 */ + StdTcpTx(u16 /* fd */), /* 31 */ + StdTcpPeek(u16 /* fd */, bool /* nonblocking */), /* 32 */ + StdTcpRx(u16 /* fd */, bool /* nonblocking */), /* 33 */ + StdGetAddress(u16 /* fd */), /* 35 */ + StdUdpBind, /* 40 */ + StdUdpRx(u16 /* fd */), /* 42 */ + StdUdpTx(u16 /* fd */), /* 43 */ + StdTcpListen, /* 44 */ + StdTcpAccept(u16 /* fd */), /* 45 */ +} + +impl Into for NetLendMut { + fn into(self) -> usize { + match self { + NetLendMut::StdTcpConnect => 30, + NetLendMut::StdTcpTx(fd) => 31 | ((fd as usize) << 16), + NetLendMut::StdTcpPeek(fd, blocking) => { + 32 | ((fd as usize) << 16) | if blocking { 0x8000 } else { 0 } + } + NetLendMut::StdTcpRx(fd, blocking) => { + 33 | ((fd as usize) << 16) | if blocking { 0x8000 } else { 0 } + } + NetLendMut::StdGetAddress(fd) => 35 | ((fd as usize) << 16), + NetLendMut::StdUdpBind => 40, + NetLendMut::StdUdpRx(fd) => 42 | ((fd as usize) << 16), + NetLendMut::StdUdpTx(fd) => 43 | ((fd as usize) << 16), + NetLendMut::StdTcpListen => 44, + NetLendMut::StdTcpAccept(fd) => 45 | ((fd as usize) << 16), + } + } +} + +impl<'a> Into<[usize; 5]> for NetBlockingScalar { + fn into(self) -> [usize; 5] { + match self { + NetBlockingScalar::StdGetTtlTcp(fd) => [36 | ((fd as usize) << 16), 0, 0, 0, 0], + NetBlockingScalar::StdGetTtlUdp(fd) => [36 | ((fd as usize) << 16), 0, 0, 0, 1], + NetBlockingScalar::StdSetTtlTcp(fd, ttl) => { + [37 | ((fd as usize) << 16), ttl as _, 0, 0, 0] + } + NetBlockingScalar::StdSetTtlUdp(fd, ttl) => { + [37 | ((fd as usize) << 16), ttl as _, 0, 0, 1] + } + NetBlockingScalar::StdGetNodelay(fd) => [38 | ((fd as usize) << 16), 0, 0, 0, 0], + NetBlockingScalar::StdSetNodelay(fd, enabled) => { + [39 | ((fd as usize) << 16), if enabled { 1 } else { 0 }, 0, 0, 1] + } + NetBlockingScalar::StdTcpClose(fd) => [34 | ((fd as usize) << 16), 0, 0, 0, 0], + NetBlockingScalar::StdUdpClose(fd) => [41 | ((fd as usize) << 16), 0, 0, 0, 0], + NetBlockingScalar::StdTcpStreamShutdown(fd, how) => [ + 46 | ((fd as usize) << 16), + match how { + crate::net::Shutdown::Read => 1, + crate::net::Shutdown::Write => 2, + crate::net::Shutdown::Both => 3, + }, + 0, + 0, + 0, + ], + } + } +} + +/// Return a `Connection` to the Network server. This server provides all +/// OS-level networking functions. +pub(crate) fn net_server() -> Connection { + static NET_CONNECTION: AtomicU32 = AtomicU32::new(0); + let cid = NET_CONNECTION.load(Ordering::Relaxed); + if cid != 0 { + return cid.into(); + } + + let cid = connect("_Middleware Network Server_").unwrap(); + NET_CONNECTION.store(cid.into(), Ordering::Relaxed); + cid +} diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index aa96925d859..7c49fb88a4e 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -12,7 +12,6 @@ pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; pub mod locks; -#[path = "../unsupported/net.rs"] pub mod net; pub mod os; #[path = "../unix/os_str.rs"] diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/pal/xous/net/dns.rs new file mode 100644 index 00000000000..63056324bfb --- /dev/null +++ b/library/std/src/sys/pal/xous/net/dns.rs @@ -0,0 +1,127 @@ +use crate::io; +use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::os::xous::ffi::lend_mut; +use crate::os::xous::services::{dns_server, DnsLendMut}; +use core::convert::{TryFrom, TryInto}; + +pub struct DnsError { + pub code: u8, +} + +#[repr(C, align(4096))] +struct LookupHostQuery([u8; 4096]); + +pub struct LookupHost { + data: LookupHostQuery, + port: u16, + offset: usize, + count: usize, +} + +impl LookupHost { + pub fn port(&self) -> u16 { + self.port + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + if self.offset >= self.data.0.len() { + return None; + } + match self.data.0.get(self.offset) { + Some(&4) => { + self.offset += 1; + if self.offset + 4 > self.data.0.len() { + return None; + } + let result = Some(SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::new( + self.data.0[self.offset], + self.data.0[self.offset + 1], + self.data.0[self.offset + 2], + self.data.0[self.offset + 3], + ), + self.port, + ))); + self.offset += 4; + result + } + Some(&6) => { + self.offset += 1; + if self.offset + 16 > self.data.0.len() { + return None; + } + let mut new_addr = [0u8; 16]; + for (src, octet) in self.data.0[(self.offset + 1)..(self.offset + 16 + 1)] + .iter() + .zip(new_addr.iter_mut()) + { + *octet = *src; + } + let result = + Some(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.port, 0, 0))); + self.offset += 16; + result + } + _ => None, + } + } +} + +pub fn lookup(query: &str, port: u16) -> Result { + let mut result = LookupHost { data: LookupHostQuery([0u8; 4096]), offset: 0, count: 0, port }; + + // Copy the query into the message that gets sent to the DNS server + for (query_byte, result_byte) in query.as_bytes().iter().zip(result.data.0.iter_mut()) { + *result_byte = *query_byte; + } + + lend_mut( + dns_server(), + DnsLendMut::RawLookup.into(), + &mut result.data.0, + 0, + query.as_bytes().len(), + ) + .unwrap(); + if result.data.0[0] != 0 { + return Err(DnsError { code: result.data.0[1] }); + } + assert_eq!(result.offset, 0); + result.count = result.data.0[1] as usize; + + // Advance the offset to the first record + result.offset = 2; + Ok(result) +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(s: &str) -> io::Result { + macro_rules! try_opt { + ($e:expr, $msg:expr) => { + match $e { + Some(r) => r, + None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &$msg)), + } + }; + } + + // split the string by ':' and convert the second part to u16 + let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); + let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); + (host, port).try_into() + } +} + +impl TryFrom<(&str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(v: (&str, u16)) -> io::Result { + lookup(v.0, v.1) + .map_err(|_e| io::const_io_error!(io::ErrorKind::InvalidInput, &"DNS failure")) + } +} diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/pal/xous/net/mod.rs new file mode 100644 index 00000000000..b5a3da136a6 --- /dev/null +++ b/library/std/src/sys/pal/xous/net/mod.rs @@ -0,0 +1,84 @@ +mod dns; + +mod tcpstream; +pub use tcpstream::*; + +mod tcplistener; +pub use tcplistener::*; + +mod udp; +pub use udp::*; + +// this structure needs to be synchronized with what's in net/src/api.rs +#[repr(C)] +#[derive(Debug)] +enum NetError { + // Ok = 0, + Unaddressable = 1, + SocketInUse = 2, + // AccessDenied = 3, + Invalid = 4, + // Finished = 5, + LibraryError = 6, + // AlreadyUsed = 7, + TimedOut = 8, + WouldBlock = 9, +} + +#[repr(C, align(4096))] +struct ConnectRequest { + raw: [u8; 4096], +} + +#[repr(C, align(4096))] +struct SendData { + raw: [u8; 4096], +} + +#[repr(C, align(4096))] +pub struct ReceiveData { + raw: [u8; 4096], +} + +#[repr(C, align(4096))] +pub struct GetAddress { + raw: [u8; 4096], +} + +pub use dns::LookupHost; + +#[allow(nonstandard_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr {} +} diff --git a/library/std/src/sys/pal/xous/net/tcplistener.rs b/library/std/src/sys/pal/xous/net/tcplistener.rs new file mode 100644 index 00000000000..47305013083 --- /dev/null +++ b/library/std/src/sys/pal/xous/net/tcplistener.rs @@ -0,0 +1,247 @@ +use super::*; +use crate::fmt; +use crate::io; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use crate::os::xous::services; +use crate::sync::Arc; +use core::convert::TryInto; +use core::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering}; + +macro_rules! unimpl { + () => { + return Err(io::const_io_error!( + io::ErrorKind::Unsupported, + &"This function is not yet implemented", + )); + }; +} + +#[derive(Clone)] +pub struct TcpListener { + fd: Arc, + local: SocketAddr, + handle_count: Arc, + nonblocking: Arc, +} + +impl TcpListener { + pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result { + let mut addr = *socketaddr?; + + let fd = TcpListener::bind_inner(&mut addr)?; + return Ok(TcpListener { + fd: Arc::new(AtomicU16::new(fd)), + local: addr, + handle_count: Arc::new(AtomicUsize::new(1)), + nonblocking: Arc::new(AtomicBool::new(false)), + }); + } + + /// This returns the raw fd of a Listener, so that it can also be used by the + /// accept routine to replenish the Listener object after its handle has been converted into + /// a TcpStream object. + fn bind_inner(addr: &mut SocketAddr) -> io::Result { + // Construct the request + let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; + + // Serialize the StdUdpBind structure. This is done "manually" because we don't want to + // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous. + let port_bytes = addr.port().to_le_bytes(); + connect_request.raw[0] = port_bytes[0]; + connect_request.raw[1] = port_bytes[1]; + match addr.ip() { + IpAddr::V4(addr) => { + connect_request.raw[2] = 4; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + IpAddr::V6(addr) => { + connect_request.raw[2] = 6; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + } + + let Ok((_, valid)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdTcpListen.into(), + &mut connect_request.raw, + 0, + 4096, + ) else { + return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response")); + }; + + // The first four bytes should be zero upon success, and will be nonzero + // for an error. + let response = connect_request.raw; + if response[0] != 0 || valid == 0 { + let errcode = response[1]; + if errcode == NetError::SocketInUse as u8 { + return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); + } else if errcode == NetError::Invalid as u8 { + return Err(io::const_io_error!( + io::ErrorKind::AddrNotAvailable, + &"Invalid address" + )); + } else if errcode == NetError::LibraryError as u8 { + return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); + } else { + return Err(io::const_io_error!( + io::ErrorKind::Other, + &"Unable to connect or internal error" + )); + } + } + let fd = response[1] as usize; + if addr.port() == 0 { + // oddly enough, this is a valid port and it means "give me something valid, up to you what that is" + let assigned_port = u16::from_le_bytes(response[2..4].try_into().unwrap()); + addr.set_port(assigned_port); + } + // println!("TcpListening with file handle of {}\r\n", fd); + Ok(fd.try_into().unwrap()) + } + + pub fn socket_addr(&self) -> io::Result { + Ok(self.local) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let mut receive_request = ReceiveData { raw: [0u8; 4096] }; + + if self.nonblocking.load(Ordering::Relaxed) { + // nonblocking + receive_request.raw[0] = 0; + } else { + // blocking + receive_request.raw[0] = 1; + } + + if let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdTcpAccept(self.fd.load(Ordering::Relaxed)).into(), + &mut receive_request.raw, + 0, + 0, + ) { + if receive_request.raw[0] != 0 { + // error case + if receive_request.raw[1] == NetError::TimedOut as u8 { + return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"accept timed out",)); + } else if receive_request.raw[1] == NetError::WouldBlock as u8 { + return Err(io::const_io_error!( + io::ErrorKind::WouldBlock, + &"accept would block", + )); + } else if receive_request.raw[1] == NetError::LibraryError as u8 { + return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); + } else { + return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); + } + } else { + // accept successful + let rr = &receive_request.raw; + let stream_fd = u16::from_le_bytes(rr[1..3].try_into().unwrap()); + let port = u16::from_le_bytes(rr[20..22].try_into().unwrap()); + let addr = if rr[3] == 4 { + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(rr[4], rr[5], rr[6], rr[7])), port) + } else if rr[3] == 6 { + SocketAddr::new( + IpAddr::V6(Ipv6Addr::new( + u16::from_be_bytes(rr[4..6].try_into().unwrap()), + u16::from_be_bytes(rr[6..8].try_into().unwrap()), + u16::from_be_bytes(rr[8..10].try_into().unwrap()), + u16::from_be_bytes(rr[10..12].try_into().unwrap()), + u16::from_be_bytes(rr[12..14].try_into().unwrap()), + u16::from_be_bytes(rr[14..16].try_into().unwrap()), + u16::from_be_bytes(rr[16..18].try_into().unwrap()), + u16::from_be_bytes(rr[18..20].try_into().unwrap()), + )), + port, + ) + } else { + return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); + }; + + // replenish the listener + let mut local_copy = self.local.clone(); // port is non-0 by this time, but the method signature needs a mut + let new_fd = TcpListener::bind_inner(&mut local_copy)?; + self.fd.store(new_fd, Ordering::Relaxed); + + // now return a stream converted from the old stream's fd + Ok((TcpStream::from_listener(stream_fd, self.local.port(), port, addr), addr)) + } + } else { + Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unable to accept")) + } + } + + pub fn duplicate(&self) -> io::Result { + self.handle_count.fetch_add(1, Ordering::Relaxed); + Ok(self.clone()) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + if ttl > 255 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256")); + } + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdSetTtlTcp(self.fd.load(Ordering::Relaxed), ttl).into(), + ) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|_| ()) + } + + pub fn ttl(&self) -> io::Result { + Ok(crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdGetTtlTcp(self.fd.load(Ordering::Relaxed)).into(), + ) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|res| res[0] as _)?) + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn only_v6(&self) -> io::Result { + unimpl!(); + } + + pub fn take_error(&self) -> io::Result> { + // this call doesn't have a meaning on our platform, but we can at least not panic if it's used. + Ok(None) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.nonblocking.store(nonblocking, Ordering::Relaxed); + Ok(()) + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TCP listening on {:?}", self.local) + } +} + +impl Drop for TcpListener { + fn drop(&mut self) { + if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 { + // only drop if we're the last clone + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + crate::os::xous::services::NetBlockingScalar::StdTcpClose( + self.fd.load(Ordering::Relaxed), + ) + .into(), + ) + .unwrap(); + } + } +} diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/pal/xous/net/tcpstream.rs new file mode 100644 index 00000000000..7149678118a --- /dev/null +++ b/library/std/src/sys/pal/xous/net/tcpstream.rs @@ -0,0 +1,435 @@ +use super::*; +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::os::xous::services; +use crate::sync::Arc; +use crate::time::Duration; +use core::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; + +macro_rules! unimpl { + () => { + return Err(io::const_io_error!( + io::ErrorKind::Unsupported, + &"This function is not yet implemented", + )); + }; +} + +enum ReadOrPeek { + Read, + Peek, +} + +#[derive(Clone)] +pub struct TcpStream { + fd: u16, + local_port: u16, + remote_port: u16, + peer_addr: SocketAddr, + // milliseconds + read_timeout: Arc, + // milliseconds + write_timeout: Arc, + handle_count: Arc, + nonblocking: Arc, +} + +fn sockaddr_to_buf(duration: Duration, addr: &SocketAddr, buf: &mut [u8]) { + // Construct the request. + let port_bytes = addr.port().to_le_bytes(); + buf[0] = port_bytes[0]; + buf[1] = port_bytes[1]; + for (dest, src) in buf[2..].iter_mut().zip((duration.as_millis() as u64).to_le_bytes()) { + *dest = src; + } + match addr.ip() { + IpAddr::V4(addr) => { + buf[10] = 4; + for (dest, src) in buf[11..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + IpAddr::V6(addr) => { + buf[10] = 6; + for (dest, src) in buf[11..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + } +} + +impl TcpStream { + pub(crate) fn from_listener( + fd: u16, + local_port: u16, + remote_port: u16, + peer_addr: SocketAddr, + ) -> TcpStream { + TcpStream { + fd, + local_port, + remote_port, + peer_addr, + read_timeout: Arc::new(AtomicU32::new(0)), + write_timeout: Arc::new(AtomicU32::new(0)), + handle_count: Arc::new(AtomicUsize::new(1)), + nonblocking: Arc::new(AtomicBool::new(false)), + } + } + + pub fn connect(socketaddr: io::Result<&SocketAddr>) -> io::Result { + Self::connect_timeout(socketaddr?, Duration::ZERO) + } + + pub fn connect_timeout(addr: &SocketAddr, duration: Duration) -> io::Result { + let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; + + // Construct the request. + sockaddr_to_buf(duration, &addr, &mut connect_request.raw); + + let Ok((_, valid)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdTcpConnect.into(), + &mut connect_request.raw, + 0, + 4096, + ) else { + return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response")); + }; + + // The first four bytes should be zero upon success, and will be nonzero + // for an error. + let response = connect_request.raw; + if response[0] != 0 || valid == 0 { + // errcode is a u8 but stuck in a u16 where the upper byte is invalid. Mask & decode accordingly. + let errcode = response[0]; + if errcode == NetError::SocketInUse as u8 { + return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use",)); + } else if errcode == NetError::Unaddressable as u8 { + return Err(io::const_io_error!( + io::ErrorKind::AddrNotAvailable, + &"Invalid address", + )); + } else { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + &"Unable to connect or internal error", + )); + } + } + let fd = u16::from_le_bytes([response[2], response[3]]); + let local_port = u16::from_le_bytes([response[4], response[5]]); + let remote_port = u16::from_le_bytes([response[6], response[7]]); + // println!( + // "Connected with local port of {}, remote port of {}, file handle of {}", + // local_port, remote_port, fd + // ); + Ok(TcpStream { + fd, + local_port, + remote_port, + peer_addr: *addr, + read_timeout: Arc::new(AtomicU32::new(0)), + write_timeout: Arc::new(AtomicU32::new(0)), + handle_count: Arc::new(AtomicUsize::new(1)), + nonblocking: Arc::new(AtomicBool::new(false)), + }) + } + + pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + if let Some(to) = timeout { + if to.is_zero() { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + &"Zero is an invalid timeout", + )); + } + } + self.read_timeout.store( + timeout.map(|t| t.as_millis().min(u32::MAX as u128) as u32).unwrap_or_default(), + Ordering::Relaxed, + ); + Ok(()) + } + + pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + if let Some(to) = timeout { + if to.is_zero() { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + &"Zero is an invalid timeout", + )); + } + } + self.write_timeout.store( + timeout.map(|t| t.as_millis().min(u32::MAX as u128) as u32).unwrap_or_default(), + Ordering::Relaxed, + ); + Ok(()) + } + + pub fn read_timeout(&self) -> io::Result> { + match self.read_timeout.load(Ordering::Relaxed) { + 0 => Ok(None), + t => Ok(Some(Duration::from_millis(t as u64))), + } + } + + pub fn write_timeout(&self) -> io::Result> { + match self.write_timeout.load(Ordering::Relaxed) { + 0 => Ok(None), + t => Ok(Some(Duration::from_millis(t as u64))), + } + } + + fn read_or_peek(&self, buf: &mut [u8], op: ReadOrPeek) -> io::Result { + let mut receive_request = ReceiveData { raw: [0u8; 4096] }; + let data_to_read = buf.len().min(receive_request.raw.len()); + + let opcode = match op { + ReadOrPeek::Read => { + services::NetLendMut::StdTcpRx(self.fd, self.nonblocking.load(Ordering::Relaxed)) + } + ReadOrPeek::Peek => { + services::NetLendMut::StdTcpPeek(self.fd, self.nonblocking.load(Ordering::Relaxed)) + } + }; + + let Ok((offset, length)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + opcode.into(), + &mut receive_request.raw, + // Reuse the `offset` as the read timeout + self.read_timeout.load(Ordering::Relaxed) as usize, + data_to_read, + ) else { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + &"Library failure: wrong message type or messaging error" + )); + }; + + if offset != 0 { + for (dest, src) in buf.iter_mut().zip(receive_request.raw[..length].iter()) { + *dest = *src; + } + Ok(length) + } else { + let result = receive_request.raw; + if result[0] != 0 { + if result[1] == 8 { + // timed out + return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"Timeout",)); + } + if result[1] == 9 { + // would block + return Err(io::const_io_error!(io::ErrorKind::WouldBlock, &"Would block",)); + } + } + Err(io::const_io_error!(io::ErrorKind::Other, &"recv_slice failure")) + } + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.read_or_peek(buf, ReadOrPeek::Peek) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.read_or_peek(buf, ReadOrPeek::Read) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|b| self.read(b), bufs) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let mut send_request = SendData { raw: [0u8; 4096] }; + for (dest, src) in send_request.raw.iter_mut().zip(buf) { + *dest = *src; + } + let buf_len = send_request.raw.len().min(buf.len()); + + let (_offset, _valid) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdTcpTx(self.fd).into(), + &mut send_request.raw, + // Reuse the offset as the timeout + self.write_timeout.load(Ordering::Relaxed) as usize, + buf_len, + ) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")))?; + + if send_request.raw[0] != 0 { + if send_request.raw[4] == 8 { + // timed out + return Err(io::const_io_error!( + io::ErrorKind::BrokenPipe, + &"Timeout or connection closed", + )); + } else if send_request.raw[4] == 9 { + // would block + return Err(io::const_io_error!(io::ErrorKind::WouldBlock, &"Would block",)); + } else { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + &"Error when sending", + )); + } + } + Ok(u32::from_le_bytes([ + send_request.raw[4], + send_request.raw[5], + send_request.raw[6], + send_request.raw[7], + ]) as usize) + } + + 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 peer_addr(&self) -> io::Result { + Ok(self.peer_addr) + } + + pub fn socket_addr(&self) -> io::Result { + let mut get_addr = GetAddress { raw: [0u8; 4096] }; + + let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdGetAddress(self.fd).into(), + &mut get_addr.raw, + 0, + 0, + ) else { + return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")); + }; + let mut i = get_addr.raw.iter(); + match *i.next().unwrap() { + 4 => Ok(SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::new( + *i.next().unwrap(), + *i.next().unwrap(), + *i.next().unwrap(), + *i.next().unwrap(), + ), + self.local_port, + ))), + 6 => { + let mut new_addr = [0u8; 16]; + for (src, octet) in i.zip(new_addr.iter_mut()) { + *octet = *src; + } + Ok(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.local_port, 0, 0))) + } + _ => Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")), + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdTcpStreamShutdown(self.fd, how).into(), + ) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|_| ()) + } + + pub fn duplicate(&self) -> io::Result { + self.handle_count.fetch_add(1, Ordering::Relaxed); + Ok(self.clone()) + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn linger(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nodelay(&self, enabled: bool) -> io::Result<()> { + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdSetNodelay(self.fd, enabled).into(), + ) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|_| ()) + } + + pub fn nodelay(&self) -> io::Result { + Ok(crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdGetNodelay(self.fd).into(), + ) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|res| res[0] != 0)?) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + if ttl > 255 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256")); + } + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdSetTtlTcp(self.fd, ttl).into(), + ) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|_| ()) + } + + pub fn ttl(&self) -> io::Result { + Ok(crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdGetTtlTcp(self.fd).into(), + ) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|res| res[0] as _)?) + } + + pub fn take_error(&self) -> io::Result> { + // this call doesn't have a meaning on our platform, but we can at least not panic if it's used. + Ok(None) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.nonblocking.store(nonblocking, Ordering::SeqCst); + Ok(()) + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "TCP connection to {:?} port {} to local port {}", + self.peer_addr, self.remote_port, self.local_port + ) + } +} + +impl Drop for TcpStream { + fn drop(&mut self) { + if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 { + // only drop if we're the last clone + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdTcpClose(self.fd).into(), + ) + .unwrap(); + } + } +} diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/pal/xous/net/udp.rs new file mode 100644 index 00000000000..cafa5b3bde8 --- /dev/null +++ b/library/std/src/sys/pal/xous/net/udp.rs @@ -0,0 +1,471 @@ +use super::*; +use crate::cell::Cell; +use crate::fmt; +use crate::io; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use crate::os::xous::services; +use crate::sync::Arc; +use crate::time::Duration; +use core::convert::TryInto; +use core::sync::atomic::{AtomicUsize, Ordering}; + +macro_rules! unimpl { + () => { + return Err(io::const_io_error!( + io::ErrorKind::Unsupported, + &"This function is not yet implemented", + )); + }; +} + +#[derive(Clone)] +pub struct UdpSocket { + fd: u16, + local: SocketAddr, + remote: Cell>, + // in milliseconds. The setting applies only to `recv` calls after the timeout is set. + read_timeout: Cell, + // in milliseconds. The setting applies only to `send` calls after the timeout is set. + write_timeout: Cell, + handle_count: Arc, + nonblocking: Cell, +} + +impl UdpSocket { + pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result { + let addr = socketaddr?; + // Construct the request + let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; + + // Serialize the StdUdpBind structure. This is done "manually" because we don't want to + // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous. + let port_bytes = addr.port().to_le_bytes(); + connect_request.raw[0] = port_bytes[0]; + connect_request.raw[1] = port_bytes[1]; + match addr.ip() { + IpAddr::V4(addr) => { + connect_request.raw[2] = 4; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + IpAddr::V6(addr) => { + connect_request.raw[2] = 6; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + } + + let response = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdUdpBind.into(), + &mut connect_request.raw, + 0, + 4096, + ); + + if let Ok((_, valid)) = response { + // The first four bytes should be zero upon success, and will be nonzero + // for an error. + let response = connect_request.raw; + if response[0] != 0 || valid == 0 { + let errcode = response[1]; + if errcode == NetError::SocketInUse as u8 { + return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); + } else if errcode == NetError::Invalid as u8 { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + &"Port can't be 0 or invalid address" + )); + } else if errcode == NetError::LibraryError as u8 { + return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); + } else { + return Err(io::const_io_error!( + io::ErrorKind::Other, + &"Unable to connect or internal error" + )); + } + } + let fd = response[1] as u16; + return Ok(UdpSocket { + fd, + local: *addr, + remote: Cell::new(None), + read_timeout: Cell::new(0), + write_timeout: Cell::new(0), + handle_count: Arc::new(AtomicUsize::new(1)), + nonblocking: Cell::new(false), + }); + } + Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response")) + } + + pub fn peer_addr(&self) -> io::Result { + match self.remote.get() { + Some(dest) => Ok(dest), + None => Err(io::const_io_error!(io::ErrorKind::NotConnected, &"No peer specified")), + } + } + + pub fn socket_addr(&self) -> io::Result { + Ok(self.local) + } + + fn recv_inner(&self, buf: &mut [u8], do_peek: bool) -> io::Result<(usize, SocketAddr)> { + let mut receive_request = ReceiveData { raw: [0u8; 4096] }; + + if self.nonblocking.get() { + // nonblocking + receive_request.raw[0] = 0; + } else { + // blocking + receive_request.raw[0] = 1; + for (&s, d) in self + .read_timeout + .get() + .to_le_bytes() + .iter() + .zip(receive_request.raw[1..9].iter_mut()) + { + *d = s; + } + } + if let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdUdpRx(self.fd).into(), + &mut receive_request.raw, + if do_peek { 1 } else { 0 }, + 0, + ) { + if receive_request.raw[0] != 0 { + // error case + if receive_request.raw[1] == NetError::TimedOut as u8 { + return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"recv timed out",)); + } else if receive_request.raw[1] == NetError::WouldBlock as u8 { + return Err(io::const_io_error!( + io::ErrorKind::WouldBlock, + &"recv would block", + )); + } else if receive_request.raw[1] == NetError::LibraryError as u8 { + return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); + } else { + return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); + } + } else { + let rr = &receive_request.raw; + let rxlen = u16::from_le_bytes(rr[1..3].try_into().unwrap()); + let port = u16::from_le_bytes(rr[20..22].try_into().unwrap()); + let addr = if rr[3] == 4 { + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(rr[4], rr[5], rr[6], rr[7])), port) + } else if rr[3] == 6 { + SocketAddr::new( + IpAddr::V6(Ipv6Addr::new( + u16::from_be_bytes(rr[4..6].try_into().unwrap()), + u16::from_be_bytes(rr[6..8].try_into().unwrap()), + u16::from_be_bytes(rr[8..10].try_into().unwrap()), + u16::from_be_bytes(rr[10..12].try_into().unwrap()), + u16::from_be_bytes(rr[12..14].try_into().unwrap()), + u16::from_be_bytes(rr[14..16].try_into().unwrap()), + u16::from_be_bytes(rr[16..18].try_into().unwrap()), + u16::from_be_bytes(rr[18..20].try_into().unwrap()), + )), + port, + ) + } else { + return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); + }; + for (&s, d) in rr[22..22 + rxlen as usize].iter().zip(buf.iter_mut()) { + *d = s; + } + Ok((rxlen as usize, addr)) + } + } else { + Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unable to recv")) + } + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_inner(buf, false) + } + + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + self.recv_from(buf).map(|(len, _addr)| len) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_inner(buf, true) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.peek_from(buf).map(|(len, _addr)| len) + } + + pub fn connect(&self, maybe_addr: io::Result<&SocketAddr>) -> io::Result<()> { + let addr = maybe_addr?; + self.remote.set(Some(*addr)); + Ok(()) + } + + pub fn send(&self, buf: &[u8]) -> io::Result { + if let Some(addr) = self.remote.get() { + self.send_to(buf, &addr) + } else { + Err(io::const_io_error!(io::ErrorKind::NotConnected, &"No remote specified")) + } + } + + pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> io::Result { + let mut tx_req = SendData { raw: [0u8; 4096] }; + + // Construct the request. + let port_bytes = addr.port().to_le_bytes(); + tx_req.raw[0] = port_bytes[0]; + tx_req.raw[1] = port_bytes[1]; + match addr.ip() { + IpAddr::V4(addr) => { + tx_req.raw[2] = 4; + for (dest, src) in tx_req.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + IpAddr::V6(addr) => { + tx_req.raw[2] = 6; + for (dest, src) in tx_req.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + } + let len = buf.len() as u16; + let len_bytes = len.to_le_bytes(); + tx_req.raw[19] = len_bytes[0]; + tx_req.raw[20] = len_bytes[1]; + for (&s, d) in buf.iter().zip(tx_req.raw[21..].iter_mut()) { + *d = s; + } + + // let buf = unsafe { + // xous::MemoryRange::new( + // &mut tx_req as *mut SendData as usize, + // core::mem::size_of::(), + // ) + // .unwrap() + // }; + + // write time-outs are implemented on the caller side. Basically, if the Net crate server + // is too busy to take the call immediately: retry, until the timeout is reached. + let now = crate::time::Instant::now(); + let write_timeout = if self.nonblocking.get() { + // nonblocking + core::time::Duration::ZERO + } else { + // blocking + if self.write_timeout.get() == 0 { + // forever + core::time::Duration::from_millis(u64::MAX) + } else { + // or this amount of time + core::time::Duration::from_millis(self.write_timeout.get()) + } + }; + loop { + let response = crate::os::xous::ffi::try_lend_mut( + services::net_server(), + services::NetLendMut::StdUdpTx(self.fd).into(), + &mut tx_req.raw, + 0, + 4096, + ); + match response { + Ok((_, valid)) => { + let response = &tx_req.raw; + if response[0] != 0 || valid == 0 { + let errcode = response[1]; + if errcode == NetError::SocketInUse as u8 { + return Err(io::const_io_error!( + io::ErrorKind::ResourceBusy, + &"Socket in use" + )); + } else if errcode == NetError::Invalid as u8 { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + &"Socket not valid" + )); + } else if errcode == NetError::LibraryError as u8 { + return Err(io::const_io_error!( + io::ErrorKind::Other, + &"Library error" + )); + } else { + return Err(io::const_io_error!( + io::ErrorKind::Other, + &"Unable to connect" + )); + } + } else { + // no error + return Ok(len as usize); + } + } + Err(crate::os::xous::ffi::Error::ServerQueueFull) => { + if now.elapsed() >= write_timeout { + return Err(io::const_io_error!( + io::ErrorKind::WouldBlock, + &"Write timed out" + )); + } else { + // question: do we want to do something a bit more gentle than immediately retrying? + crate::thread::yield_now(); + } + } + _ => return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")), + } + } + } + + pub fn duplicate(&self) -> io::Result { + self.handle_count.fetch_add(1, Ordering::Relaxed); + Ok(self.clone()) + } + + pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + if let Some(d) = timeout { + if d.is_zero() { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + &"Zero duration is invalid" + )); + } + } + self.read_timeout + .set(timeout.map(|t| t.as_millis().min(u64::MAX as u128) as u64).unwrap_or_default()); + Ok(()) + } + + pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + if let Some(d) = timeout { + if d.is_zero() { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + &"Zero duration is invalid" + )); + } + } + self.write_timeout + .set(timeout.map(|t| t.as_millis().min(u64::MAX as u128) as u64).unwrap_or_default()); + Ok(()) + } + + pub fn read_timeout(&self) -> io::Result> { + match self.read_timeout.get() { + 0 => Ok(None), + t => Ok(Some(Duration::from_millis(t as u64))), + } + } + + pub fn write_timeout(&self) -> io::Result> { + match self.write_timeout.get() { + 0 => Ok(None), + t => Ok(Some(Duration::from_millis(t as u64))), + } + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + if ttl > 255 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256")); + } + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdSetTtlUdp(self.fd, ttl).into(), + ) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|_| ()) + } + + pub fn ttl(&self) -> io::Result { + Ok(crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdGetTtlUdp(self.fd).into(), + ) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|res| res[0] as _)?) + } + + pub fn take_error(&self) -> io::Result> { + // this call doesn't have a meaning on our platform, but we can at least not panic if it's used. + Ok(None) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.nonblocking.set(nonblocking); + Ok(()) + } + + // ------------- smoltcp base stack does not have multicast or broadcast support --------------- + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn broadcast(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_loop_v4(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_loop_v6(&self) -> io::Result { + unimpl!(); + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + unimpl!(); + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + unimpl!(); + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + unimpl!(); + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + unimpl!(); + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UDP listening on {:?} to {:?}", self.local, self.remote.get(),) + } +} + +impl Drop for UdpSocket { + fn drop(&mut self) { + if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 { + // only drop if we're the last clone + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdUdpClose(self.fd).into(), + ) + .unwrap(); + } + } +} From aa738606282c38194fefc06b7bbf7e0b5d26400e Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 29 Dec 2023 18:06:55 +0800 Subject: [PATCH 12/23] std: xous: mark stdio structs as `repr(C)` Ensure these structs have a well-defined ABI layout. Signed-off-by: Sean Cross --- library/std/src/sys/pal/xous/stdio.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/pal/xous/stdio.rs b/library/std/src/sys/pal/xous/stdio.rs index 2ac694641ba..546d0db6026 100644 --- a/library/std/src/sys/pal/xous/stdio.rs +++ b/library/std/src/sys/pal/xous/stdio.rs @@ -27,7 +27,7 @@ impl Stdout { impl io::Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result { - #[repr(align(4096))] + #[repr(C, align(4096))] struct LendBuffer([u8; 4096]); let mut lend_buffer = LendBuffer([0u8; 4096]); let connection = log_server(); @@ -53,7 +53,7 @@ impl Stderr { impl io::Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result { - #[repr(align(4096))] + #[repr(C, align(4096))] struct LendBuffer([u8; 4096]); let mut lend_buffer = LendBuffer([0u8; 4096]); let connection = log_server(); From 99b06594a8fc0bbfa2fe86ee3a31c71bf8586166 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 29 Dec 2023 19:05:57 +0800 Subject: [PATCH 13/23] std: xous: use constants for stdout and stderr Use constants for the opcodes when writing to stdout or stderr. There still is no stdin operation. Signed-off-by: Sean Cross --- library/std/src/os/xous/services/log.rs | 11 +++++++++++ library/std/src/sys/pal/xous/stdio.rs | 8 +++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/library/std/src/os/xous/services/log.rs b/library/std/src/os/xous/services/log.rs index e6bae929eac..55a501dc7d0 100644 --- a/library/std/src/os/xous/services/log.rs +++ b/library/std/src/os/xous/services/log.rs @@ -45,6 +45,17 @@ impl<'a> Into<[usize; 5]> for LogScalar<'a> { } } +pub(crate) enum LogLend { + StandardOutput = 1, + StandardError = 2, +} + +impl Into for LogLend { + fn into(self) -> usize { + self as usize + } +} + /// Return a `Connection` to the log server, which is used for printing messages to /// the console and reporting panics. If the log server has not yet started, this /// will block until the server is running. It is safe to call this multiple times, diff --git a/library/std/src/sys/pal/xous/stdio.rs b/library/std/src/sys/pal/xous/stdio.rs index 546d0db6026..11608964b52 100644 --- a/library/std/src/sys/pal/xous/stdio.rs +++ b/library/std/src/sys/pal/xous/stdio.rs @@ -5,7 +5,7 @@ pub struct Stdout {} pub struct Stderr; use crate::os::xous::ffi::{lend, try_lend, try_scalar, Connection}; -use crate::os::xous::services::{log_server, try_connect, LogScalar}; +use crate::os::xous::services::{log_server, try_connect, LogLend, LogScalar}; impl Stdin { pub const fn new() -> Stdin { @@ -35,7 +35,8 @@ impl io::Write for Stdout { for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { *dest = *src; } - lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap(); + lend(connection, LogLend::StandardOutput.into(), &lend_buffer.0, 0, chunk.len()) + .unwrap(); } Ok(buf.len()) } @@ -61,7 +62,8 @@ impl io::Write for Stderr { for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { *dest = *src; } - lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap(); + lend(connection, LogLend::StandardError.into(), &lend_buffer.0, 0, chunk.len()) + .unwrap(); } Ok(buf.len()) } From 50e4fede2427f54cbdb9e4d556aa9ef4c68332fb Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 29 Dec 2023 19:16:43 +0800 Subject: [PATCH 14/23] xous: thread: mark thread_main() as divergent The thread wrapper function never returns, so we can mark it as divergent. Signed-off-by: Sean Cross --- library/std/src/sys/pal/xous/thread.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index 78c68de7bf3..0f452e07a5c 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -68,14 +68,18 @@ impl Thread { ) .map_err(|code| io::Error::from_raw_os_error(code as i32))?; - extern "C" fn thread_start(main: *mut usize, guard_page_pre: usize, stack_size: usize) { + extern "C" fn thread_start( + main: *mut usize, + guard_page_pre: usize, + stack_size: usize, + ) -> ! { unsafe { - // Finally, let's run some code. + // Run the contents of the new thread. Box::from_raw(main as *mut Box)(); } // Destroy TLS, which will free the TLS page and call the destructor for - // any thread local storage. + // any thread local storage (if any). unsafe { crate::sys::thread_local_key::destroy_tls(); } From 89cf17777bd3e1ebb15cc6e85cbcbd955bf12252 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 13 Jan 2024 20:04:47 +0000 Subject: [PATCH 15/23] std::net: bind update for using backlog as `-1` too. Albeit not documented, macOs also support negative value for the backlog argument. ref: https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/kern/uipc_socket.c#L1061 --- library/std/src/os/unix/net/listener.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 8bf1e2dca6f..ecc0bbce543 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -80,7 +80,12 @@ impl UnixListener { target_os = "horizon" ))] const backlog: libc::c_int = 128; - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] + #[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd", + target_os = "macos" + ))] const backlog: libc::c_int = -1; #[cfg(not(any( target_os = "windows", @@ -88,6 +93,7 @@ impl UnixListener { target_os = "linux", target_os = "freebsd", target_os = "openbsd", + target_os = "macos", target_os = "espidf", target_os = "horizon" )))] From a8bb418ae5952e2fc9f96fe40e9c0a36c9864cf9 Mon Sep 17 00:00:00 2001 From: asquared31415 <34665709+asquared31415@users.noreply.github.com> Date: Sat, 13 Jan 2024 23:06:34 +0000 Subject: [PATCH 16/23] make unsafe_op_in_unsafe_fn MachineApplicable and add it to 2024 compatibility --- compiler/rustc_lint_defs/src/builtin.rs | 5 ++ compiler/rustc_mir_build/src/errors.rs | 2 +- .../safe-calls.stderr | 1 + ...edition-2024-unsafe_op_in_unsafe_fn.stderr | 1 + .../auxiliary/external_unsafe_macro.rs | 0 .../edition_2024_default.rs | 20 ++++++++ .../edition_2024_default.stderr | 17 +++++++ .../in_2024_compatibility.rs | 9 ++++ .../in_2024_compatibility.stderr | 22 ++++++++ .../rfc-2585-unsafe_op_in_unsafe_fn.rs | 0 .../rfc-2585-unsafe_op_in_unsafe_fn.stderr | 6 +++ .../wrapping-unsafe-block-sugg.fixed | 50 +++++++++++-------- .../wrapping-unsafe-block-sugg.rs | 50 +++++++++++-------- .../wrapping-unsafe-block-sugg.stderr | 28 +++++++---- 14 files changed, 160 insertions(+), 51 deletions(-) rename tests/ui/unsafe/{ => unsafe_op_in_unsafe_fn}/auxiliary/external_unsafe_macro.rs (100%) create mode 100644 tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.rs create mode 100644 tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr create mode 100644 tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.rs create mode 100644 tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr rename tests/ui/unsafe/{ => unsafe_op_in_unsafe_fn}/rfc-2585-unsafe_op_in_unsafe_fn.rs (100%) rename tests/ui/unsafe/{ => unsafe_op_in_unsafe_fn}/rfc-2585-unsafe_op_in_unsafe_fn.stderr (87%) rename tests/ui/unsafe/{ => unsafe_op_in_unsafe_fn}/wrapping-unsafe-block-sugg.fixed (56%) rename tests/ui/unsafe/{ => unsafe_op_in_unsafe_fn}/wrapping-unsafe-block-sugg.rs (56%) rename tests/ui/unsafe/{ => unsafe_op_in_unsafe_fn}/wrapping-unsafe-block-sugg.stderr (74%) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index e917e7cb02b..51539703746 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2755,6 +2755,11 @@ declare_lint! { pub UNSAFE_OP_IN_UNSAFE_FN, Allow, "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), + reference: "issue #71668 ", + explain_reason: false + }; @edition Edition2024 => Warn; } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 61ad99acf38..7b8cb130e44 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -430,7 +430,7 @@ impl AddToDiagnostic for UnsafeNotInheritedLintNote { diag.tool_only_multipart_suggestion( fluent::mir_build_wrap_suggestion, vec![(body_start, "{ unsafe ".into()), (body_end, "}".into())], - Applicability::MaybeIncorrect, + Applicability::MachineApplicable, ); } } diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr index e17859eb40f..d9d7e297f8e 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr @@ -97,6 +97,7 @@ error: call to function `sse2` with `#[target_feature]` is unsafe and requires u LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` | + = note: for more information, see issue #71668 = help: in order for the call to be safe, the context requires the following additional target feature: sse2 = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` note: an unsafe function restricts its caller, but its body is safe by default diff --git a/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr b/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr index 1187c2d80f3..2eb1754392e 100644 --- a/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr +++ b/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr @@ -4,6 +4,7 @@ warning: call to unsafe function `unsf` is unsafe and requires unsafe block (err LL | unsf(); | ^^^^^^ call to unsafe function | + = note: for more information, see issue #71668 = note: consult the function's documentation for information on how to avoid undefined behavior note: an unsafe function restricts its caller, but its body is safe by default --> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:9:1 diff --git a/tests/ui/unsafe/auxiliary/external_unsafe_macro.rs b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/auxiliary/external_unsafe_macro.rs similarity index 100% rename from tests/ui/unsafe/auxiliary/external_unsafe_macro.rs rename to tests/ui/unsafe/unsafe_op_in_unsafe_fn/auxiliary/external_unsafe_macro.rs diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.rs b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.rs new file mode 100644 index 00000000000..4196b1cae24 --- /dev/null +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.rs @@ -0,0 +1,20 @@ +// edition: 2024 +// compile-flags: -Zunstable-options +// check-pass + +// Tests that `unsafe_op_in_unsafe_fn` is warn-by-default in edition 2024 and that the +// `unused_unsafe` lint does not consider the inner unsafe block to be unused. +#![crate_type = "lib"] +#![deny(unused_unsafe)] + +unsafe fn unsf() {} + +unsafe fn foo() { + unsf(); + //~^ WARN + + // no unused_unsafe + unsafe { + unsf(); + } +} diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr new file mode 100644 index 00000000000..e6d1d4e5cc7 --- /dev/null +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr @@ -0,0 +1,17 @@ +warning: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) + --> $DIR/edition_2024_default.rs:13:5 + | +LL | unsf(); + | ^^^^^^ call to unsafe function + | + = note: for more information, see issue #71668 + = note: consult the function's documentation for information on how to avoid undefined behavior +note: an unsafe function restricts its caller, but its body is safe by default + --> $DIR/edition_2024_default.rs:12:1 + | +LL | unsafe fn foo() { + | ^^^^^^^^^^^^^^^ + = note: `#[warn(unsafe_op_in_unsafe_fn)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.rs b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.rs new file mode 100644 index 00000000000..ec04bf8d631 --- /dev/null +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.rs @@ -0,0 +1,9 @@ +#![deny(rust_2024_compatibility)] +#![crate_type = "lib"] + +unsafe fn unsf() {} + +unsafe fn foo() { + unsf(); + //~^ ERROR call to unsafe function `unsf` is unsafe and requires unsafe block +} diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr new file mode 100644 index 00000000000..5092c1e689d --- /dev/null +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr @@ -0,0 +1,22 @@ +error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) + --> $DIR/in_2024_compatibility.rs:7:5 + | +LL | unsf(); + | ^^^^^^ call to unsafe function + | + = note: for more information, see issue #71668 + = note: consult the function's documentation for information on how to avoid undefined behavior +note: an unsafe function restricts its caller, but its body is safe by default + --> $DIR/in_2024_compatibility.rs:6:1 + | +LL | unsafe fn foo() { + | ^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/in_2024_compatibility.rs:1:9 + | +LL | #![deny(rust_2024_compatibility)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(unsafe_op_in_unsafe_fn)]` implied by `#[deny(rust_2024_compatibility)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.rs similarity index 100% rename from tests/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs rename to tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.rs diff --git a/tests/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr similarity index 87% rename from tests/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr rename to tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr index ea0659b2e10..4bc604a110e 100644 --- a/tests/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr @@ -4,6 +4,7 @@ error: call to unsafe function `unsf` is unsafe and requires unsafe block (error LL | unsf(); | ^^^^^^ call to unsafe function | + = note: for more information, see issue #71668 = note: consult the function's documentation for information on how to avoid undefined behavior note: an unsafe function restricts its caller, but its body is safe by default --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:8:1 @@ -22,6 +23,7 @@ error: dereference of raw pointer is unsafe and requires unsafe block (error E01 LL | *PTR; | ^^^^ dereference of raw pointer | + = note: for more information, see issue #71668 = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior error: use of mutable static is unsafe and requires unsafe block (error E0133) @@ -30,6 +32,7 @@ error: use of mutable static is unsafe and requires unsafe block (error E0133) LL | VOID = (); | ^^^^ use of mutable static | + = note: for more information, see issue #71668 = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error: unnecessary `unsafe` block @@ -50,6 +53,7 @@ error: call to unsafe function `unsf` is unsafe and requires unsafe block (error LL | unsf(); | ^^^^^^ call to unsafe function | + = note: for more information, see issue #71668 = note: consult the function's documentation for information on how to avoid undefined behavior note: an unsafe function restricts its caller, but its body is safe by default --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:23:1 @@ -69,6 +73,7 @@ error: dereference of raw pointer is unsafe and requires unsafe block (error E01 LL | *PTR; | ^^^^ dereference of raw pointer | + = note: for more information, see issue #71668 = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior error: use of mutable static is unsafe and requires unsafe block (error E0133) @@ -77,6 +82,7 @@ error: use of mutable static is unsafe and requires unsafe block (error E0133) LL | VOID = (); | ^^^^ use of mutable static | + = note: for more information, see issue #71668 = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error: unnecessary `unsafe` block diff --git a/tests/ui/unsafe/wrapping-unsafe-block-sugg.fixed b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.fixed similarity index 56% rename from tests/ui/unsafe/wrapping-unsafe-block-sugg.fixed rename to tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.fixed index 20f4fe847da..8f5cc014a3b 100644 --- a/tests/ui/unsafe/wrapping-unsafe-block-sugg.fixed +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.fixed @@ -11,50 +11,60 @@ unsafe fn unsf() {} pub unsafe fn foo() { unsafe { //~^ NOTE an unsafe function restricts its caller, but its body is safe by default unsf(); //~ ERROR call to unsafe function `unsf` is unsafe - //~^ NOTE - //~| NOTE + //~^ NOTE call to unsafe function + //~| NOTE for more information, see issue #71668 + //~| NOTE consult the function's documentation unsf(); //~ ERROR call to unsafe function `unsf` is unsafe - //~^ NOTE - //~| NOTE + //~^ NOTE call to unsafe function + //~| NOTE for more information, see issue #71668 + //~| NOTE consult the function's documentation }} pub unsafe fn bar(x: *const i32) -> i32 { unsafe { //~^ NOTE an unsafe function restricts its caller, but its body is safe by default let y = *x; //~ ERROR dereference of raw pointer is unsafe and requires unsafe block - //~^ NOTE - //~| NOTE + //~^ NOTE dereference of raw pointer + //~| NOTE for more information, see issue #71668 + //~| NOTE raw pointers may be null y + *x //~ ERROR dereference of raw pointer is unsafe and requires unsafe block - //~^ NOTE - //~| NOTE + //~^ NOTE dereference of raw pointer + //~| NOTE for more information, see issue #71668 + //~| NOTE raw pointers may be null }} static mut BAZ: i32 = 0; pub unsafe fn baz() -> i32 { unsafe { //~^ NOTE an unsafe function restricts its caller, but its body is safe by default let y = BAZ; //~ ERROR use of mutable static is unsafe and requires unsafe block - //~^ NOTE - //~| NOTE + //~^ NOTE use of mutable static + //~| NOTE for more information, see issue #71668 + //~| NOTE mutable statics can be mutated by multiple threads y + BAZ //~ ERROR use of mutable static is unsafe and requires unsafe block - //~^ NOTE - //~| NOTE + //~^ NOTE use of mutable static + //~| NOTE for more information, see issue #71668 + //~| NOTE mutable statics can be mutated by multiple threads }} macro_rules! unsafe_macro { () => (unsf()) } //~^ ERROR call to unsafe function `unsf` is unsafe -//~| NOTE -//~| NOTE +//~| NOTE call to unsafe function +//~| NOTE for more information, see issue #71668 +//~| NOTE consult the function's documentation //~| ERROR call to unsafe function `unsf` is unsafe -//~| NOTE -//~| NOTE +//~| NOTE call to unsafe function +//~| NOTE for more information, see issue #71668 +//~| NOTE consult the function's documentation pub unsafe fn unsafe_in_macro() { unsafe { //~^ NOTE an unsafe function restricts its caller, but its body is safe by default unsafe_macro!(); - //~^ NOTE - //~| NOTE + //~^ NOTE in this expansion + //~| NOTE in this expansion + //~| NOTE in this expansion unsafe_macro!(); - //~^ NOTE - //~| NOTE + //~^ NOTE in this expansion + //~| NOTE in this expansion + //~| NOTE in this expansion }} pub unsafe fn unsafe_in_external_macro() { diff --git a/tests/ui/unsafe/wrapping-unsafe-block-sugg.rs b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.rs similarity index 56% rename from tests/ui/unsafe/wrapping-unsafe-block-sugg.rs rename to tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.rs index 13a446d2d24..90c9e282396 100644 --- a/tests/ui/unsafe/wrapping-unsafe-block-sugg.rs +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.rs @@ -11,50 +11,60 @@ unsafe fn unsf() {} pub unsafe fn foo() { //~^ NOTE an unsafe function restricts its caller, but its body is safe by default unsf(); //~ ERROR call to unsafe function `unsf` is unsafe - //~^ NOTE - //~| NOTE + //~^ NOTE call to unsafe function + //~| NOTE for more information, see issue #71668 + //~| NOTE consult the function's documentation unsf(); //~ ERROR call to unsafe function `unsf` is unsafe - //~^ NOTE - //~| NOTE + //~^ NOTE call to unsafe function + //~| NOTE for more information, see issue #71668 + //~| NOTE consult the function's documentation } pub unsafe fn bar(x: *const i32) -> i32 { //~^ NOTE an unsafe function restricts its caller, but its body is safe by default let y = *x; //~ ERROR dereference of raw pointer is unsafe and requires unsafe block - //~^ NOTE - //~| NOTE + //~^ NOTE dereference of raw pointer + //~| NOTE for more information, see issue #71668 + //~| NOTE raw pointers may be null y + *x //~ ERROR dereference of raw pointer is unsafe and requires unsafe block - //~^ NOTE - //~| NOTE + //~^ NOTE dereference of raw pointer + //~| NOTE for more information, see issue #71668 + //~| NOTE raw pointers may be null } static mut BAZ: i32 = 0; pub unsafe fn baz() -> i32 { //~^ NOTE an unsafe function restricts its caller, but its body is safe by default let y = BAZ; //~ ERROR use of mutable static is unsafe and requires unsafe block - //~^ NOTE - //~| NOTE + //~^ NOTE use of mutable static + //~| NOTE for more information, see issue #71668 + //~| NOTE mutable statics can be mutated by multiple threads y + BAZ //~ ERROR use of mutable static is unsafe and requires unsafe block - //~^ NOTE - //~| NOTE + //~^ NOTE use of mutable static + //~| NOTE for more information, see issue #71668 + //~| NOTE mutable statics can be mutated by multiple threads } macro_rules! unsafe_macro { () => (unsf()) } //~^ ERROR call to unsafe function `unsf` is unsafe -//~| NOTE -//~| NOTE +//~| NOTE call to unsafe function +//~| NOTE for more information, see issue #71668 +//~| NOTE consult the function's documentation //~| ERROR call to unsafe function `unsf` is unsafe -//~| NOTE -//~| NOTE +//~| NOTE call to unsafe function +//~| NOTE for more information, see issue #71668 +//~| NOTE consult the function's documentation pub unsafe fn unsafe_in_macro() { //~^ NOTE an unsafe function restricts its caller, but its body is safe by default unsafe_macro!(); - //~^ NOTE - //~| NOTE + //~^ NOTE in this expansion + //~| NOTE in this expansion + //~| NOTE in this expansion unsafe_macro!(); - //~^ NOTE - //~| NOTE + //~^ NOTE in this expansion + //~| NOTE in this expansion + //~| NOTE in this expansion } pub unsafe fn unsafe_in_external_macro() { diff --git a/tests/ui/unsafe/wrapping-unsafe-block-sugg.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr similarity index 74% rename from tests/ui/unsafe/wrapping-unsafe-block-sugg.stderr rename to tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr index 84b58bc0288..b9f5969474d 100644 --- a/tests/ui/unsafe/wrapping-unsafe-block-sugg.stderr +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr @@ -4,6 +4,7 @@ error: call to unsafe function `unsf` is unsafe and requires unsafe block (error LL | unsf(); | ^^^^^^ call to unsafe function | + = note: for more information, see issue #71668 = note: consult the function's documentation for information on how to avoid undefined behavior note: an unsafe function restricts its caller, but its body is safe by default --> $DIR/wrapping-unsafe-block-sugg.rs:11:1 @@ -17,57 +18,62 @@ LL | #![deny(unsafe_op_in_unsafe_fn)] | ^^^^^^^^^^^^^^^^^^^^^^ error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) - --> $DIR/wrapping-unsafe-block-sugg.rs:16:5 + --> $DIR/wrapping-unsafe-block-sugg.rs:17:5 | LL | unsf(); | ^^^^^^ call to unsafe function | + = note: for more information, see issue #71668 = note: consult the function's documentation for information on how to avoid undefined behavior error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) - --> $DIR/wrapping-unsafe-block-sugg.rs:23:13 + --> $DIR/wrapping-unsafe-block-sugg.rs:25:13 | LL | let y = *x; | ^^ dereference of raw pointer | + = note: for more information, see issue #71668 = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/wrapping-unsafe-block-sugg.rs:21:1 + --> $DIR/wrapping-unsafe-block-sugg.rs:23:1 | LL | pub unsafe fn bar(x: *const i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) - --> $DIR/wrapping-unsafe-block-sugg.rs:26:9 + --> $DIR/wrapping-unsafe-block-sugg.rs:29:9 | LL | y + *x | ^^ dereference of raw pointer | + = note: for more information, see issue #71668 = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior error: use of mutable static is unsafe and requires unsafe block (error E0133) - --> $DIR/wrapping-unsafe-block-sugg.rs:34:13 + --> $DIR/wrapping-unsafe-block-sugg.rs:38:13 | LL | let y = BAZ; | ^^^ use of mutable static | + = note: for more information, see issue #71668 = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/wrapping-unsafe-block-sugg.rs:32:1 + --> $DIR/wrapping-unsafe-block-sugg.rs:36:1 | LL | pub unsafe fn baz() -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of mutable static is unsafe and requires unsafe block (error E0133) - --> $DIR/wrapping-unsafe-block-sugg.rs:37:9 + --> $DIR/wrapping-unsafe-block-sugg.rs:42:9 | LL | y + BAZ | ^^^ use of mutable static | + = note: for more information, see issue #71668 = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) - --> $DIR/wrapping-unsafe-block-sugg.rs:42:36 + --> $DIR/wrapping-unsafe-block-sugg.rs:48:36 | LL | macro_rules! unsafe_macro { () => (unsf()) } | ^^^^^^ call to unsafe function @@ -75,16 +81,17 @@ LL | macro_rules! unsafe_macro { () => (unsf()) } LL | unsafe_macro!(); | --------------- in this macro invocation | + = note: for more information, see issue #71668 = note: consult the function's documentation for information on how to avoid undefined behavior note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/wrapping-unsafe-block-sugg.rs:50:1 + --> $DIR/wrapping-unsafe-block-sugg.rs:58:1 | LL | pub unsafe fn unsafe_in_macro() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `unsafe_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) - --> $DIR/wrapping-unsafe-block-sugg.rs:42:36 + --> $DIR/wrapping-unsafe-block-sugg.rs:48:36 | LL | macro_rules! unsafe_macro { () => (unsf()) } | ^^^^^^ call to unsafe function @@ -92,6 +99,7 @@ LL | macro_rules! unsafe_macro { () => (unsf()) } LL | unsafe_macro!(); | --------------- in this macro invocation | + = note: for more information, see issue #71668 = note: consult the function's documentation for information on how to avoid undefined behavior = note: this error originates in the macro `unsafe_macro` (in Nightly builds, run with -Z macro-backtrace for more info) From 1c77f8738fdb33412bbd6ae83c16fe9cc6356815 Mon Sep 17 00:00:00 2001 From: Rowan S-L Date: Fri, 19 Jan 2024 13:37:18 -0500 Subject: [PATCH 17/23] add help message for `exclusive_range_pattern` error --- compiler/rustc_ast_passes/src/feature_gate.rs | 3 ++- .../feature-gates/feature-gate-exclusive-range-pattern.stderr | 1 + .../half-open-range-patterns/range_pat_interactions1.stderr | 2 ++ .../half-open-range-patterns/range_pat_interactions3.stderr | 4 ++++ .../slice_pattern_syntax_problem1.stderr | 2 ++ 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 192e458775a..82236d2e306 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -398,7 +398,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { &self, exclusive_range_pattern, pattern.span, - "exclusive range pattern syntax is experimental" + "exclusive range pattern syntax is experimental", + "use an inclusive range pattern, like N..=M" ); } _ => {} diff --git a/tests/ui/feature-gates/feature-gate-exclusive-range-pattern.stderr b/tests/ui/feature-gates/feature-gate-exclusive-range-pattern.stderr index d05971fb052..5e3d34aa9f3 100644 --- a/tests/ui/feature-gates/feature-gate-exclusive-range-pattern.stderr +++ b/tests/ui/feature-gates/feature-gate-exclusive-range-pattern.stderr @@ -7,6 +7,7 @@ LL | 0 .. 3 => {} = note: see issue #37854 for more information = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M error: aborting due to 1 previous error diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr index e2fab14ffc2..cf3bde9705b 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr @@ -21,6 +21,7 @@ LL | if let n @ 2..3|4 = x { = note: see issue #37854 for more information = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M error[E0658]: exclusive range pattern syntax is experimental --> $DIR/range_pat_interactions1.rs:14:23 @@ -31,6 +32,7 @@ LL | } else if let 2..3 | 4 = x { = note: see issue #37854 for more information = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M error: aborting due to 4 previous errors diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions3.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions3.stderr index fe233496261..51cc22e7d56 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions3.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions3.stderr @@ -17,6 +17,7 @@ LL | 1 | -3..0 => first_or.push(x), = note: see issue #37854 for more information = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M error[E0658]: exclusive range pattern syntax is experimental --> $DIR/range_pat_interactions3.rs:12:18 @@ -27,6 +28,7 @@ LL | y @ (0..5 | 6) => or_two.push(y), = note: see issue #37854 for more information = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M error[E0658]: exclusive range pattern syntax is experimental --> $DIR/range_pat_interactions3.rs:14:17 @@ -37,6 +39,7 @@ LL | y @ 0..const { 5 + 1 } => assert_eq!(y, 5), = note: see issue #37854 for more information = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M error[E0658]: exclusive range pattern syntax is experimental --> $DIR/range_pat_interactions3.rs:18:17 @@ -47,6 +50,7 @@ LL | y @ ..-7 => assert_eq!(y, -8), = note: see issue #37854 for more information = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M error: aborting due to 5 previous errors diff --git a/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem1.stderr b/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem1.stderr index 5edd877bee0..be8f3aa5051 100644 --- a/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem1.stderr +++ b/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem1.stderr @@ -17,6 +17,7 @@ LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs; = note: see issue #37854 for more information = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M error[E0658]: exclusive range pattern syntax is experimental --> $DIR/slice_pattern_syntax_problem1.rs:4:32 @@ -27,6 +28,7 @@ LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs; = note: see issue #37854 for more information = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M error: aborting due to 3 previous errors From 755cfbf236240e2dd0f0e7c3d8c6d175b04b8883 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sun, 3 Dec 2023 15:18:21 +0100 Subject: [PATCH 18/23] core: introduce split_at{,_mut}_checked Introduce split_at_checked and split_at_mut_checked methods to slices types (including str) which are non-panicking versions of split_at and split_at_mut respectively. This is analogous to get method being non-panicking version of indexing. --- library/core/src/lib.rs | 1 + library/core/src/slice/mod.rs | 273 ++++++++++++++++++++++++++++++++-- library/core/src/str/mod.rs | 129 +++++++++++++--- 3 files changed, 375 insertions(+), 28 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 81bf6f28693..840c6254641 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -184,6 +184,7 @@ #![feature(set_ptr_value)] #![feature(slice_ptr_get)] #![feature(slice_split_at_unchecked)] +#![feature(split_at_checked)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] #![feature(str_split_remainder)] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index ee36d93576b..ffc3b4486be 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1842,7 +1842,8 @@ impl [T] { /// /// # Panics /// - /// Panics if `mid > len`. + /// Panics if `mid > len`. For a non-panicking alternative see + /// [`split_at_checked`](slice::split_at_checked). /// /// # Examples /// @@ -1869,14 +1870,15 @@ impl [T] { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_split_at_not_mut", since = "1.71.0")] + #[rustc_allow_const_fn_unstable(split_at_checked)] #[inline] #[track_caller] #[must_use] pub const fn split_at(&self, mid: usize) -> (&[T], &[T]) { - assert!(mid <= self.len()); - // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which - // fulfills the requirements of `split_at_unchecked`. - unsafe { self.split_at_unchecked(mid) } + match self.split_at_checked(mid) { + Some(pair) => pair, + None => panic!("mid > len"), + } } /// Divides one mutable slice into two at an index. @@ -1887,7 +1889,8 @@ impl [T] { /// /// # Panics /// - /// Panics if `mid > len`. + /// Panics if `mid > len`. For a non-panicking alternative see + /// [`split_at_mut_checked`](slice::split_at_mut_checked). /// /// # Examples /// @@ -1906,10 +1909,10 @@ impl [T] { #[must_use] #[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")] pub const fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { - assert!(mid <= self.len()); - // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which - // fulfills the requirements of `from_raw_parts_mut`. - unsafe { self.split_at_mut_unchecked(mid) } + match self.split_at_mut_checked(mid) { + Some(pair) => pair, + None => panic!("mid > len"), + } } /// Divides one slice into two at an index, without doing bounds checking. @@ -2031,6 +2034,256 @@ impl [T] { unsafe { (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) } } + /// Divides one slice into two at an index returning, `None` if slice is too + /// short. + /// + /// The first will contain all indices from `[0, mid)` (excluding + /// the index `mid` itself) and the second will contain all + /// indices from `[mid, len)` (excluding the index `len` itself). + /// + /// Returns `None` if `mid > len`. + /// + /// # Examples + /// + /// ``` + /// #![feature(split_at_checked)] + /// + /// let v = [1, 2, 3, 4, 5, 6]; + /// + /// { + /// let (left, right) = v.split_at_checked(0).unwrap(); + /// assert_eq!(left, []); + /// assert_eq!(right, [1, 2, 3, 4, 5, 6]); + /// } + /// + /// { + /// let (left, right) = v.split_at_checked(2).unwrap(); + /// assert_eq!(left, [1, 2]); + /// assert_eq!(right, [3, 4, 5, 6]); + /// } + /// + /// { + /// let (left, right) = v.split_at_checked(6).unwrap(); + /// assert_eq!(left, [1, 2, 3, 4, 5, 6]); + /// assert_eq!(right, []); + /// } + /// + /// assert_eq!(None, v.split_at_checked(7)); + /// ``` + #[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")] + #[rustc_const_unstable(feature = "split_at_checked", issue = "119128")] + #[inline] + #[track_caller] + #[must_use] + pub const fn split_at_checked(&self, mid: usize) -> Option<(&[T], &[T])> { + if mid <= self.len() { + // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which + // fulfills the requirements of `split_at_unchecked`. + Some(unsafe { self.split_at_unchecked(mid) }) + } else { + None + } + } + + /// Divides one mutable slice into two at an index, returning `None` if + /// slice is too short. + /// + /// The first will contain all indices from `[0, mid)` (excluding + /// the index `mid` itself) and the second will contain all + /// indices from `[mid, len)` (excluding the index `len` itself). + /// + /// Returns `None` if `mid > len`. + /// + /// # Examples + /// + /// ``` + /// #![feature(split_at_checked)] + /// + /// let mut v = [1, 0, 3, 0, 5, 6]; + /// + /// if let Some((left, right)) = v.split_at_mut_checked(2) { + /// assert_eq!(left, [1, 0]); + /// assert_eq!(right, [3, 0, 5, 6]); + /// left[1] = 2; + /// right[1] = 4; + /// } + /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); + /// + /// assert_eq!(None, v.split_at_mut_checked(7)); + /// ``` + #[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")] + #[rustc_const_unstable(feature = "split_at_checked", issue = "119128")] + #[inline] + #[track_caller] + #[must_use] + pub const fn split_at_mut_checked(&mut self, mid: usize) -> Option<(&mut [T], &mut [T])> { + if mid <= self.len() { + // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which + // fulfills the requirements of `split_at_unchecked`. + Some(unsafe { self.split_at_mut_unchecked(mid) }) + } else { + None + } + } + + /// Divides one slice into an array and a remainder slice at an index. + /// + /// The array will contain all indices from `[0, N)` (excluding + /// the index `N` itself) and the slice will contain all + /// indices from `[N, len)` (excluding the index `len` itself). + /// + /// # Panics + /// + /// Panics if `N > len`. + /// + /// # Examples + /// + /// ``` + /// #![feature(split_array)] + /// + /// let v = &[1, 2, 3, 4, 5, 6][..]; + /// + /// { + /// let (left, right) = v.split_array_ref::<0>(); + /// assert_eq!(left, &[]); + /// assert_eq!(right, [1, 2, 3, 4, 5, 6]); + /// } + /// + /// { + /// let (left, right) = v.split_array_ref::<2>(); + /// assert_eq!(left, &[1, 2]); + /// assert_eq!(right, [3, 4, 5, 6]); + /// } + /// + /// { + /// let (left, right) = v.split_array_ref::<6>(); + /// assert_eq!(left, &[1, 2, 3, 4, 5, 6]); + /// assert_eq!(right, []); + /// } + /// ``` + #[unstable(feature = "split_array", reason = "new API", issue = "90091")] + #[inline] + #[track_caller] + #[must_use] + pub fn split_array_ref(&self) -> (&[T; N], &[T]) { + let (a, b) = self.split_at(N); + // SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at) + unsafe { (&*(a.as_ptr() as *const [T; N]), b) } + } + + /// Divides one mutable slice into an array and a remainder slice at an index. + /// + /// The array will contain all indices from `[0, N)` (excluding + /// the index `N` itself) and the slice will contain all + /// indices from `[N, len)` (excluding the index `len` itself). + /// + /// # Panics + /// + /// Panics if `N > len`. + /// + /// # Examples + /// + /// ``` + /// #![feature(split_array)] + /// + /// let mut v = &mut [1, 0, 3, 0, 5, 6][..]; + /// let (left, right) = v.split_array_mut::<2>(); + /// assert_eq!(left, &mut [1, 0]); + /// assert_eq!(right, [3, 0, 5, 6]); + /// left[1] = 2; + /// right[1] = 4; + /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); + /// ``` + #[unstable(feature = "split_array", reason = "new API", issue = "90091")] + #[inline] + #[track_caller] + #[must_use] + pub fn split_array_mut(&mut self) -> (&mut [T; N], &mut [T]) { + let (a, b) = self.split_at_mut(N); + // SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at_mut) + unsafe { (&mut *(a.as_mut_ptr() as *mut [T; N]), b) } + } + + /// Divides one slice into an array and a remainder slice at an index from + /// the end. + /// + /// The slice will contain all indices from `[0, len - N)` (excluding + /// the index `len - N` itself) and the array will contain all + /// indices from `[len - N, len)` (excluding the index `len` itself). + /// + /// # Panics + /// + /// Panics if `N > len`. + /// + /// # Examples + /// + /// ``` + /// #![feature(split_array)] + /// + /// let v = &[1, 2, 3, 4, 5, 6][..]; + /// + /// { + /// let (left, right) = v.rsplit_array_ref::<0>(); + /// assert_eq!(left, [1, 2, 3, 4, 5, 6]); + /// assert_eq!(right, &[]); + /// } + /// + /// { + /// let (left, right) = v.rsplit_array_ref::<2>(); + /// assert_eq!(left, [1, 2, 3, 4]); + /// assert_eq!(right, &[5, 6]); + /// } + /// + /// { + /// let (left, right) = v.rsplit_array_ref::<6>(); + /// assert_eq!(left, []); + /// assert_eq!(right, &[1, 2, 3, 4, 5, 6]); + /// } + /// ``` + #[unstable(feature = "split_array", reason = "new API", issue = "90091")] + #[inline] + #[must_use] + pub fn rsplit_array_ref(&self) -> (&[T], &[T; N]) { + assert!(N <= self.len()); + let (a, b) = self.split_at(self.len() - N); + // SAFETY: b points to [T; N]? Yes it's [T] of length N (checked by split_at) + unsafe { (a, &*(b.as_ptr() as *const [T; N])) } + } + + /// Divides one mutable slice into an array and a remainder slice at an + /// index from the end. + /// + /// The slice will contain all indices from `[0, len - N)` (excluding + /// the index `N` itself) and the array will contain all + /// indices from `[len - N, len)` (excluding the index `len` itself). + /// + /// # Panics + /// + /// Panics if `N > len`. + /// + /// # Examples + /// + /// ``` + /// #![feature(split_array)] + /// + /// let mut v = &mut [1, 0, 3, 0, 5, 6][..]; + /// let (left, right) = v.rsplit_array_mut::<4>(); + /// assert_eq!(left, [1, 0]); + /// assert_eq!(right, &mut [3, 0, 5, 6]); + /// left[1] = 2; + /// right[1] = 4; + /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); + /// ``` + #[unstable(feature = "split_array", reason = "new API", issue = "90091")] + #[inline] + #[must_use] + pub fn rsplit_array_mut(&mut self) -> (&mut [T], &mut [T; N]) { + assert!(N <= self.len()); + let (a, b) = self.split_at_mut(self.len() - N); + // SAFETY: b points to [T; N]? Yes it's [T] of length N (checked by split_at_mut) + unsafe { (a, &mut *(b.as_mut_ptr() as *mut [T; N])) } + } + /// Returns an iterator over subslices separated by elements that match /// `pred`. The matched element is not contained in the subslices. /// diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index a22c46edce2..ebda6e994a7 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -641,8 +641,9 @@ impl str { /// /// # Panics /// - /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is - /// past the end of the last code point of the string slice. + /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is past + /// the end of the last code point of the string slice. For non-panicking + /// alternative see [`split_at_checked`](str::split_at_checked). /// /// # Examples /// @@ -658,12 +659,9 @@ impl str { #[must_use] #[stable(feature = "str_split_at", since = "1.4.0")] pub fn split_at(&self, mid: usize) -> (&str, &str) { - // is_char_boundary checks that the index is in [0, .len()] - if self.is_char_boundary(mid) { - // SAFETY: just checked that `mid` is on a char boundary. - unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) } - } else { - slice_error_fail(self, 0, mid) + match self.split_at_checked(mid) { + None => slice_error_fail(self, 0, mid), + Some(pair) => pair, } } @@ -681,8 +679,9 @@ impl str { /// /// # Panics /// - /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is - /// past the end of the last code point of the string slice. + /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is past + /// the end of the last code point of the string slice. For non-panicking + /// alternative see [`split_at_mut_checked`](str::split_at_mut_checked). /// /// # Examples /// @@ -702,20 +701,114 @@ impl str { pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { - let len = self.len(); - let ptr = self.as_mut_ptr(); // SAFETY: just checked that `mid` is on a char boundary. - unsafe { - ( - from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)), - from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr.add(mid), len - mid)), - ) - } + unsafe { self.split_at_mut_unchecked(mid) } } else { slice_error_fail(self, 0, mid) } } + /// Divide one string slice into two at an index. + /// + /// The argument, `mid`, should be a valid byte offset from the start of the + /// string. It must also be on the boundary of a UTF-8 code point. The + /// method returns `None` if that’s not the case. + /// + /// The two slices returned go from the start of the string slice to `mid`, + /// and from `mid` to the end of the string slice. + /// + /// To get mutable string slices instead, see the [`split_at_mut_checked`] + /// method. + /// + /// [`split_at_mut_checked`]: str::split_at_mut_checked + /// + /// # Examples + /// + /// ``` + /// #![feature(split_at_checked)] + /// + /// let s = "Per Martin-Löf"; + /// + /// let (first, last) = s.split_at_checked(3).unwrap(); + /// assert_eq!("Per", first); + /// assert_eq!(" Martin-Löf", last); + /// + /// assert_eq!(None, s.split_at_checked(13)); // Inside “ö” + /// assert_eq!(None, s.split_at_checked(16)); // Beyond the string length + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")] + pub fn split_at_checked(&self, mid: usize) -> Option<(&str, &str)> { + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(mid) { + // SAFETY: just checked that `mid` is on a char boundary. + Some(unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) }) + } else { + None + } + } + + /// Divide one mutable string slice into two at an index. + /// + /// The argument, `mid`, should be a valid byte offset from the start of the + /// string. It must also be on the boundary of a UTF-8 code point. The + /// method returns `None` if that’s not the case. + /// + /// The two slices returned go from the start of the string slice to `mid`, + /// and from `mid` to the end of the string slice. + /// + /// To get immutable string slices instead, see the [`split_at_checked`] method. + /// + /// [`split_at_checked`]: str::split_at_checked + /// + /// # Examples + /// + /// ``` + /// #![feature(split_at_checked)] + /// + /// let mut s = "Per Martin-Löf".to_string(); + /// if let Some((first, last)) = s.split_at_mut_checked(3) { + /// first.make_ascii_uppercase(); + /// assert_eq!("PER", first); + /// assert_eq!(" Martin-Löf", last); + /// } + /// assert_eq!("PER Martin-Löf", s); + /// + /// assert_eq!(None, s.split_at_mut_checked(13)); // Inside “ö” + /// assert_eq!(None, s.split_at_mut_checked(16)); // Beyond the string length + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")] + pub fn split_at_mut_checked(&mut self, mid: usize) -> Option<(&mut str, &mut str)> { + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(mid) { + // SAFETY: just checked that `mid` is on a char boundary. + Some(unsafe { self.split_at_mut_unchecked(mid) }) + } else { + None + } + } + + /// Divide one string slice into two at an index. + /// + /// # Safety + /// + /// The caller must ensure that `mid` is a valid byte offset from the start + /// of the string and falls on the boundary of a UTF-8 code point. + unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut str, &mut str) { + let len = self.len(); + let ptr = self.as_mut_ptr(); + // SAFETY: caller guarantees `mid` is on a char boundary. + unsafe { + ( + from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)), + from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr.add(mid), len - mid)), + ) + } + } + /// Returns an iterator over the [`char`]s of a string slice. /// /// As a string slice consists of valid UTF-8, we can iterate through a From 9c3091e9cf9e7bb68afe3e06fd4e8c3986943285 Mon Sep 17 00:00:00 2001 From: bohan Date: Thu, 28 Dec 2023 15:18:49 +0800 Subject: [PATCH 19/23] exclude unexported macro bindings from extern crate --- compiler/rustc_lint_defs/src/builtin.rs | 47 +++++++++++++++++++ .../rustc_resolve/src/build_reduced_graph.rs | 24 ++++++++-- compiler/rustc_resolve/src/check_unused.rs | 4 +- compiler/rustc_resolve/src/diagnostics.rs | 8 ++-- compiler/rustc_resolve/src/imports.rs | 14 ++++-- compiler/rustc_resolve/src/lib.rs | 5 ++ .../extern/auxiliary/issue-80074-macro-2.rs | 3 ++ .../ui/extern/auxiliary/issue-80074-macro.rs | 2 + tests/ui/extern/issue-80074.rs | 12 ++++- tests/ui/extern/issue-80074.stderr | 31 ++++++++++++ .../imports/auxiliary/issue-119369-extern.rs | 2 + tests/ui/imports/issue-119369.rs | 14 ++++++ 12 files changed, 149 insertions(+), 17 deletions(-) create mode 100644 tests/ui/extern/auxiliary/issue-80074-macro-2.rs create mode 100644 tests/ui/extern/issue-80074.stderr create mode 100644 tests/ui/imports/auxiliary/issue-119369-extern.rs create mode 100644 tests/ui/imports/issue-119369.rs diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index e917e7cb02b..01c9e118a11 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4604,3 +4604,50 @@ declare_lint! { reference: "issue #X ", }; } + +declare_lint! { + /// The `private_macro_use` lint detects private macros that are imported + /// with `#[macro_use]`. + /// + /// ### Example + /// + /// ```rust,ignore (needs extern crate) + /// // extern_macro.rs + /// macro_rules! foo_ { () => {}; } + /// use foo_ as foo; + /// + /// // code.rs + /// + /// #![deny(private_macro_use)] + /// + /// #[macro_use] + /// extern crate extern_macro; + /// + /// fn main() { + /// foo!(); + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: cannot find macro `foo` in this scope + /// ``` + /// + /// ### Explanation + /// + /// This lint arises from overlooking visibility checks for macros + /// in an external crate. + /// + /// This is a [future-incompatible] lint to transition this to a + /// hard error in the future. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub PRIVATE_MACRO_USE, + Warn, + "detects certain macro bindings that should not be re-exported", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reference: "issue #120192 ", + }; +} diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index a4e2f9e3ff8..b855ec8f920 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1029,9 +1029,9 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { } } - let macro_use_import = |this: &Self, span| { + let macro_use_import = |this: &Self, span, warn_private| { this.r.arenas.alloc_import(ImportData { - kind: ImportKind::MacroUse, + kind: ImportKind::MacroUse { warn_private }, root_id: item.id, parent_scope: this.parent_scope, imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))), @@ -1048,11 +1048,25 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { let allow_shadowing = self.parent_scope.expansion == LocalExpnId::ROOT; if let Some(span) = import_all { - let import = macro_use_import(self, span); + let import = macro_use_import(self, span, false); self.r.potentially_unused_imports.push(import); module.for_each_child(self, |this, ident, ns, binding| { if ns == MacroNS { - let imported_binding = this.r.import(binding, import); + let imported_binding = + if this.r.is_accessible_from(binding.vis, this.parent_scope.module) { + this.r.import(binding, import) + } else if !this.r.is_builtin_macro(binding.res()) + && !this.r.macro_use_prelude.contains_key(&ident.name) + { + // - `!r.is_builtin_macro(res)` excluding the built-in macros such as `Debug` or `Hash`. + // - `!r.macro_use_prelude.contains_key(name)` excluding macros defined in other extern + // crates such as `std`. + // FIXME: This branch should eventually be removed. + let import = macro_use_import(this, span, true); + this.r.import(binding, import) + } else { + return; + }; this.add_macro_use_binding(ident.name, imported_binding, span, allow_shadowing); } }); @@ -1065,7 +1079,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { &self.parent_scope, ); if let Ok(binding) = result { - let import = macro_use_import(self, ident.span); + let import = macro_use_import(self, ident.span, false); self.r.potentially_unused_imports.push(import); let imported_binding = self.r.import(binding, import); self.add_macro_use_binding( diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 0e43a35ce73..fc72d76c3a7 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -290,7 +290,7 @@ impl Resolver<'_, '_> { || import.expect_vis().is_public() || import.span.is_dummy() => { - if let ImportKind::MacroUse = import.kind { + if let ImportKind::MacroUse { .. } = import.kind { if !import.span.is_dummy() { self.lint_buffer.buffer_lint( MACRO_USE_EXTERN_CRATE, @@ -315,7 +315,7 @@ impl Resolver<'_, '_> { maybe_unused_extern_crates.insert(id, import.span); } } - ImportKind::MacroUse => { + ImportKind::MacroUse { .. } => { let msg = "unused `#[macro_use]` import"; self.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.root_id, import.span, msg); } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 4a0c522b6ec..66dfee6c062 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -285,7 +285,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { use NameBindingKind::Import; let can_suggest = |binding: NameBinding<'_>, import: self::Import<'_>| { !binding.span.is_dummy() - && !matches!(import.kind, ImportKind::MacroUse | ImportKind::MacroExport) + && !matches!(import.kind, ImportKind::MacroUse { .. } | ImportKind::MacroExport) }; let import = match (&new_binding.kind, &old_binding.kind) { // If there are two imports where one or both have attributes then prefer removing the @@ -1819,9 +1819,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { next_ident = source; Some(binding) } - ImportKind::Glob { .. } | ImportKind::MacroUse | ImportKind::MacroExport => { - Some(binding) - } + ImportKind::Glob { .. } + | ImportKind::MacroUse { .. } + | ImportKind::MacroExport => Some(binding), ImportKind::ExternCrate { .. } => None, }, _ => None, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index f846dbec2c6..ad60caed354 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -80,7 +80,11 @@ pub(crate) enum ImportKind<'a> { target: Ident, id: NodeId, }, - MacroUse, + MacroUse { + /// A field has been added indicating whether it should be reported as a lint, + /// addressing issue#119301. + warn_private: bool, + }, MacroExport, } @@ -127,7 +131,7 @@ impl<'a> std::fmt::Debug for ImportKind<'a> { .field("target", target) .field("id", id) .finish(), - MacroUse => f.debug_struct("MacroUse").finish(), + MacroUse { .. } => f.debug_struct("MacroUse").finish(), MacroExport => f.debug_struct("MacroExport").finish(), } } @@ -197,7 +201,7 @@ impl<'a> ImportData<'a> { ImportKind::Single { id, .. } | ImportKind::Glob { id, .. } | ImportKind::ExternCrate { id, .. } => Some(id), - ImportKind::MacroUse | ImportKind::MacroExport => None, + ImportKind::MacroUse { .. } | ImportKind::MacroExport => None, } } @@ -207,7 +211,7 @@ impl<'a> ImportData<'a> { ImportKind::Single { id, .. } => Reexport::Single(to_def_id(id)), ImportKind::Glob { id, .. } => Reexport::Glob(to_def_id(id)), ImportKind::ExternCrate { id, .. } => Reexport::ExternCrate(to_def_id(id)), - ImportKind::MacroUse => Reexport::MacroUse, + ImportKind::MacroUse { .. } => Reexport::MacroUse, ImportKind::MacroExport => Reexport::MacroExport, } } @@ -1482,7 +1486,7 @@ fn import_kind_to_string(import_kind: &ImportKind<'_>) -> String { ImportKind::Single { source, .. } => source.to_string(), ImportKind::Glob { .. } => "*".to_string(), ImportKind::ExternCrate { .. } => "".to_string(), - ImportKind::MacroUse => "#[macro_use]".to_string(), + ImportKind::MacroUse { .. } => "#[macro_use]".to_string(), ImportKind::MacroExport => "#[macro_export]".to_string(), } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 341c566d97f..7b4f3383c3f 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -55,6 +55,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::{self, MainDefinition, RegisteredTools, TyCtxt}; use rustc_middle::ty::{ResolverGlobalCtxt, ResolverOutputs}; use rustc_query_system::ich::StableHashingContext; +use rustc_session::lint::builtin::PRIVATE_MACRO_USE; use rustc_session::lint::LintBuffer; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -1799,6 +1800,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } if let NameBindingKind::Import { import, binding, ref used } = used_binding.kind { + if let ImportKind::MacroUse { warn_private: true } = import.kind { + let msg = format!("macro `{ident}` is private"); + self.lint_buffer().buffer_lint(PRIVATE_MACRO_USE, import.root_id, ident.span, msg); + } // Avoid marking `extern crate` items that refer to a name from extern prelude, // but not introduce it, as used if they are accessed from lexical scope. if is_lexical_scope { diff --git a/tests/ui/extern/auxiliary/issue-80074-macro-2.rs b/tests/ui/extern/auxiliary/issue-80074-macro-2.rs new file mode 100644 index 00000000000..bc87a2b5434 --- /dev/null +++ b/tests/ui/extern/auxiliary/issue-80074-macro-2.rs @@ -0,0 +1,3 @@ +// edition:2018 + +macro_rules! m { () => {}; } diff --git a/tests/ui/extern/auxiliary/issue-80074-macro.rs b/tests/ui/extern/auxiliary/issue-80074-macro.rs index 30e0f19ab8d..3e912d97715 100644 --- a/tests/ui/extern/auxiliary/issue-80074-macro.rs +++ b/tests/ui/extern/auxiliary/issue-80074-macro.rs @@ -2,3 +2,5 @@ macro_rules! foo_ { () => {}; } use foo_ as foo; + +macro_rules! bar { () => {}; } diff --git a/tests/ui/extern/issue-80074.rs b/tests/ui/extern/issue-80074.rs index f83027d4abf..6e4f176de82 100644 --- a/tests/ui/extern/issue-80074.rs +++ b/tests/ui/extern/issue-80074.rs @@ -1,10 +1,20 @@ // edition:2018 -// build-pass // aux-crate:issue_80074=issue-80074-macro.rs +// aux-crate:issue_80074_2=issue-80074-macro-2.rs #[macro_use] extern crate issue_80074; +#[macro_use(m)] +extern crate issue_80074_2; +//~^^ ERROR: imported macro not found + fn main() { foo!(); + //~^ WARN: macro `foo` is private + //~| WARN: it will become a hard error in a future release! + bar!(); + //~^ ERROR: cannot find macro `bar` in this scope + m!(); + //~^ ERROR: cannot find macro `m` in this scope } diff --git a/tests/ui/extern/issue-80074.stderr b/tests/ui/extern/issue-80074.stderr new file mode 100644 index 00000000000..b30b761593e --- /dev/null +++ b/tests/ui/extern/issue-80074.stderr @@ -0,0 +1,31 @@ +error[E0469]: imported macro not found + --> $DIR/issue-80074.rs:8:13 + | +LL | #[macro_use(m)] + | ^ + +error: cannot find macro `bar` in this scope + --> $DIR/issue-80074.rs:16:5 + | +LL | bar!(); + | ^^^ + +error: cannot find macro `m` in this scope + --> $DIR/issue-80074.rs:18:5 + | +LL | m!(); + | ^ + +warning: macro `foo` is private + --> $DIR/issue-80074.rs:13:5 + | +LL | foo!(); + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #120192 + = note: `#[warn(private_macro_use)]` on by default + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0469`. diff --git a/tests/ui/imports/auxiliary/issue-119369-extern.rs b/tests/ui/imports/auxiliary/issue-119369-extern.rs new file mode 100644 index 00000000000..278cbfa1e3e --- /dev/null +++ b/tests/ui/imports/auxiliary/issue-119369-extern.rs @@ -0,0 +1,2 @@ +use std::vec; +use std::hash::Hash; diff --git a/tests/ui/imports/issue-119369.rs b/tests/ui/imports/issue-119369.rs new file mode 100644 index 00000000000..0b4dc3f4654 --- /dev/null +++ b/tests/ui/imports/issue-119369.rs @@ -0,0 +1,14 @@ +// check-pass +// aux-build: issue-119369-extern.rs + +// https://github.com/rust-lang/rust/pull/119369#issuecomment-1874905662 + +#[macro_use] +extern crate issue_119369_extern; + +#[derive(Hash)] +struct A; + +fn main() { + let _: Vec = vec![]; +} From 1afd216f760601fb13a9982d283ef8e2224b7527 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Sun, 21 Jan 2024 12:06:42 +0300 Subject: [PATCH 20/23] use u64 to represent file size This increases the maximum supported file size (previously limited to 4GB) and avoids potential issues with u32 to u64 conversions, which are no longer needed. Signed-off-by: onur-ozkan --- src/tools/remote-test-client/src/main.rs | 11 +++++------ src/tools/remote-test-server/src/main.rs | 16 ++++++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/tools/remote-test-client/src/main.rs b/src/tools/remote-test-client/src/main.rs index bcda930d093..590c735596e 100644 --- a/src/tools/remote-test-client/src/main.rs +++ b/src/tools/remote-test-client/src/main.rs @@ -300,7 +300,7 @@ fn run(support_lib_count: usize, exe: String, all_args: Vec) { // Ok now it's time to read all the output. We're receiving "frames" // representing stdout/stderr, so we decode all that here. - let mut header = [0; 5]; + let mut header = [0; 9]; let mut stderr_done = false; let mut stdout_done = false; let mut client = t!(client.into_inner()); @@ -308,10 +308,8 @@ fn run(support_lib_count: usize, exe: String, all_args: Vec) { let mut stderr = io::stderr(); while !stdout_done || !stderr_done { t!(client.read_exact(&mut header)); - let amt = ((header[1] as u64) << 24) - | ((header[2] as u64) << 16) - | ((header[3] as u64) << 8) - | ((header[4] as u64) << 0); + let amt = u64::from_be_bytes(header[1..9].try_into().unwrap()); + if header[0] == 0 { if amt == 0 { stdout_done = true; @@ -349,7 +347,8 @@ fn send(path: &Path, dst: &mut dyn Write) { t!(dst.write_all(&[0])); let mut file = t!(File::open(&path)); let amt = t!(file.metadata()).len(); - t!(dst.write_all(&[(amt >> 24) as u8, (amt >> 16) as u8, (amt >> 8) as u8, (amt >> 0) as u8,])); + + t!(dst.write_all(&amt.to_be_bytes())); t!(io::copy(&mut file, dst)); } diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index 3d61a067559..68d7aa56c43 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -347,7 +347,7 @@ fn recv(dir: &Path, io: &mut B) -> PathBuf { // the filesystem limits. let len = cmp::min(filename.len() - 1, 50); let dst = dir.join(t!(str::from_utf8(&filename[..len]))); - let amt = read_u32(io) as u64; + let amt = read_u64(io); t!(io::copy(&mut io.take(amt), &mut t!(File::create(&dst)))); set_permissions(&dst); dst @@ -365,7 +365,7 @@ fn my_copy(src: &mut dyn Read, which: u8, dst: &Mutex) { loop { let n = t!(src.read(&mut b)); let mut dst = dst.lock().unwrap(); - t!(dst.write_all(&create_header(which, n as u32))); + t!(dst.write_all(&create_header(which, n as u64))); if n > 0 { t!(dst.write_all(&b[..n])); } else { @@ -377,7 +377,7 @@ fn my_copy(src: &mut dyn Read, which: u8, dst: &Mutex) { fn batch_copy(buf: &[u8], which: u8, dst: &Mutex) { let n = buf.len(); let mut dst = dst.lock().unwrap(); - t!(dst.write_all(&create_header(which, n as u32))); + t!(dst.write_all(&create_header(which, n as u64))); if n > 0 { t!(dst.write_all(buf)); // Marking buf finished @@ -385,13 +385,13 @@ fn batch_copy(buf: &[u8], which: u8, dst: &Mutex) { } } -const fn create_header(which: u8, n: u32) -> [u8; 5] { +const fn create_header(which: u8, n: u64) -> [u8; 9] { let bytes = n.to_be_bytes(); - [which, bytes[0], bytes[1], bytes[2], bytes[3]] + [which, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]] } -fn read_u32(r: &mut dyn Read) -> u32 { - let mut len = [0; 4]; +fn read_u64(r: &mut dyn Read) -> u64 { + let mut len = [0; 8]; t!(r.read_exact(&mut len)); - u32::from_be_bytes(len) + u64::from_be_bytes(len) } From 50cbbef86a1a8df32642864c522de2cd730c1ae7 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sun, 21 Jan 2024 20:12:00 +0100 Subject: [PATCH 21/23] review --- library/core/src/slice/mod.rs | 194 ++++------------------------------ library/core/src/str/mod.rs | 4 +- 2 files changed, 20 insertions(+), 178 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index ffc3b4486be..eb5a672b78d 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2034,37 +2034,38 @@ impl [T] { unsafe { (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) } } - /// Divides one slice into two at an index returning, `None` if slice is too - /// short. + /// Divides one slice into two at an index, returning `None` if the slice is + /// too short. /// - /// The first will contain all indices from `[0, mid)` (excluding - /// the index `mid` itself) and the second will contain all - /// indices from `[mid, len)` (excluding the index `len` itself). + /// If `mid ≤ len` returns a pair of slices where the first will contain all + /// indices from `[0, mid)` (excluding the index `mid` itself) and the + /// second will contain all indices from `[mid, len)` (excluding the index + /// `len` itself). /// - /// Returns `None` if `mid > len`. + /// Otherwise, if `mid > len`, returns `None`. /// /// # Examples /// /// ``` /// #![feature(split_at_checked)] /// - /// let v = [1, 2, 3, 4, 5, 6]; + /// let v = [1, -2, 3, -4, 5, -6]; /// /// { /// let (left, right) = v.split_at_checked(0).unwrap(); /// assert_eq!(left, []); - /// assert_eq!(right, [1, 2, 3, 4, 5, 6]); + /// assert_eq!(right, [1, -2, 3, -4, 5, -6]); /// } /// /// { /// let (left, right) = v.split_at_checked(2).unwrap(); - /// assert_eq!(left, [1, 2]); - /// assert_eq!(right, [3, 4, 5, 6]); + /// assert_eq!(left, [1, -2]); + /// assert_eq!(right, [3, -4, 5, -6]); /// } /// /// { /// let (left, right) = v.split_at_checked(6).unwrap(); - /// assert_eq!(left, [1, 2, 3, 4, 5, 6]); + /// assert_eq!(left, [1, -2, 3, -4, 5, -6]); /// assert_eq!(right, []); /// } /// @@ -2073,7 +2074,6 @@ impl [T] { #[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")] #[rustc_const_unstable(feature = "split_at_checked", issue = "119128")] #[inline] - #[track_caller] #[must_use] pub const fn split_at_checked(&self, mid: usize) -> Option<(&[T], &[T])> { if mid <= self.len() { @@ -2085,14 +2085,15 @@ impl [T] { } } - /// Divides one mutable slice into two at an index, returning `None` if + /// Divides one mutable slice into two at an index, returning `None` if the /// slice is too short. /// - /// The first will contain all indices from `[0, mid)` (excluding - /// the index `mid` itself) and the second will contain all - /// indices from `[mid, len)` (excluding the index `len` itself). + /// If `mid ≤ len` returns a pair of slices where the first will contain all + /// indices from `[0, mid)` (excluding the index `mid` itself) and the + /// second will contain all indices from `[mid, len)` (excluding the index + /// `len` itself). /// - /// Returns `None` if `mid > len`. + /// Otherwise, if `mid > len`, returns `None`. /// /// # Examples /// @@ -2114,7 +2115,6 @@ impl [T] { #[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")] #[rustc_const_unstable(feature = "split_at_checked", issue = "119128")] #[inline] - #[track_caller] #[must_use] pub const fn split_at_mut_checked(&mut self, mid: usize) -> Option<(&mut [T], &mut [T])> { if mid <= self.len() { @@ -2126,164 +2126,6 @@ impl [T] { } } - /// Divides one slice into an array and a remainder slice at an index. - /// - /// The array will contain all indices from `[0, N)` (excluding - /// the index `N` itself) and the slice will contain all - /// indices from `[N, len)` (excluding the index `len` itself). - /// - /// # Panics - /// - /// Panics if `N > len`. - /// - /// # Examples - /// - /// ``` - /// #![feature(split_array)] - /// - /// let v = &[1, 2, 3, 4, 5, 6][..]; - /// - /// { - /// let (left, right) = v.split_array_ref::<0>(); - /// assert_eq!(left, &[]); - /// assert_eq!(right, [1, 2, 3, 4, 5, 6]); - /// } - /// - /// { - /// let (left, right) = v.split_array_ref::<2>(); - /// assert_eq!(left, &[1, 2]); - /// assert_eq!(right, [3, 4, 5, 6]); - /// } - /// - /// { - /// let (left, right) = v.split_array_ref::<6>(); - /// assert_eq!(left, &[1, 2, 3, 4, 5, 6]); - /// assert_eq!(right, []); - /// } - /// ``` - #[unstable(feature = "split_array", reason = "new API", issue = "90091")] - #[inline] - #[track_caller] - #[must_use] - pub fn split_array_ref(&self) -> (&[T; N], &[T]) { - let (a, b) = self.split_at(N); - // SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at) - unsafe { (&*(a.as_ptr() as *const [T; N]), b) } - } - - /// Divides one mutable slice into an array and a remainder slice at an index. - /// - /// The array will contain all indices from `[0, N)` (excluding - /// the index `N` itself) and the slice will contain all - /// indices from `[N, len)` (excluding the index `len` itself). - /// - /// # Panics - /// - /// Panics if `N > len`. - /// - /// # Examples - /// - /// ``` - /// #![feature(split_array)] - /// - /// let mut v = &mut [1, 0, 3, 0, 5, 6][..]; - /// let (left, right) = v.split_array_mut::<2>(); - /// assert_eq!(left, &mut [1, 0]); - /// assert_eq!(right, [3, 0, 5, 6]); - /// left[1] = 2; - /// right[1] = 4; - /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); - /// ``` - #[unstable(feature = "split_array", reason = "new API", issue = "90091")] - #[inline] - #[track_caller] - #[must_use] - pub fn split_array_mut(&mut self) -> (&mut [T; N], &mut [T]) { - let (a, b) = self.split_at_mut(N); - // SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at_mut) - unsafe { (&mut *(a.as_mut_ptr() as *mut [T; N]), b) } - } - - /// Divides one slice into an array and a remainder slice at an index from - /// the end. - /// - /// The slice will contain all indices from `[0, len - N)` (excluding - /// the index `len - N` itself) and the array will contain all - /// indices from `[len - N, len)` (excluding the index `len` itself). - /// - /// # Panics - /// - /// Panics if `N > len`. - /// - /// # Examples - /// - /// ``` - /// #![feature(split_array)] - /// - /// let v = &[1, 2, 3, 4, 5, 6][..]; - /// - /// { - /// let (left, right) = v.rsplit_array_ref::<0>(); - /// assert_eq!(left, [1, 2, 3, 4, 5, 6]); - /// assert_eq!(right, &[]); - /// } - /// - /// { - /// let (left, right) = v.rsplit_array_ref::<2>(); - /// assert_eq!(left, [1, 2, 3, 4]); - /// assert_eq!(right, &[5, 6]); - /// } - /// - /// { - /// let (left, right) = v.rsplit_array_ref::<6>(); - /// assert_eq!(left, []); - /// assert_eq!(right, &[1, 2, 3, 4, 5, 6]); - /// } - /// ``` - #[unstable(feature = "split_array", reason = "new API", issue = "90091")] - #[inline] - #[must_use] - pub fn rsplit_array_ref(&self) -> (&[T], &[T; N]) { - assert!(N <= self.len()); - let (a, b) = self.split_at(self.len() - N); - // SAFETY: b points to [T; N]? Yes it's [T] of length N (checked by split_at) - unsafe { (a, &*(b.as_ptr() as *const [T; N])) } - } - - /// Divides one mutable slice into an array and a remainder slice at an - /// index from the end. - /// - /// The slice will contain all indices from `[0, len - N)` (excluding - /// the index `N` itself) and the array will contain all - /// indices from `[len - N, len)` (excluding the index `len` itself). - /// - /// # Panics - /// - /// Panics if `N > len`. - /// - /// # Examples - /// - /// ``` - /// #![feature(split_array)] - /// - /// let mut v = &mut [1, 0, 3, 0, 5, 6][..]; - /// let (left, right) = v.rsplit_array_mut::<4>(); - /// assert_eq!(left, [1, 0]); - /// assert_eq!(right, &mut [3, 0, 5, 6]); - /// left[1] = 2; - /// right[1] = 4; - /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); - /// ``` - #[unstable(feature = "split_array", reason = "new API", issue = "90091")] - #[inline] - #[must_use] - pub fn rsplit_array_mut(&mut self) -> (&mut [T], &mut [T; N]) { - assert!(N <= self.len()); - let (a, b) = self.split_at_mut(self.len() - N); - // SAFETY: b points to [T; N]? Yes it's [T] of length N (checked by split_at_mut) - unsafe { (a, &mut *(b.as_mut_ptr() as *mut [T; N])) } - } - /// Returns an iterator over subslices separated by elements that match /// `pred`. The matched element is not contained in the subslices. /// diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index ebda6e994a7..80c5fe0de8d 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -642,7 +642,7 @@ impl str { /// # Panics /// /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is past - /// the end of the last code point of the string slice. For non-panicking + /// the end of the last code point of the string slice. For a non-panicking /// alternative see [`split_at_checked`](str::split_at_checked). /// /// # Examples @@ -680,7 +680,7 @@ impl str { /// # Panics /// /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is past - /// the end of the last code point of the string slice. For non-panicking + /// the end of the last code point of the string slice. For a non-panicking /// alternative see [`split_at_mut_checked`](str::split_at_mut_checked). /// /// # Examples From 802d16ce3a530786757670e954693b125f3fa957 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 21 Jan 2024 22:26:59 +0000 Subject: [PATCH 22/23] Don't actually make bound ty/const for RTN --- .../rustc_hir_analysis/src/astconv/bounds.rs | 34 +++++-------------- .../issue-120208-higher-ranked-const.rs | 17 ++++++++++ .../issue-120208-higher-ranked-const.stderr | 20 +++++++++++ 3 files changed, 46 insertions(+), 25 deletions(-) create mode 100644 tests/ui/associated-type-bounds/return-type-notation/issue-120208-higher-ranked-const.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/issue-120208-higher-ranked-const.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index 401efff9242..3aad510f366 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -329,7 +329,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } let projection_ty = if let ty::AssocKind::Fn = assoc_kind { - let mut emitted_bad_param_err = false; + let mut emitted_bad_param_err = None; // If we have an method return type bound, then we need to substitute // the method's early bound params with suitable late-bound params. let mut num_bound_vars = candidate.bound_vars().len(); @@ -346,46 +346,30 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { ) .into(), ty::GenericParamDefKind::Type { .. } => { - if !emitted_bad_param_err { + let guar = *emitted_bad_param_err.get_or_insert_with(|| { tcx.dcx().emit_err( crate::errors::ReturnTypeNotationIllegalParam::Type { span: path_span, param_span: tcx.def_span(param.def_id), }, - ); - emitted_bad_param_err = true; - } - Ty::new_bound( - tcx, - ty::INNERMOST, - ty::BoundTy { - var: ty::BoundVar::from_usize(num_bound_vars), - kind: ty::BoundTyKind::Param(param.def_id, param.name), - }, - ) - .into() + ) + }); + Ty::new_error(tcx, guar).into() } ty::GenericParamDefKind::Const { .. } => { - if !emitted_bad_param_err { + let guar = *emitted_bad_param_err.get_or_insert_with(|| { tcx.dcx().emit_err( crate::errors::ReturnTypeNotationIllegalParam::Const { span: path_span, param_span: tcx.def_span(param.def_id), }, - ); - emitted_bad_param_err = true; - } + ) + }); let ty = tcx .type_of(param.def_id) .no_bound_vars() .expect("ct params cannot have early bound vars"); - ty::Const::new_bound( - tcx, - ty::INNERMOST, - ty::BoundVar::from_usize(num_bound_vars), - ty, - ) - .into() + ty::Const::new_error(tcx, guar, ty).into() } }; num_bound_vars += 1; diff --git a/tests/ui/associated-type-bounds/return-type-notation/issue-120208-higher-ranked-const.rs b/tests/ui/associated-type-bounds/return-type-notation/issue-120208-higher-ranked-const.rs new file mode 100644 index 00000000000..3b350e14fd9 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/issue-120208-higher-ranked-const.rs @@ -0,0 +1,17 @@ +// edition: 2021 + +#![feature(return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait HealthCheck { + async fn check() -> bool; +} + +async fn do_health_check_par(hc: HC) +where + HC: HealthCheck + Send + 'static, + //~^ ERROR return type notation is not allowed for functions that have const parameters +{ +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/issue-120208-higher-ranked-const.stderr b/tests/ui/associated-type-bounds/return-type-notation/issue-120208-higher-ranked-const.stderr new file mode 100644 index 00000000000..8a3f037d003 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/issue-120208-higher-ranked-const.stderr @@ -0,0 +1,20 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-120208-higher-ranked-const.rs:3:12 + | +LL | #![feature(return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: return type notation is not allowed for functions that have const parameters + --> $DIR/issue-120208-higher-ranked-const.rs:12:21 + | +LL | async fn check() -> bool; + | -------------- const parameter declared here +... +LL | HC: HealthCheck + Send + 'static, + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + From 7ce206d185a429314a4577858f866008858db66e Mon Sep 17 00:00:00 2001 From: micolous Date: Mon, 22 Jan 2024 15:12:19 +1000 Subject: [PATCH 23/23] Fix -Zremap-path-scope typo --- src/doc/unstable-book/src/compiler-flags/remap-path-scope.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md index 13349ff6b8f..424f1128e3b 100644 --- a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md +++ b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md @@ -20,5 +20,5 @@ This flag accepts a comma-separated list of values and may be specified multiple ```sh # This would produce an absolute path to main.rs in build outputs of # "./main.rs". -rustc --remap-path-prefix=$(PWD)=/remapped -Zremap-path-prefix=object main.rs +rustc --remap-path-prefix=$(PWD)=/remapped -Zremap-path-scope=object main.rs ```