From a838a708e3239af7f9cbf0a79be10d495759e63c Mon Sep 17 00:00:00 2001 From: Nicolas Nattis Date: Sat, 12 Sep 2020 13:53:30 -0300 Subject: [PATCH 01/36] Add a spin loop hint for Arc::downgrade --- library/alloc/src/lib.rs | 1 + library/alloc/src/sync.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 755a21934f0c5..8083fa5ba3e08 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -116,6 +116,7 @@ #![feature(raw_ref_op)] #![feature(rustc_attrs)] #![feature(receiver_trait)] +#![feature(renamed_spin_loop)] #![feature(min_specialization)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 6a240fbb42a99..f2609e58c1463 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -11,6 +11,7 @@ use core::convert::{From, TryFrom}; use core::fmt; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; +use core::hint; use core::iter; use core::marker::{PhantomData, Unpin, Unsize}; use core::mem::{self, align_of_val, size_of_val}; @@ -764,6 +765,7 @@ impl Arc { loop { // check if the weak counter is currently "locked"; if so, spin. if cur == usize::MAX { + hint::spin_loop(); cur = this.inner().weak.load(Relaxed); continue; } From b58db1b7a122f4b45eec51cc72d5b1708cdb7a4f Mon Sep 17 00:00:00 2001 From: Nicolas Nattis Date: Sat, 12 Sep 2020 15:32:12 -0300 Subject: [PATCH 02/36] Formatting --- library/alloc/src/sync.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index f2609e58c1463..762997bccd09a 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -10,8 +10,8 @@ use core::cmp::Ordering; use core::convert::{From, TryFrom}; use core::fmt; use core::hash::{Hash, Hasher}; -use core::intrinsics::abort; use core::hint; +use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unpin, Unsize}; use core::mem::{self, align_of_val, size_of_val}; From 16e10bf81ee73f61cf813acef3d5dbbce4f66da2 Mon Sep 17 00:00:00 2001 From: Francesca Lovebloom Date: Wed, 7 Oct 2020 15:46:05 -0700 Subject: [PATCH 03/36] Revert "Allow dynamic linking for iOS/tvOS targets." This reverts commit 56e115a2627ba8bdd2e66c759457af96b2b0286a. --- compiler/rustc_target/src/spec/apple_sdk_base.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs index e34277d5af04c..1b17c2c278f9a 100644 --- a/compiler/rustc_target/src/spec/apple_sdk_base.rs +++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs @@ -34,6 +34,7 @@ fn link_env_remove(arch: Arch) -> Vec { pub fn opts(arch: Arch) -> TargetOptions { TargetOptions { cpu: target_cpu(arch), + dynamic_linking: false, executables: true, link_env_remove: link_env_remove(arch), has_elf_tls: false, From 16d65d04322de4a00327dfe26b4af6bd3e4187c8 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 6 Oct 2020 11:39:59 +0200 Subject: [PATCH 04/36] revise Hermit's mutex interface to support the behaviour of StaticMutex rust-lang/rust#77147 simplifies things by splitting this Mutex type into two types matching the two use cases: StaticMutex and MovableMutex. To support the behavior of StaticMutex, we move part of the mutex implementation into libstd. --- library/std/src/sys/hermit/mutex.rs | 190 ++++++++++++++++++++++++++-- 1 file changed, 182 insertions(+), 8 deletions(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index 3d4813209cbc4..511a5100ac625 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -1,9 +1,161 @@ +use crate::cell::UnsafeCell; +use crate::collections::VecDeque; use crate::ffi::c_void; +use crate::ops::{Deref, DerefMut, Drop}; use crate::ptr; +use crate::sync::atomic::{AtomicUsize, Ordering, spin_loop_hint}; use crate::sys::hermit::abi; +/// This type provides a lock based on busy waiting to realize mutual exclusion +/// +/// # Description +/// +/// This structure behaves a lot like a common mutex. There are some differences: +/// +/// - By using busy waiting, it can be used outside the runtime. +/// - It is a so called ticket lock (https://en.wikipedia.org/wiki/Ticket_lock) +/// and completly fair. +#[cfg_attr(target_arch = "x86_64", repr(align(128)))] +#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))] +struct Spinlock { + queue: AtomicUsize, + dequeue: AtomicUsize, + data: UnsafeCell, +} + +unsafe impl Sync for Spinlock {} +unsafe impl Send for Spinlock {} + +/// A guard to which the protected data can be accessed +/// +/// When the guard falls out of scope it will release the lock. +struct SpinlockGuard<'a, T: ?Sized + 'a> { + dequeue: &'a AtomicUsize, + data: &'a mut T, +} + +impl Spinlock { + pub const fn new(user_data: T) -> Spinlock { + SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() } + } + + #[inline] + fn obtain_lock(&self) { + let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1; + while self.dequeue.load(Ordering::SeqCst) != ticket { + spin_loop_hint(); + } + } + + #[inline] + pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> { + self.obtain_lock(); + SpinlockGuard { + dequeue: &self.dequeue, + data: &mut *self.data.get(), + } + } +} + +impl Default for Spinlock { + fn default() -> Spinlock { + Spinlock::new(Default::default()) + } +} + +impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> { + type Target = T; + fn deref(&self) -> &T { + &*self.data + } +} + +impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + &mut *self.data + } +} + +impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> { + /// The dropping of the SpinlockGuard will release the lock it was created from. + fn drop(&mut self) { + self.dequeue.fetch_add(1, Ordering::SeqCst); + } +} + +/// Realize a priority queue for tasks +struct PriorityQueue { + queues: [Option>; abi::NO_PRIORITIES], + prio_bitmap: u64, +} + +impl PriorityQueue { + pub const fn new() -> PriorityQueue { + PriorityQueue { + queues: [ + None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, + ], + prio_bitmap: 0, + } + } + + /// Add a task handle by its priority to the queue + pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) { + let i: usize = prio.into().into(); + self.prio_bitmap |= (1 << i) as u64; + if let Some(queue) = &mut self.queues[i] { + queue.push_back(id); + } else { + let mut queue = VecDeque::new(); + queue.push_back(id); + self.queues[i] = Some(queue); + } + } + + fn pop_from_queue(&mut self, queue_index: usize) -> Option { + if let Some(queue) = &mut self.queues[queue_index] { + let id = queue.pop_front(); + + if queue.is_empty() { + self.prio_bitmap &= !(1 << queue_index as u64); + } + + id + } else { + None + } + } + + /// Pop the task handle with the highest priority from the queue + pub fn pop(&mut self) -> Option { + for i in 0..abi::NO_PRIORITIES { + if self.prio_bitmap & (1 << i) != 0 { + return self.pop_from_queue(i); + } + } + + None + } +} + +struct MutexInner { + locked: bool, + blocked_task: PriorityQueue, +} + +impl MutexInner { + pub const fn new() -> MutexInner { + MutexInner { + locked: false, + blocked_task: PriorityQueue::new(), + } + } +} + pub struct Mutex { - inner: *const c_void, + inner: Spinlock, } unsafe impl Send for Mutex {} @@ -11,33 +163,55 @@ unsafe impl Sync for Mutex {} impl Mutex { pub const fn new() -> Mutex { - Mutex { inner: ptr::null() } + Mutex { + inner: Spinlock::new(MutexInner::new()), + } } #[inline] pub unsafe fn init(&mut self) { - let _ = abi::sem_init(&mut self.inner as *mut *const c_void, 1); + self.inner = Spinlock::new(MutexInner::new()); } #[inline] pub unsafe fn lock(&self) { - let _ = abi::sem_timedwait(self.inner, 0); + loop { + let mut guard = self.inner.lock(); + if guard.locked == false { + guard.locked = true; + return; + } else { + let prio = abi::get_priority(); + let id = abi::getpid(); + + guard.blocked_task.push(prio, id); + abi::block_current_task(); + drop(guard); + abi::yield_now(); + } + } } #[inline] pub unsafe fn unlock(&self) { - let _ = abi::sem_post(self.inner); + let mut guard = self.inner.lock(); + guard.locked = false; + if let Some(tid) = guard.blocked_task.pop() { + abi::wakeup_task(tid); + } } #[inline] pub unsafe fn try_lock(&self) -> bool { - let result = abi::sem_trywait(self.inner); - result == 0 + let mut guard = self.inner.lock(); + if guard.locked == false { + guard.locked = true; + } + guard.locked } #[inline] pub unsafe fn destroy(&self) { - let _ = abi::sem_destroy(self.inner); } } From 98fcc3fbc7b2f3420d393b8916746002d501401c Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 6 Oct 2020 11:42:57 +0200 Subject: [PATCH 05/36] using the latest version of libhermit-rs --- Cargo.lock | 4 ++-- library/std/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec4f3091d2da2..493c184313cb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1328,9 +1328,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "compiler_builtins", "libc", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index c08828bc0cde9..98d955efb2899 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -42,7 +42,7 @@ dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] } [target.'cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_os = "hermit"))'.dependencies] -hermit-abi = { version = "0.1.15", features = ['rustc-dep-of-std'] } +hermit-abi = { version = "0.1.17", features = ['rustc-dep-of-std'] } [target.wasm32-wasi.dependencies] wasi = { version = "0.9.0", features = ['rustc-dep-of-std'], default-features = false } From d560b50d87c05ea5a8e6186c05a50ecd828eaaae Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 6 Oct 2020 12:12:15 +0200 Subject: [PATCH 06/36] revise code to pass the format check --- library/std/src/sys/hermit/mutex.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index 511a5100ac625..bd9a9023396b4 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -3,7 +3,7 @@ use crate::collections::VecDeque; use crate::ffi::c_void; use crate::ops::{Deref, DerefMut, Drop}; use crate::ptr; -use crate::sync::atomic::{AtomicUsize, Ordering, spin_loop_hint}; +use crate::sync::atomic::{spin_loop_hint, AtomicUsize, Ordering}; use crate::sys::hermit::abi; /// This type provides a lock based on busy waiting to realize mutual exclusion @@ -50,10 +50,7 @@ impl Spinlock { #[inline] pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> { self.obtain_lock(); - SpinlockGuard { - dequeue: &self.dequeue, - data: &mut *self.data.get(), - } + SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() } } } @@ -147,10 +144,7 @@ struct MutexInner { impl MutexInner { pub const fn new() -> MutexInner { - MutexInner { - locked: false, - blocked_task: PriorityQueue::new(), - } + MutexInner { locked: false, blocked_task: PriorityQueue::new() } } } @@ -163,9 +157,7 @@ unsafe impl Sync for Mutex {} impl Mutex { pub const fn new() -> Mutex { - Mutex { - inner: Spinlock::new(MutexInner::new()), - } + Mutex { inner: Spinlock::new(MutexInner::new()) } } #[inline] @@ -211,8 +203,7 @@ impl Mutex { } #[inline] - pub unsafe fn destroy(&self) { - } + pub unsafe fn destroy(&self) {} } pub struct ReentrantMutex { From 986c1fc053828a051c9fd888cbf49393f276f4f5 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 7 Oct 2020 10:39:22 +0200 Subject: [PATCH 07/36] revise comments and descriptions of the helper functions --- library/std/src/sys/hermit/mutex.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index bd9a9023396b4..1bf142ec683d5 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -13,8 +13,7 @@ use crate::sys::hermit::abi; /// This structure behaves a lot like a common mutex. There are some differences: /// /// - By using busy waiting, it can be used outside the runtime. -/// - It is a so called ticket lock (https://en.wikipedia.org/wiki/Ticket_lock) -/// and completly fair. +/// - It is a so called ticket lock and is completly fair. #[cfg_attr(target_arch = "x86_64", repr(align(128)))] #[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))] struct Spinlock { @@ -98,7 +97,7 @@ impl PriorityQueue { } } - /// Add a task handle by its priority to the queue + /// Add a task id by its priority to the queue pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) { let i: usize = prio.into().into(); self.prio_bitmap |= (1 << i) as u64; From d6e955f3bfd0a47240879549dc2fb3284dfe08e4 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Fri, 9 Oct 2020 06:42:19 +0200 Subject: [PATCH 08/36] fix typos in new method --- library/std/src/sys/hermit/mutex.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index 1bf142ec683d5..c3f88ada0f4bc 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -35,7 +35,11 @@ struct SpinlockGuard<'a, T: ?Sized + 'a> { impl Spinlock { pub const fn new(user_data: T) -> Spinlock { - SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() } + Spinlock { + queue: AtomicUsize::new(0), + dequeue: AtomicUsize::new(1), + data: UnsafeCell::new(user_data), + } } #[inline] From 530f5754664699dee29bde6cfa99aaf861499544 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Fri, 9 Oct 2020 07:26:48 +0200 Subject: [PATCH 09/36] revise code to pass the format check --- library/std/src/sys/hermit/mutex.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index c3f88ada0f4bc..e9222f34b89b4 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -36,9 +36,9 @@ struct SpinlockGuard<'a, T: ?Sized + 'a> { impl Spinlock { pub const fn new(user_data: T) -> Spinlock { Spinlock { - queue: AtomicUsize::new(0), - dequeue: AtomicUsize::new(1), - data: UnsafeCell::new(user_data), + queue: AtomicUsize::new(0), + dequeue: AtomicUsize::new(1), + data: UnsafeCell::new(user_data), } } From 8d8a290c691db7a8ee566edbd485a729eb41d4ba Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Fri, 9 Oct 2020 08:25:19 +0200 Subject: [PATCH 10/36] add hermit to the list of omit OS --- library/std/src/sys/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 7b5fac922d08a..b4628b649117e 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -89,6 +89,7 @@ cfg_if::cfg_if! { #[stable(feature = "rust1", since = "1.0.0")] pub use self::ext as windows_ext; } else if #[cfg(any(target_os = "cloudabi", + target_os = "hermit", target_arch = "wasm32", all(target_vendor = "fortanix", target_env = "sgx")))] { // On CloudABI and wasm right now the shim below doesn't compile, so From 33fd08b61f57cde81bf01964b2fc4d2dca0e4d3f Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 12 Oct 2020 06:51:52 +0200 Subject: [PATCH 11/36] remove obsolete function diverge --- library/std/src/sys/hermit/fs.rs | 4 - library/std/src/sys/hermit/process.rs | 149 -------------------------- 2 files changed, 153 deletions(-) delete mode 100644 library/std/src/sys/hermit/process.rs diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 82ccab1462ba8..829d4c943f11b 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -334,10 +334,6 @@ impl File { pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } - - pub fn diverge(&self) -> ! { - loop {} - } } impl DirBuilder { diff --git a/library/std/src/sys/hermit/process.rs b/library/std/src/sys/hermit/process.rs deleted file mode 100644 index 4702e5c549228..0000000000000 --- a/library/std/src/sys/hermit/process.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::ffi::OsStr; -use crate::fmt; -use crate::io; -use crate::sys::fs::File; -use crate::sys::pipe::AnonPipe; -use crate::sys::{unsupported, Void}; -use crate::sys_common::process::CommandEnv; - -pub use crate::ffi::OsString as EnvKey; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - env: CommandEnv, -} - -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - -pub enum Stdio { - Inherit, - Null, - MakePipe, -} - -impl Command { - pub fn new(_program: &OsStr) -> Command { - Command { env: Default::default() } - } - - pub fn arg(&mut self, _arg: &OsStr) {} - - pub fn env_mut(&mut self) -> &mut CommandEnv { - &mut self.env - } - - pub fn cwd(&mut self, _dir: &OsStr) {} - - pub fn stdin(&mut self, _stdin: Stdio) {} - - pub fn stdout(&mut self, _stdout: Stdio) {} - - pub fn stderr(&mut self, _stderr: Stdio) {} - - pub fn spawn( - &mut self, - _default: Stdio, - _needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - unsupported() - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - pipe.diverge() - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - file.diverge() - } -} - -impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -pub struct ExitStatus(Void); - -impl ExitStatus { - pub fn success(&self) -> bool { - match self.0 {} - } - - pub fn code(&self) -> Option { - match self.0 {} - } -} - -impl Clone for ExitStatus { - fn clone(&self) -> ExitStatus { - match self.0 {} - } -} - -impl Copy for ExitStatus {} - -impl PartialEq for ExitStatus { - fn eq(&self, _other: &ExitStatus) -> bool { - match self.0 {} - } -} - -impl Eq for ExitStatus {} - -impl fmt::Debug for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(bool); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(false); - pub const FAILURE: ExitCode = ExitCode(true); - - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} - -pub struct Process(Void); - -impl Process { - pub fn id(&self) -> u32 { - match self.0 {} - } - - pub fn kill(&mut self) -> io::Result<()> { - match self.0 {} - } - - pub fn wait(&mut self) -> io::Result { - match self.0 {} - } - - pub fn try_wait(&mut self) -> io::Result> { - match self.0 {} - } -} From 30c3dadb4da44c950f79e9772b36bbaf2660bb0e Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 12 Oct 2020 06:53:06 +0200 Subject: [PATCH 12/36] reuse implementation of the system provider "unsupported" --- library/std/src/sys/hermit/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index 8eaf07e52d69a..af05310a8d3ab 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -31,6 +31,7 @@ pub mod net; pub mod os; pub mod path; pub mod pipe; +#[path = "../unsupported/process.rs"] pub mod process; pub mod rwlock; pub mod stack_overflow; From 1741e5b8f581960a6e9cb9f0b8261b5f0c26e92d Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 12 Oct 2020 06:54:48 +0200 Subject: [PATCH 13/36] define required type 'MovableMutex' --- library/std/src/sys/hermit/mutex.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index e9222f34b89b4..e12c2f4e00c50 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -155,6 +155,8 @@ pub struct Mutex { inner: Spinlock, } +pub type MovableMutex = Mutex; + unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} From bc6b2ac449a0f6d9a8bd87788a0eae9516cb58ce Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 13 Oct 2020 12:06:48 +0200 Subject: [PATCH 14/36] move __rg_oom to the libos to avoid duplicated symbols --- library/alloc/src/alloc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 4646d4a833525..850451bb29e12 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -372,7 +372,7 @@ pub fn handle_alloc_error(layout: Layout) -> ! { unsafe { oom_impl(layout) } } -#[cfg(not(any(test, bootstrap)))] +#[cfg(not(any(target_os="hermit", test, bootstrap)))] #[doc(hidden)] #[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "none")] From 77d98316f489f4490459b3dcf12d14f45caf1286 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 13 Oct 2020 12:22:18 +0200 Subject: [PATCH 15/36] minor changes to pass the format check --- library/alloc/src/alloc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 850451bb29e12..1ea34be71dd3b 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -372,7 +372,7 @@ pub fn handle_alloc_error(layout: Layout) -> ! { unsafe { oom_impl(layout) } } -#[cfg(not(any(target_os="hermit", test, bootstrap)))] +#[cfg(not(any(target_os = "hermit", test, bootstrap)))] #[doc(hidden)] #[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "none")] From bf268fe928eae8d85a868ccdbcc086ea033ae51c Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 13 Oct 2020 23:25:42 +0200 Subject: [PATCH 16/36] box mutex to get a movable mutex the commit avoid an alignement issue in Mutex implementation --- library/std/src/sys/hermit/mutex.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index e12c2f4e00c50..f988a019cfedb 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -155,7 +155,7 @@ pub struct Mutex { inner: Spinlock, } -pub type MovableMutex = Mutex; +pub type MovableMutex = Box; unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} From 91a9f83dd1d73cfd451f81306361df3fafad84a5 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 16 Oct 2020 09:09:20 -0700 Subject: [PATCH 17/36] Define `fs::hard_link` to not follow symlinks. POSIX leaves it implementation-defined whether `link` follows symlinks. In practice, for example, on Linux it does not and on FreeBSD it does. So, switch to `linkat`, so that we can pick a behavior rather than depending on OS defaults. Pick the option to not follow symlinks. This is somewhat arbitrary, but seems the less surprising choice because hard linking is a very low-level feature which requires the source and destination to be on the same mounted filesystem, and following a symbolic link could end up in a different mounted filesystem. --- library/std/src/fs.rs | 7 +++-- library/std/src/fs/tests.rs | 51 ++++++++++++++++++++++++++++++++++ library/std/src/sys/unix/fs.rs | 5 +++- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 161bfe3795c2c..c611bf4d74a49 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1701,10 +1701,13 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// The `dst` path will be a link pointing to the `src` path. Note that systems /// often require these two paths to both be located on the same filesystem. /// +/// If `src` names a symbolic link, it is not followed. The created hard link +/// points to the symbolic link itself. +/// /// # Platform-specific behavior /// -/// This function currently corresponds to the `link` function on Unix -/// and the `CreateHardLink` function on Windows. +/// This function currently corresponds to the `linkat` function with no flags +/// on Unix and the `CreateHardLink` function on Windows. /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 65a29076fefa8..8a723d3b4ae22 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1337,3 +1337,54 @@ fn metadata_access_times() { } } } + +/// Test creating hard links to symlinks. +#[test] +fn symlink_hard_link() { + let tmpdir = tmpdir(); + + // Create "file", a file. + check!(fs::File::create(tmpdir.join("file"))); + + // Create "symlink", a symlink to "file". + check!(symlink_file("file", tmpdir.join("symlink"))); + + // Create "hard_link", a hard link to "symlink". + check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link"))); + + // "hard_link" should appear as a symlink. + assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); + + // We sould be able to open "file" via any of the above names. + let _ = check!(fs::File::open(tmpdir.join("file"))); + assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); + let _ = check!(fs::File::open(tmpdir.join("symlink"))); + let _ = check!(fs::File::open(tmpdir.join("hard_link"))); + + // Rename "file" to "file.renamed". + check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed"))); + + // Now, the symlink and the hard link should be dangling. + assert!(fs::File::open(tmpdir.join("file")).is_err()); + let _ = check!(fs::File::open(tmpdir.join("file.renamed"))); + assert!(fs::File::open(tmpdir.join("symlink")).is_err()); + assert!(fs::File::open(tmpdir.join("hard_link")).is_err()); + + // The symlink and the hard link should both still point to "file". + assert!(fs::read_link(tmpdir.join("file")).is_err()); + assert!(fs::read_link(tmpdir.join("file.renamed")).is_err()); + assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file")); + assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file")); + + // Remove "file.renamed". + check!(fs::remove_file(tmpdir.join("file.renamed"))); + + // Now, we can't open the file by any name. + assert!(fs::File::open(tmpdir.join("file")).is_err()); + assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); + assert!(fs::File::open(tmpdir.join("symlink")).is_err()); + assert!(fs::File::open(tmpdir.join("hard_link")).is_err()); + + // "hard_link" should still appear as a symlink. + assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); +} diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 819e8ef18415b..88693e4786c0f 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1067,7 +1067,10 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { pub fn link(src: &Path, dst: &Path) -> io::Result<()> { let src = cstr(src)?; let dst = cstr(dst)?; - cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; + // Use `linkat` with `AT_FDCWD` instead of `link` as `link` leaves it + // implmentation-defined whether it follows symlinks. Pass 0 as the + // `linkat` flags argument so that we don't follow symlinks. + cvt(unsafe { libc::linkat(libc::AT_FDCWD, src.as_ptr(), libc::AT_FDCWD, dst.as_ptr(), 0) })?; Ok(()) } From d3467fe520d17f26f3781286e6b6caab4700928e Mon Sep 17 00:00:00 2001 From: chansuke Date: Sun, 2 Aug 2020 20:57:55 +0900 Subject: [PATCH 18/36] `#[deny(unsafe_op_in_unsafe_fn)]` in sys/cloudabi --- library/std/src/sys/cloudabi/abi/cloudabi.rs | 133 ++++++++++--------- library/std/src/sys/cloudabi/mod.rs | 2 + library/std/src/sys/cloudabi/mutex.rs | 4 +- 3 files changed, 75 insertions(+), 64 deletions(-) diff --git a/library/std/src/sys/cloudabi/abi/cloudabi.rs b/library/std/src/sys/cloudabi/abi/cloudabi.rs index b02faf1830c53..5c4e3fd85c41c 100644 --- a/library/std/src/sys/cloudabi/abi/cloudabi.rs +++ b/library/std/src/sys/cloudabi/abi/cloudabi.rs @@ -1910,7 +1910,7 @@ extern "C" { /// The resolution of the clock. #[inline] pub unsafe fn clock_res_get(clock_id_: clockid, resolution_: &mut timestamp) -> errno { - cloudabi_sys_clock_res_get(clock_id_, resolution_) + unsafe { cloudabi_sys_clock_res_get(clock_id_, resolution_) } } /// Obtains the time value of a clock. @@ -1934,7 +1934,7 @@ pub unsafe fn clock_time_get( precision_: timestamp, time_: *mut timestamp, ) -> errno { - cloudabi_sys_clock_time_get(clock_id_, precision_, time_) + unsafe { cloudabi_sys_clock_time_get(clock_id_, precision_, time_) } } /// Wakes up threads waiting on a userspace condition variable. @@ -1961,7 +1961,7 @@ pub unsafe fn clock_time_get( /// threads, all threads are woken up. #[inline] pub unsafe fn condvar_signal(condvar_: *mut condvar, scope_: scope, nwaiters_: nthreads) -> errno { - cloudabi_sys_condvar_signal(condvar_, scope_, nwaiters_) + unsafe { cloudabi_sys_condvar_signal(condvar_, scope_, nwaiters_) } } /// Closes a file descriptor. @@ -1972,7 +1972,7 @@ pub unsafe fn condvar_signal(condvar_: *mut condvar, scope_: scope, nwaiters_: n /// The file descriptor that needs to be closed. #[inline] pub unsafe fn fd_close(fd_: fd) -> errno { - cloudabi_sys_fd_close(fd_) + unsafe { cloudabi_sys_fd_close(fd_) } } /// Creates a file descriptor. @@ -1990,7 +1990,7 @@ pub unsafe fn fd_close(fd_: fd) -> errno { /// The file descriptor that has been created. #[inline] pub unsafe fn fd_create1(type_: filetype, fd_: &mut fd) -> errno { - cloudabi_sys_fd_create1(type_, fd_) + unsafe { cloudabi_sys_fd_create1(type_, fd_) } } /// Creates a pair of file descriptors. @@ -2013,7 +2013,8 @@ pub unsafe fn fd_create1(type_: filetype, fd_: &mut fd) -> errno { /// The second file descriptor of the pair. #[inline] pub unsafe fn fd_create2(type_: filetype, fd1_: &mut fd, fd2_: &mut fd) -> errno { - cloudabi_sys_fd_create2(type_, fd1_, fd2_) + // SAFETY: the caller must uphold the safety contract for `cloudabi_sys_fd_create2`. + unsafe { cloudabi_sys_fd_create2(type_, fd1_, fd2_) } } /// Synchronizes the data of a file to disk. @@ -2025,7 +2026,9 @@ pub unsafe fn fd_create2(type_: filetype, fd1_: &mut fd, fd2_: &mut fd) -> errno /// needs to be synchronized to disk. #[inline] pub unsafe fn fd_datasync(fd_: fd) -> errno { - cloudabi_sys_fd_datasync(fd_) + // SAFETY: the caller must guarantee that `fd` is valid + // for synchronization. + unsafe { cloudabi_sys_fd_datasync(fd_) } } /// Duplicates a file descriptor. @@ -2040,7 +2043,7 @@ pub unsafe fn fd_datasync(fd_: fd) -> errno { /// The new file descriptor. #[inline] pub unsafe fn fd_dup(from_: fd, fd_: &mut fd) -> errno { - cloudabi_sys_fd_dup(from_, fd_) + unsafe { cloudabi_sys_fd_dup(from_, fd_) } } /// Reads from a file descriptor, without using and updating the @@ -2064,7 +2067,7 @@ pub unsafe fn fd_dup(from_: fd, fd_: &mut fd) -> errno { /// The number of bytes read. #[inline] pub unsafe fn fd_pread(fd_: fd, iovs_: &[iovec], offset_: filesize, nread_: &mut usize) -> errno { - cloudabi_sys_fd_pread(fd_, iovs_.as_ptr(), iovs_.len(), offset_, nread_) + unsafe { cloudabi_sys_fd_pread(fd_, iovs_.as_ptr(), iovs_.len(), offset_, nread_) } } /// Writes to a file descriptor, without using and updating the @@ -2093,7 +2096,7 @@ pub unsafe fn fd_pwrite( offset_: filesize, nwritten_: &mut usize, ) -> errno { - cloudabi_sys_fd_pwrite(fd_, iovs_.as_ptr(), iovs_.len(), offset_, nwritten_) + unsafe { cloudabi_sys_fd_pwrite(fd_, iovs_.as_ptr(), iovs_.len(), offset_, nwritten_) } } /// Reads from a file descriptor. @@ -2112,7 +2115,7 @@ pub unsafe fn fd_pwrite( /// The number of bytes read. #[inline] pub unsafe fn fd_read(fd_: fd, iovs_: &[iovec], nread_: &mut usize) -> errno { - cloudabi_sys_fd_read(fd_, iovs_.as_ptr(), iovs_.len(), nread_) + unsafe { cloudabi_sys_fd_read(fd_, iovs_.as_ptr(), iovs_.len(), nread_) } } /// Atomically replaces a file descriptor by a copy of another @@ -2138,7 +2141,7 @@ pub unsafe fn fd_read(fd_: fd, iovs_: &[iovec], nread_: &mut usize) -> errno { /// overwritten. #[inline] pub unsafe fn fd_replace(from_: fd, to_: fd) -> errno { - cloudabi_sys_fd_replace(from_, to_) + unsafe { cloudabi_sys_fd_replace(from_, to_) } } /// Moves the offset of the file descriptor. @@ -2166,7 +2169,7 @@ pub unsafe fn fd_seek( whence_: whence, newoffset_: &mut filesize, ) -> errno { - cloudabi_sys_fd_seek(fd_, offset_, whence_, newoffset_) + unsafe { cloudabi_sys_fd_seek(fd_, offset_, whence_, newoffset_) } } /// Gets attributes of a file descriptor. @@ -2182,7 +2185,7 @@ pub unsafe fn fd_seek( /// attributes are stored. #[inline] pub unsafe fn fd_stat_get(fd_: fd, buf_: *mut fdstat) -> errno { - cloudabi_sys_fd_stat_get(fd_, buf_) + unsafe { cloudabi_sys_fd_stat_get(fd_, buf_) } } /// Adjusts attributes of a file descriptor. @@ -2202,7 +2205,7 @@ pub unsafe fn fd_stat_get(fd_: fd, buf_: *mut fdstat) -> errno { /// be adjusted. #[inline] pub unsafe fn fd_stat_put(fd_: fd, buf_: *const fdstat, flags_: fdsflags) -> errno { - cloudabi_sys_fd_stat_put(fd_, buf_, flags_) + unsafe { cloudabi_sys_fd_stat_put(fd_, buf_, flags_) } } /// Synchronizes the data and metadata of a file to disk. @@ -2214,7 +2217,7 @@ pub unsafe fn fd_stat_put(fd_: fd, buf_: *const fdstat, flags_: fdsflags) -> err /// and metadata needs to be synchronized to disk. #[inline] pub unsafe fn fd_sync(fd_: fd) -> errno { - cloudabi_sys_fd_sync(fd_) + unsafe { cloudabi_sys_fd_sync(fd_) } } /// Writes to a file descriptor. @@ -2233,7 +2236,7 @@ pub unsafe fn fd_sync(fd_: fd) -> errno { /// The number of bytes written. #[inline] pub unsafe fn fd_write(fd_: fd, iovs_: &[ciovec], nwritten_: &mut usize) -> errno { - cloudabi_sys_fd_write(fd_, iovs_.as_ptr(), iovs_.len(), nwritten_) + unsafe { cloudabi_sys_fd_write(fd_, iovs_.as_ptr(), iovs_.len(), nwritten_) } } /// Provides file advisory information on a file descriptor. @@ -2256,7 +2259,7 @@ pub unsafe fn fd_write(fd_: fd, iovs_: &[ciovec], nwritten_: &mut usize) -> errn /// The advice. #[inline] pub unsafe fn file_advise(fd_: fd, offset_: filesize, len_: filesize, advice_: advice) -> errno { - cloudabi_sys_file_advise(fd_, offset_, len_, advice_) + unsafe { cloudabi_sys_file_advise(fd_, offset_, len_, advice_) } } /// Forces the allocation of space in a file. @@ -2275,7 +2278,7 @@ pub unsafe fn file_advise(fd_: fd, offset_: filesize, len_: filesize, advice_: a /// The length of the area that is allocated. #[inline] pub unsafe fn file_allocate(fd_: fd, offset_: filesize, len_: filesize) -> errno { - cloudabi_sys_file_allocate(fd_, offset_, len_) + unsafe { cloudabi_sys_file_allocate(fd_, offset_, len_) } } /// Creates a file of a specified type. @@ -2296,7 +2299,7 @@ pub unsafe fn file_allocate(fd_: fd, offset_: filesize, len_: filesize) -> errno /// Creates a directory. #[inline] pub unsafe fn file_create(fd_: fd, path_: &[u8], type_: filetype) -> errno { - cloudabi_sys_file_create(fd_, path_.as_ptr(), path_.len(), type_) + unsafe { cloudabi_sys_file_create(fd_, path_.as_ptr(), path_.len(), type_)} } /// Creates a hard link. @@ -2320,7 +2323,7 @@ pub unsafe fn file_create(fd_: fd, path_: &[u8], type_: filetype) -> errno { /// should be created. #[inline] pub unsafe fn file_link(fd1_: lookup, path1_: &[u8], fd2_: fd, path2_: &[u8]) -> errno { - cloudabi_sys_file_link(fd1_, path1_.as_ptr(), path1_.len(), fd2_, path2_.as_ptr(), path2_.len()) + unsafe { cloudabi_sys_file_link(fd1_, path1_.as_ptr(), path1_.len(), fd2_, path2_.as_ptr(), path2_.len()) } } /// Opens a file. @@ -2362,7 +2365,7 @@ pub unsafe fn file_open( fds_: *const fdstat, fd_: &mut fd, ) -> errno { - cloudabi_sys_file_open(dirfd_, path_.as_ptr(), path_.len(), oflags_, fds_, fd_) + unsafe { cloudabi_sys_file_open(dirfd_, path_.as_ptr(), path_.len(), oflags_, fds_, fd_) } } /// Reads directory entries from a directory. @@ -2402,7 +2405,7 @@ pub unsafe fn file_readdir( cookie_: dircookie, bufused_: &mut usize, ) -> errno { - cloudabi_sys_file_readdir(fd_, buf_.as_mut_ptr() as *mut (), buf_.len(), cookie_, bufused_) + unsafe { cloudabi_sys_file_readdir(fd_, buf_.as_mut_ptr() as *mut (), buf_.len(), cookie_, bufused_) } } /// Reads the contents of a symbolic link. @@ -2425,14 +2428,16 @@ pub unsafe fn file_readdir( /// The number of bytes placed in the buffer. #[inline] pub unsafe fn file_readlink(fd_: fd, path_: &[u8], buf_: &mut [u8], bufused_: &mut usize) -> errno { - cloudabi_sys_file_readlink( - fd_, - path_.as_ptr(), - path_.len(), - buf_.as_mut_ptr(), - buf_.len(), - bufused_, - ) + unsafe { + cloudabi_sys_file_readlink( + fd_, + path_.as_ptr(), + path_.len(), + buf_.as_mut_ptr(), + buf_.len(), + bufused_, + ) + } } /// Renames a file. @@ -2456,14 +2461,16 @@ pub unsafe fn file_readlink(fd_: fd, path_: &[u8], buf_: &mut [u8], bufused_: &m /// be renamed. #[inline] pub unsafe fn file_rename(fd1_: fd, path1_: &[u8], fd2_: fd, path2_: &[u8]) -> errno { - cloudabi_sys_file_rename( - fd1_, - path1_.as_ptr(), - path1_.len(), - fd2_, - path2_.as_ptr(), - path2_.len(), - ) + unsafe { + cloudabi_sys_file_rename( + fd1_, + path1_.as_ptr(), + path1_.len(), + fd2_, + path2_.as_ptr(), + path2_.len(), + ) + } } /// Gets attributes of a file by file descriptor. @@ -2479,7 +2486,7 @@ pub unsafe fn file_rename(fd1_: fd, path1_: &[u8], fd2_: fd, path2_: &[u8]) -> e /// stored. #[inline] pub unsafe fn file_stat_fget(fd_: fd, buf_: *mut filestat) -> errno { - cloudabi_sys_file_stat_fget(fd_, buf_) + unsafe { cloudabi_sys_file_stat_fget(fd_, buf_) } } /// Adjusts attributes of a file by file descriptor. @@ -2499,7 +2506,7 @@ pub unsafe fn file_stat_fget(fd_: fd, buf_: *mut filestat) -> errno { /// be adjusted. #[inline] pub unsafe fn file_stat_fput(fd_: fd, buf_: *const filestat, flags_: fsflags) -> errno { - cloudabi_sys_file_stat_fput(fd_, buf_, flags_) + unsafe { cloudabi_sys_file_stat_fput(fd_, buf_, flags_) } } /// Gets attributes of a file by path. @@ -2520,7 +2527,7 @@ pub unsafe fn file_stat_fput(fd_: fd, buf_: *const filestat, flags_: fsflags) -> /// stored. #[inline] pub unsafe fn file_stat_get(fd_: lookup, path_: &[u8], buf_: *mut filestat) -> errno { - cloudabi_sys_file_stat_get(fd_, path_.as_ptr(), path_.len(), buf_) + unsafe { cloudabi_sys_file_stat_get(fd_, path_.as_ptr(), path_.len(), buf_) } } /// Adjusts attributes of a file by path. @@ -2550,7 +2557,7 @@ pub unsafe fn file_stat_put( buf_: *const filestat, flags_: fsflags, ) -> errno { - cloudabi_sys_file_stat_put(fd_, path_.as_ptr(), path_.len(), buf_, flags_) + unsafe { cloudabi_sys_file_stat_put(fd_, path_.as_ptr(), path_.len(), buf_, flags_) } } /// Creates a symbolic link. @@ -2569,7 +2576,7 @@ pub unsafe fn file_stat_put( /// link should be created. #[inline] pub unsafe fn file_symlink(path1_: &[u8], fd_: fd, path2_: &[u8]) -> errno { - cloudabi_sys_file_symlink(path1_.as_ptr(), path1_.len(), fd_, path2_.as_ptr(), path2_.len()) + unsafe { cloudabi_sys_file_symlink(path1_.as_ptr(), path1_.len(), fd_, path2_.as_ptr(), path2_.len()) } } /// Unlinks a file, or removes a directory. @@ -2591,7 +2598,7 @@ pub unsafe fn file_symlink(path1_: &[u8], fd_: fd, path2_: &[u8]) -> errno { /// Otherwise, unlink a file. #[inline] pub unsafe fn file_unlink(fd_: fd, path_: &[u8], flags_: ulflags) -> errno { - cloudabi_sys_file_unlink(fd_, path_.as_ptr(), path_.len(), flags_) + unsafe { cloudabi_sys_file_unlink(fd_, path_.as_ptr(), path_.len(), flags_) } } /// Unlocks a write-locked userspace lock. @@ -2618,7 +2625,7 @@ pub unsafe fn file_unlink(fd_: fd, path_: &[u8], flags_: ulflags) -> errno { /// shared memory. #[inline] pub unsafe fn lock_unlock(lock_: *mut lock, scope_: scope) -> errno { - cloudabi_sys_lock_unlock(lock_, scope_) + unsafe { cloudabi_sys_lock_unlock(lock_, scope_) } } /// Provides memory advisory information on a region of memory. @@ -2633,7 +2640,7 @@ pub unsafe fn lock_unlock(lock_: *mut lock, scope_: scope) -> errno { /// The advice. #[inline] pub unsafe fn mem_advise(mapping_: &mut [u8], advice_: advice) -> errno { - cloudabi_sys_mem_advise(mapping_.as_mut_ptr() as *mut (), mapping_.len(), advice_) + unsafe { cloudabi_sys_mem_advise(mapping_.as_mut_ptr() as *mut (), mapping_.len(), advice_) } } /// Creates a memory mapping, making the contents of a file @@ -2682,7 +2689,7 @@ pub unsafe fn mem_map( off_: filesize, mem_: &mut *mut (), ) -> errno { - cloudabi_sys_mem_map(addr_, len_, prot_, flags_, fd_, off_, mem_) + unsafe { cloudabi_sys_mem_map(addr_, len_, prot_, flags_, fd_, off_, mem_) } } /// Changes the protection of a memory mapping. @@ -2696,7 +2703,7 @@ pub unsafe fn mem_map( /// New protection options. #[inline] pub unsafe fn mem_protect(mapping_: &mut [u8], prot_: mprot) -> errno { - cloudabi_sys_mem_protect(mapping_.as_mut_ptr() as *mut (), mapping_.len(), prot_) + unsafe { cloudabi_sys_mem_protect(mapping_.as_mut_ptr() as *mut (), mapping_.len(), prot_) } } /// Synchronizes a region of memory with its physical storage. @@ -2710,7 +2717,7 @@ pub unsafe fn mem_protect(mapping_: &mut [u8], prot_: mprot) -> errno { /// The method of synchronization. #[inline] pub unsafe fn mem_sync(mapping_: &mut [u8], flags_: msflags) -> errno { - cloudabi_sys_mem_sync(mapping_.as_mut_ptr() as *mut (), mapping_.len(), flags_) + unsafe { cloudabi_sys_mem_sync(mapping_.as_mut_ptr() as *mut (), mapping_.len(), flags_) } } /// Unmaps a region of memory. @@ -2721,7 +2728,7 @@ pub unsafe fn mem_sync(mapping_: &mut [u8], flags_: msflags) -> errno { /// The pages that needs to be unmapped. #[inline] pub unsafe fn mem_unmap(mapping_: &mut [u8]) -> errno { - cloudabi_sys_mem_unmap(mapping_.as_mut_ptr() as *mut (), mapping_.len()) + unsafe { cloudabi_sys_mem_unmap(mapping_.as_mut_ptr() as *mut (), mapping_.len()) } } /// Concurrently polls for the occurrence of a set of events. @@ -2746,7 +2753,7 @@ pub unsafe fn poll( nsubscriptions_: usize, nevents_: *mut usize, ) -> errno { - cloudabi_sys_poll(in_, out_, nsubscriptions_, nevents_) + unsafe { cloudabi_sys_poll(in_, out_, nsubscriptions_, nevents_) } } /// Replaces the process by a new executable. @@ -2784,7 +2791,7 @@ pub unsafe fn poll( /// execution. #[inline] pub unsafe fn proc_exec(fd_: fd, data_: &[u8], fds_: &[fd]) -> errno { - cloudabi_sys_proc_exec(fd_, data_.as_ptr() as *const (), data_.len(), fds_.as_ptr(), fds_.len()) + unsafe { cloudabi_sys_proc_exec(fd_, data_.as_ptr() as *const (), data_.len(), fds_.as_ptr(), fds_.len()) } } /// Terminates the process normally. @@ -2797,7 +2804,7 @@ pub unsafe fn proc_exec(fd_: fd, data_: &[u8], fds_: &[fd]) -> errno { /// through [`event.union.proc_terminate.exitcode`](struct.event_proc_terminate.html#structfield.exitcode). #[inline] pub unsafe fn proc_exit(rval_: exitcode) -> ! { - cloudabi_sys_proc_exit(rval_) + unsafe { cloudabi_sys_proc_exit(rval_) } } /// Forks the process of the calling thread. @@ -2822,7 +2829,7 @@ pub unsafe fn proc_exit(rval_: exitcode) -> ! { /// initial thread of the child process. #[inline] pub unsafe fn proc_fork(fd_: &mut fd, tid_: &mut tid) -> errno { - cloudabi_sys_proc_fork(fd_, tid_) + unsafe { cloudabi_sys_proc_fork(fd_, tid_) } } /// Sends a signal to the process of the calling thread. @@ -2837,7 +2844,7 @@ pub unsafe fn proc_fork(fd_: &mut fd, tid_: &mut tid) -> errno { /// [`event.union.proc_terminate.signal`](struct.event_proc_terminate.html#structfield.signal). #[inline] pub unsafe fn proc_raise(sig_: signal) -> errno { - cloudabi_sys_proc_raise(sig_) + unsafe { cloudabi_sys_proc_raise(sig_) } } /// Obtains random data from the kernel random number generator. @@ -2853,7 +2860,7 @@ pub unsafe fn proc_raise(sig_: signal) -> errno { /// data. #[inline] pub unsafe fn random_get(buf_: &mut [u8]) -> errno { - cloudabi_sys_random_get(buf_.as_mut_ptr() as *mut (), buf_.len()) + unsafe { cloudabi_sys_random_get(buf_.as_mut_ptr() as *mut (), buf_.len()) } } /// Receives a message on a socket. @@ -2871,7 +2878,7 @@ pub unsafe fn random_get(buf_: &mut [u8]) -> errno { /// Output parameters. #[inline] pub unsafe fn sock_recv(sock_: fd, in_: *const recv_in, out_: *mut recv_out) -> errno { - cloudabi_sys_sock_recv(sock_, in_, out_) + unsafe { cloudabi_sys_sock_recv(sock_, in_, out_) } } /// Sends a message on a socket. @@ -2888,7 +2895,7 @@ pub unsafe fn sock_recv(sock_: fd, in_: *const recv_in, out_: *mut recv_out) -> /// Output parameters. #[inline] pub unsafe fn sock_send(sock_: fd, in_: *const send_in, out_: *mut send_out) -> errno { - cloudabi_sys_sock_send(sock_, in_, out_) + unsafe { cloudabi_sys_sock_send(sock_, in_, out_) } } /// Shuts down socket send and receive channels. @@ -2903,7 +2910,7 @@ pub unsafe fn sock_send(sock_: fd, in_: *const send_in, out_: *mut send_out) -> /// down. #[inline] pub unsafe fn sock_shutdown(sock_: fd, how_: sdflags) -> errno { - cloudabi_sys_sock_shutdown(sock_, how_) + unsafe { cloudabi_sys_sock_shutdown(sock_, how_) } } /// Creates a new thread within the current process. @@ -2917,7 +2924,7 @@ pub unsafe fn sock_shutdown(sock_: fd, how_: sdflags) -> errno { /// The thread ID of the new thread. #[inline] pub unsafe fn thread_create(attr_: *mut threadattr, tid_: &mut tid) -> errno { - cloudabi_sys_thread_create(attr_, tid_) + unsafe { cloudabi_sys_thread_create(attr_, tid_) } } /// Terminates the calling thread. @@ -2937,11 +2944,11 @@ pub unsafe fn thread_create(attr_: *mut threadattr, tid_: &mut tid) -> errno { /// shared memory. #[inline] pub unsafe fn thread_exit(lock_: *mut lock, scope_: scope) -> ! { - cloudabi_sys_thread_exit(lock_, scope_) + unsafe { cloudabi_sys_thread_exit(lock_, scope_) } } /// Temporarily yields execution of the calling thread. #[inline] pub unsafe fn thread_yield() -> errno { - cloudabi_sys_thread_yield() + unsafe { cloudabi_sys_thread_yield() } } diff --git a/library/std/src/sys/cloudabi/mod.rs b/library/std/src/sys/cloudabi/mod.rs index f7dd2c8d00fd2..13f1bc8826e61 100644 --- a/library/std/src/sys/cloudabi/mod.rs +++ b/library/std/src/sys/cloudabi/mod.rs @@ -1,3 +1,5 @@ +#![deny(unsafe_op_in_unsafe_fn)] + use crate::io::ErrorKind; use crate::mem; diff --git a/library/std/src/sys/cloudabi/mutex.rs b/library/std/src/sys/cloudabi/mutex.rs index 1203d8de0c572..9dafcbc1fba0b 100644 --- a/library/std/src/sys/cloudabi/mutex.rs +++ b/library/std/src/sys/cloudabi/mutex.rs @@ -103,7 +103,9 @@ impl ReentrantMutex { }; let mut event = MaybeUninit::::uninit(); let mut nevents = MaybeUninit::::uninit(); - let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr()); + // SAFE: The caller must to ensure that `event` and `nevents` are initialized. + let ret = + unsafe { abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr()) }; assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire mutex"); let event = event.assume_init(); assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire mutex"); From 23a5c214150f462043ab411f87ef297309421d71 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 19 Oct 2020 07:21:41 -0700 Subject: [PATCH 19/36] Fix a typo in a comment. --- library/std/src/sys/unix/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 88693e4786c0f..69f6b88a3bc56 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1068,7 +1068,7 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> { let src = cstr(src)?; let dst = cstr(dst)?; // Use `linkat` with `AT_FDCWD` instead of `link` as `link` leaves it - // implmentation-defined whether it follows symlinks. Pass 0 as the + // implementation-defined whether it follows symlinks. Pass 0 as the // `linkat` flags argument so that we don't follow symlinks. cvt(unsafe { libc::linkat(libc::AT_FDCWD, src.as_ptr(), libc::AT_FDCWD, dst.as_ptr(), 0) })?; Ok(()) From ce00b3e2e0c5c0c88fe59fb45a1c25a8ff9e1836 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 19 Oct 2020 07:47:32 -0700 Subject: [PATCH 20/36] Use `link` on platforms which lack `linkat`. --- library/std/src/sys/unix/fs.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 69f6b88a3bc56..bf4c941928719 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1067,10 +1067,20 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { pub fn link(src: &Path, dst: &Path) -> io::Result<()> { let src = cstr(src)?; let dst = cstr(dst)?; - // Use `linkat` with `AT_FDCWD` instead of `link` as `link` leaves it - // implementation-defined whether it follows symlinks. Pass 0 as the - // `linkat` flags argument so that we don't follow symlinks. - cvt(unsafe { libc::linkat(libc::AT_FDCWD, src.as_ptr(), libc::AT_FDCWD, dst.as_ptr(), 0) })?; + cfg_if::cfg_if! { + if #[cfg(any(target_os = "vxworks", target_os = "redox"))] { + // VxWorks and Redox lack `linkat`, so use `link` instead. POSIX + // leaves it implementation-defined whether `link` follows symlinks, + // so rely on the `symlink_hard_link` test in + // library/std/src/fs/tests.rs to check the behavior. + cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; + } else { + // Use `linkat` with `AT_FDCWD` instead of `link` as `linkat` gives + // us a flag to specify how symlinks should be handled. Pass 0 as + // the flags argument, meaning don't follow symlinks. + cvt(unsafe { libc::linkat(libc::AT_FDCWD, src.as_ptr(), libc::AT_FDCWD, dst.as_ptr(), 0) })?; + } + } Ok(()) } From b62b352f4779072c07c110a63fd83afa9508b9e9 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 19 Oct 2020 10:02:51 -0700 Subject: [PATCH 21/36] Check for exhaustion in RangeInclusive::contains When a range has finished iteration, `is_empty` returns true, so it should also be the case that `contains` returns false. --- library/core/src/ops/range.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 4423cfc27dd17..1da186d9fbb24 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -479,13 +479,23 @@ impl> RangeInclusive { /// assert!(!(0.0..=f32::NAN).contains(&0.0)); /// assert!(!(f32::NAN..=1.0).contains(&1.0)); /// ``` + /// + /// This method always returns `false` after iteration has finished: + /// + /// ``` + /// let mut r = 3..=5; + /// assert!(r.contains(&3) && r.contains(&5)); + /// for _ in r.by_ref() {} + /// // Precise field values are unspecified here + /// assert!(!r.contains(&3) && !r.contains(&5)); + /// ``` #[stable(feature = "range_contains", since = "1.35.0")] pub fn contains(&self, item: &U) -> bool where Idx: PartialOrd, U: ?Sized + PartialOrd, { - >::contains(self, item) + !self.exhausted && >::contains(self, item) } /// Returns `true` if the range contains no items. From 9fd79a39044be777a39604856d0a276484d6480f Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 19 Oct 2020 13:46:30 -0700 Subject: [PATCH 22/36] make exhausted RangeInclusive::end_bound return Excluded(end) --- library/core/src/ops/range.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 1da186d9fbb24..084ddffab0b7a 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -495,7 +495,7 @@ impl> RangeInclusive { Idx: PartialOrd, U: ?Sized + PartialOrd, { - !self.exhausted && >::contains(self, item) + >::contains(self, item) } /// Returns `true` if the range contains no items. @@ -891,7 +891,13 @@ impl RangeBounds for RangeInclusive { Included(&self.start) } fn end_bound(&self) -> Bound<&T> { - Included(&self.end) + if self.exhausted { + // When the iterator is exhausted, we usually have start == end, + // but we want the range to appear empty, containing nothing. + Excluded(&self.end) + } else { + Included(&self.end) + } } } From a9470d0522368f67d8aaa551318e3feb2d18e790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 21 Oct 2020 00:00:00 +0000 Subject: [PATCH 23/36] Simplify assert terminator only if condition evaluates to expected value --- compiler/rustc_mir/src/transform/simplify_branches.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir/src/transform/simplify_branches.rs b/compiler/rustc_mir/src/transform/simplify_branches.rs index 5f63c03993d3a..a9a45e61a38cb 100644 --- a/compiler/rustc_mir/src/transform/simplify_branches.rs +++ b/compiler/rustc_mir/src/transform/simplify_branches.rs @@ -49,9 +49,10 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranches { } TerminatorKind::Assert { target, cond: Operand::Constant(ref c), expected, .. - } if (c.literal.try_eval_bool(tcx, param_env) == Some(true)) == expected => { - TerminatorKind::Goto { target } - } + } => match c.literal.try_eval_bool(tcx, param_env) { + Some(v) if v == expected => TerminatorKind::Goto { target }, + _ => continue, + }, TerminatorKind::FalseEdge { real_target, .. } => { TerminatorKind::Goto { target: real_target } } From 9202fbdbdbd60adb62839c3230738274e30f15fc Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 20 Oct 2020 17:18:08 -0700 Subject: [PATCH 24/36] Check for exhaustion in SliceIndex for RangeInclusive --- library/alloc/tests/str.rs | 29 +++++++++++++++++++++++++++++ library/core/src/ops/range.rs | 14 ++++++++++++++ library/core/src/slice/index.rs | 16 ++++++---------- library/core/src/str/traits.rs | 16 ++++++---------- library/core/tests/slice.rs | 30 ++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 20 deletions(-) diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index ed8ee2d8823c0..834dd4656ff76 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -529,6 +529,13 @@ mod slice_index { message: "out of bounds"; } + in mod rangeinclusive_len { + data: "abcdef"; + good: data[0..=5] == "abcdef"; + bad: data[0..=6]; + message: "out of bounds"; + } + in mod range_len_len { data: "abcdef"; good: data[6..6] == ""; @@ -544,6 +551,28 @@ mod slice_index { } } + panic_cases! { + in mod rangeinclusive_exhausted { + data: "abcdef"; + + good: data[0..=5] == "abcdef"; + good: data[{ + let mut iter = 0..=5; + iter.by_ref().count(); // exhaust it + iter + }] == ""; + + // 0..=6 is out of bounds before exhaustion, so it + // stands to reason that it still would be after. + bad: data[{ + let mut iter = 0..=6; + iter.by_ref().count(); // exhaust it + iter + }]; + message: "out of bounds"; + } + } + panic_cases! { in mod range_neg_width { data: "abcdef"; diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 084ddffab0b7a..1d67e65e51f5f 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -446,6 +446,20 @@ impl RangeInclusive { } } +impl RangeInclusive { + /// Converts to an exclusive `Range` for `SliceIndex` implementations. + /// The caller is responsible for dealing with `end == usize::MAX`. + #[inline] + pub(crate) fn into_slice_range(self) -> Range { + // If we're not exhausted, we want to simply slice `start..end + 1`. + // If we are exhausted, then slicing with `end + 1..end + 1` gives us an + // empty range that is still subject to bounds-checks for that endpoint. + let exclusive_end = self.end + 1; + let start = if self.exhausted { exclusive_end } else { self.start }; + start..exclusive_end + } +} + #[stable(feature = "inclusive_range", since = "1.26.0")] impl fmt::Debug for RangeInclusive { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index f1f21c1d24b0b..660c8a2da5da0 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -376,28 +376,24 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn get(self, slice: &[T]) -> Option<&[T]> { - if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) } } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - if *self.end() == usize::MAX { - None - } else { - (*self.start()..self.end() + 1).get_mut(slice) - } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. - unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) } + unsafe { self.into_slice_range().get_unchecked(slice) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. - unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) } + unsafe { self.into_slice_range().get_unchecked_mut(slice) } } #[inline] @@ -405,7 +401,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { if *self.end() == usize::MAX { slice_end_index_overflow_fail(); } - (*self.start()..self.end() + 1).index(slice) + self.into_slice_range().index(slice) } #[inline] @@ -413,7 +409,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { if *self.end() == usize::MAX { slice_end_index_overflow_fail(); } - (*self.start()..self.end() + 1).index_mut(slice) + self.into_slice_range().index_mut(slice) } } diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 4f8aa246e5232..f363541431100 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -398,39 +398,35 @@ unsafe impl SliceIndex for ops::RangeInclusive { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) } } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if *self.end() == usize::MAX { - None - } else { - (*self.start()..self.end() + 1).get_mut(slice) - } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { // SAFETY: the caller must uphold the safety contract for `get_unchecked`. - unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) } + unsafe { self.into_slice_range().get_unchecked(slice) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`. - unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) } + unsafe { self.into_slice_range().get_unchecked_mut(slice) } } #[inline] fn index(self, slice: &str) -> &Self::Output { if *self.end() == usize::MAX { str_index_overflow_fail(); } - (*self.start()..self.end() + 1).index(slice) + self.into_slice_range().index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { if *self.end() == usize::MAX { str_index_overflow_fail(); } - (*self.start()..self.end() + 1).index_mut(slice) + self.into_slice_range().index_mut(slice) } } diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index ac5c9353ccb46..9ccc5a08dcbea 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -1341,6 +1341,14 @@ mod slice_index { message: "out of range"; } + in mod rangeinclusive_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[0..=5] == [0, 1, 2, 3, 4, 5]; + bad: data[0..=6]; + message: "out of range"; + } + in mod range_len_len { data: [0, 1, 2, 3, 4, 5]; @@ -1358,6 +1366,28 @@ mod slice_index { } } + panic_cases! { + in mod rangeinclusive_exhausted { + data: [0, 1, 2, 3, 4, 5]; + + good: data[0..=5] == [0, 1, 2, 3, 4, 5]; + good: data[{ + let mut iter = 0..=5; + iter.by_ref().count(); // exhaust it + iter + }] == []; + + // 0..=6 is out of range before exhaustion, so it + // stands to reason that it still would be after. + bad: data[{ + let mut iter = 0..=6; + iter.by_ref().count(); // exhaust it + iter + }]; + message: "out of range"; + } + } + panic_cases! { in mod range_neg_width { data: [0, 1, 2, 3, 4, 5]; From d0178b4f99c70d2443f9b76421429d0d23dadc45 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 20 Oct 2020 16:42:31 -0700 Subject: [PATCH 25/36] Make it platform-specific whether `hard_link` follows symlinks. Also mention that where possible, `hard_link` does not follow symlinks. --- library/std/src/fs.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index c611bf4d74a49..c256f556b3c8f 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1701,8 +1701,9 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// The `dst` path will be a link pointing to the `src` path. Note that systems /// often require these two paths to both be located on the same filesystem. /// -/// If `src` names a symbolic link, it is not followed. The created hard link -/// points to the symbolic link itself. +/// If `src` names a symbolic link, it is platform-specific whether the symbolic +/// link is followed. On platforms where it's possible to not follow it, it is +/// not followed, and the created hard link points to the symbolic link itself. /// /// # Platform-specific behavior /// From 40ab18d97dab9df68418d19ef8a40c3218142d5f Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 22 Oct 2020 23:07:48 +0200 Subject: [PATCH 26/36] improve const infer error --- compiler/rustc_middle/src/infer/unify_key.rs | 16 +++++----------- .../ui/const-generics/infer/issue-77092.stderr | 2 +- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index 4d884dde39387..cf5e99845d189 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -176,17 +176,17 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> { type Error = (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>); fn unify_values(value1: &Self, value2: &Self) -> Result { - let (val, span) = match (value1.val, value2.val) { + let (val, origin) = match (value1.val, value2.val) { (ConstVariableValue::Known { .. }, ConstVariableValue::Known { .. }) => { bug!("equating two const variables, both of which have known values") } // If one side is known, prefer that one. (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => { - (value1.val, value1.origin.span) + (value1.val, value1.origin) } (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => { - (value2.val, value2.origin.span) + (value2.val, value2.origin) } // If both sides are *unknown*, it hardly matters, does it? @@ -200,17 +200,11 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> { // universe is the minimum of the two universes, because that is // the one which contains the fewest names in scope. let universe = cmp::min(universe1, universe2); - (ConstVariableValue::Unknown { universe }, value1.origin.span) + (ConstVariableValue::Unknown { universe }, value1.origin) } }; - Ok(ConstVarValue { - origin: ConstVariableOrigin { - kind: ConstVariableOriginKind::ConstInference, - span: span, - }, - val, - }) + Ok(ConstVarValue { origin, val }) } } diff --git a/src/test/ui/const-generics/infer/issue-77092.stderr b/src/test/ui/const-generics/infer/issue-77092.stderr index e84ff8baeea53..63facbf3b8c0f 100644 --- a/src/test/ui/const-generics/infer/issue-77092.stderr +++ b/src/test/ui/const-generics/infer/issue-77092.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/issue-77092.rs:13:26 | LL | println!("{:?}", take_array_from_mut(&mut arr, i)); - | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of the constant `{_: usize}` + | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of const parameter `N` declared on the function `take_array_from_mut` error: aborting due to previous error From 09135e4e758c6fb5a3ca91924a78521f749acda9 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 23 Oct 2020 16:09:17 +0900 Subject: [PATCH 27/36] Add regression test for issue-77475 --- src/test/ui/macros/issue-77475.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/test/ui/macros/issue-77475.rs diff --git a/src/test/ui/macros/issue-77475.rs b/src/test/ui/macros/issue-77475.rs new file mode 100644 index 0000000000000..7b32a33ea4f17 --- /dev/null +++ b/src/test/ui/macros/issue-77475.rs @@ -0,0 +1,10 @@ +// check-pass +// Regression test of #77475, this used to be ICE. + +#![feature(decl_macro)] + +use crate as _; + +pub macro ice(){} + +fn main() {} From e1c524cd457bd02435a47214052320f8d4bfa999 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Fri, 23 Oct 2020 09:33:47 +0200 Subject: [PATCH 28/36] review --- compiler/rustc_middle/src/infer/unify_key.rs | 21 +++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index cf5e99845d189..16e9aafb25a54 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -175,19 +175,15 @@ impl<'tcx> UnifyKey for ty::ConstVid<'tcx> { impl<'tcx> UnifyValue for ConstVarValue<'tcx> { type Error = (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>); - fn unify_values(value1: &Self, value2: &Self) -> Result { - let (val, origin) = match (value1.val, value2.val) { + fn unify_values(&value1: &Self, &value2: &Self) -> Result { + Ok(match (value1.val, value2.val) { (ConstVariableValue::Known { .. }, ConstVariableValue::Known { .. }) => { bug!("equating two const variables, both of which have known values") } // If one side is known, prefer that one. - (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => { - (value1.val, value1.origin) - } - (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => { - (value2.val, value2.origin) - } + (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => value1, + (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => value2, // If both sides are *unknown*, it hardly matters, does it? ( @@ -200,11 +196,12 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> { // universe is the minimum of the two universes, because that is // the one which contains the fewest names in scope. let universe = cmp::min(universe1, universe2); - (ConstVariableValue::Unknown { universe }, value1.origin) + ConstVarValue { + val: ConstVariableValue::Unknown { universe }, + origin: value1.origin, + } } - }; - - Ok(ConstVarValue { origin, val }) + }) } } From 9b90e1762e6cb21baa504a27530b9aa404fbe3ac Mon Sep 17 00:00:00 2001 From: Canop Date: Thu, 1 Oct 2020 11:13:38 +0200 Subject: [PATCH 29/36] add `insert` and `insert_with` to `Option` This removes a cause of `unwrap` and code complexity. This allows replacing ``` option_value = Some(build()); option_value.as_mut().unwrap() ``` with ``` option_value.insert(build()) ``` or ``` option_value.insert_with(build) ``` It's also useful in contexts not requiring the mutability of the reference. Here's a typical cache example: ``` let checked_cache = cache.as_ref().filter(|e| e.is_valid()); let content = match checked_cache { Some(e) => &e.content, None => { cache = Some(compute_cache_entry()); // unwrap is OK because we just filled the option &cache.as_ref().unwrap().content } }; ``` It can be changed into ``` let checked_cache = cache.as_ref().filter(|e| e.is_valid()); let content = match checked_cache { Some(e) => &e.content, None => &cache.insert_with(compute_cache_entry).content, }; ``` --- library/core/src/option.rs | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 825144e5a6fbe..394be746ec2fa 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -562,6 +562,52 @@ impl Option { } } + ///////////////////////////////////////////////////////////////////////// + // Setting a new value + ///////////////////////////////////////////////////////////////////////// + + /// Inserts `v` into the option then returns a mutable reference + /// to the contained value. + /// + /// # Example + /// + /// ``` + /// #![feature(option_insert)] + /// + /// let mut o = None; + /// let v = o.insert(3); + /// assert_eq!(*v, 3); + /// ``` + #[inline] + #[unstable(feature = "option_insert", reason = "new API", issue = "none")] + pub fn insert(&mut self, v: T) -> &mut T { + self.insert_with(|| v) + } + + /// Inserts a value computed from `f` into the option, then returns a + /// mutable reference to the contained value. + /// + /// # Example + /// + /// ``` + /// #![feature(option_insert)] + /// + /// let mut o = None; + /// let v = o.insert_with(|| 3); + /// assert_eq!(*v, 3); + /// ``` + #[inline] + #[unstable(feature = "option_insert", reason = "new API", issue = "none")] + pub fn insert_with T>(&mut self, f: F) -> &mut T { + *self = Some(f()); + + match *self { + Some(ref mut v) => v, + // SAFETY: the code above just filled the option + None => unsafe { hint::unreachable_unchecked() }, + } + } + ///////////////////////////////////////////////////////////////////////// // Iterator constructors ///////////////////////////////////////////////////////////////////////// From e8df2a426959fa3ff4f65eae85e618394bf27e28 Mon Sep 17 00:00:00 2001 From: Canop Date: Thu, 1 Oct 2020 13:24:33 +0200 Subject: [PATCH 30/36] remove `option.insert_with` `option.insert` covers both needs anyway, `insert_with` is redundant. --- library/core/src/option.rs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 394be746ec2fa..64541da300e86 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -581,25 +581,7 @@ impl Option { #[inline] #[unstable(feature = "option_insert", reason = "new API", issue = "none")] pub fn insert(&mut self, v: T) -> &mut T { - self.insert_with(|| v) - } - - /// Inserts a value computed from `f` into the option, then returns a - /// mutable reference to the contained value. - /// - /// # Example - /// - /// ``` - /// #![feature(option_insert)] - /// - /// let mut o = None; - /// let v = o.insert_with(|| 3); - /// assert_eq!(*v, 3); - /// ``` - #[inline] - #[unstable(feature = "option_insert", reason = "new API", issue = "none")] - pub fn insert_with T>(&mut self, f: F) -> &mut T { - *self = Some(f()); + *self = Some(v); match *self { Some(ref mut v) => v, From 60a96cae336b621be3a5e01cf6c87649b327f836 Mon Sep 17 00:00:00 2001 From: Canop Date: Thu, 1 Oct 2020 16:05:01 +0200 Subject: [PATCH 31/36] more tests in option.insert, code cleaning in option Code cleaning made according to suggestions in discussion on PR ##77392 impacts insert, get_or_insert and get_or_insert_with. --- library/core/src/option.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 64541da300e86..65575f4c41bad 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -574,17 +574,22 @@ impl Option { /// ``` /// #![feature(option_insert)] /// - /// let mut o = None; - /// let v = o.insert(3); - /// assert_eq!(*v, 3); + /// let mut opt = None; + /// let val = opt.insert(1); + /// assert_eq!(*val, 1); + /// assert_eq!(opt.unwrap(), 1); + /// let val = opt.insert(2); + /// assert_eq!(*val, 2); + /// *val = 3; + /// assert_eq!(opt.unwrap(), 3); /// ``` #[inline] - #[unstable(feature = "option_insert", reason = "new API", issue = "none")] - pub fn insert(&mut self, v: T) -> &mut T { - *self = Some(v); + #[unstable(feature = "option_insert", reason = "newly added", issue = "none")] + pub fn insert(&mut self, val: T) -> &mut T { + *self = Some(val); - match *self { - Some(ref mut v) => v, + match self { + Some(v) => v, // SAFETY: the code above just filled the option None => unsafe { hint::unreachable_unchecked() }, } @@ -839,8 +844,8 @@ impl Option { /// ``` #[inline] #[stable(feature = "option_entry", since = "1.20.0")] - pub fn get_or_insert(&mut self, v: T) -> &mut T { - self.get_or_insert_with(|| v) + pub fn get_or_insert(&mut self, val: T) -> &mut T { + self.get_or_insert_with(|| val) } /// Inserts a value computed from `f` into the option if it is [`None`], then @@ -867,8 +872,8 @@ impl Option { *self = Some(f()); } - match *self { - Some(ref mut v) => v, + match self { + Some(v) => v, // SAFETY: a `None` variant for `self` would have been replaced by a `Some` // variant in the code above. None => unsafe { hint::unreachable_unchecked() }, From cc8b77a7cf0e1179b148a70c8f760a7ba3c1debd Mon Sep 17 00:00:00 2001 From: Canop Date: Sat, 3 Oct 2020 10:29:11 +0200 Subject: [PATCH 32/36] fix naming unconsistency between function doc and prototype --- library/core/src/option.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 65575f4c41bad..8fd3bfe77b800 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -566,8 +566,7 @@ impl Option { // Setting a new value ///////////////////////////////////////////////////////////////////////// - /// Inserts `v` into the option then returns a mutable reference - /// to the contained value. + /// Inserts `value` into the option then returns a mutable reference to it. /// /// # Example /// @@ -585,8 +584,8 @@ impl Option { /// ``` #[inline] #[unstable(feature = "option_insert", reason = "newly added", issue = "none")] - pub fn insert(&mut self, val: T) -> &mut T { - *self = Some(val); + pub fn insert(&mut self, value: T) -> &mut T { + *self = Some(value); match self { Some(v) => v, @@ -825,7 +824,7 @@ impl Option { // Entry-like operations to insert if None and return a reference ///////////////////////////////////////////////////////////////////////// - /// Inserts `v` into the option if it is [`None`], then + /// Inserts `value` into the option if it is [`None`], then /// returns a mutable reference to the contained value. /// /// # Examples @@ -844,12 +843,12 @@ impl Option { /// ``` #[inline] #[stable(feature = "option_entry", since = "1.20.0")] - pub fn get_or_insert(&mut self, val: T) -> &mut T { - self.get_or_insert_with(|| val) + pub fn get_or_insert(&mut self, value: T) -> &mut T { + self.get_or_insert_with(|| value) } - /// Inserts a value computed from `f` into the option if it is [`None`], then - /// returns a mutable reference to the contained value. + /// Inserts a value computed from `f` into the option if it is [`None`], + /// then returns a mutable reference to the contained value. /// /// # Examples /// From 39557799c763d75b58cbd7235af49a29c4d1212c Mon Sep 17 00:00:00 2001 From: Canop Date: Fri, 23 Oct 2020 11:08:09 +0200 Subject: [PATCH 33/36] Update library/core/src/option.rs Co-authored-by: Mara Bos --- library/core/src/option.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 8fd3bfe77b800..4e8a74d9162a5 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -568,6 +568,8 @@ impl Option { /// Inserts `value` into the option then returns a mutable reference to it. /// + /// If the option already contains a value, the old value is dropped. + /// /// # Example /// /// ``` From 415a8e526d0e99f0e85e3b06bad9d2f2867910c5 Mon Sep 17 00:00:00 2001 From: Canop Date: Fri, 23 Oct 2020 11:09:15 +0200 Subject: [PATCH 34/36] Update library/core/src/option.rs Co-authored-by: Ivan Tham --- library/core/src/option.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 4e8a74d9162a5..a3d20f016fd98 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -562,10 +562,6 @@ impl Option { } } - ///////////////////////////////////////////////////////////////////////// - // Setting a new value - ///////////////////////////////////////////////////////////////////////// - /// Inserts `value` into the option then returns a mutable reference to it. /// /// If the option already contains a value, the old value is dropped. From 216d0fe36466ce9307a643a67afa41ebfb8c43dd Mon Sep 17 00:00:00 2001 From: Canop Date: Fri, 23 Oct 2020 11:44:58 +0200 Subject: [PATCH 35/36] add tracking issue number to option_insert feature gate --- library/core/src/option.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index a3d20f016fd98..3daf26208b937 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -581,7 +581,7 @@ impl Option { /// assert_eq!(opt.unwrap(), 3); /// ``` #[inline] - #[unstable(feature = "option_insert", reason = "newly added", issue = "none")] + #[unstable(feature = "option_insert", reason = "newly added", issue = "78271")] pub fn insert(&mut self, value: T) -> &mut T { *self = Some(value); From efedcb23447a805fad841c4e38d5dea0d53ec3c7 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Fri, 23 Oct 2020 12:13:07 +0100 Subject: [PATCH 36/36] Update description of Empty Enum for accuracy An empty enum is similar to the never type `!`, rather than the unit type `()`. --- library/std/src/keyword_docs.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index a4bbb18da5983..9b704ee9ecab2 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -346,7 +346,7 @@ mod else_keyword {} /// When data follows along with a variant, such as with rust's built-in [`Option`] type, the data /// is added as the type describes, for example `Option::Some(123)`. The same follows with /// struct-like variants, with things looking like `ComplexEnum::LotsOfThings { usual_struct_stuff: -/// true, blah: "hello!".to_string(), }`. Empty Enums are similar to () in that they cannot be +/// true, blah: "hello!".to_string(), }`. Empty Enums are similar to [`!`] in that they cannot be /// instantiated at all, and are used mainly to mess with the type system in interesting ways. /// /// For more information, take a look at the [Rust Book] or the [Reference] @@ -354,6 +354,7 @@ mod else_keyword {} /// [ADT]: https://en.wikipedia.org/wiki/Algebraic_data_type /// [Rust Book]: ../book/ch06-01-defining-an-enum.html /// [Reference]: ../reference/items/enumerations.html +/// [`!`]: primitive.never.html mod enum_keyword {} #[doc(keyword = "extern")]