diff options
| author | Soveu <marx.tomasz@gmail.com> | 2021-03-15 21:51:38 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-03-15 21:51:38 +0100 |
| commit | 96d6f22a8e7f04f2ae0712f7359470a78c77a9e0 (patch) | |
| tree | 1660abccf0df54100751181250e049e830327b3c /library/std/src | |
| parent | 2285f11724e2fa3251c94c9ab7672544099600e2 (diff) | |
| parent | 2ccf06302c08d7d4911aad40e66a9a3ee731c6f9 (diff) | |
Merge branch 'master' into dedup
Diffstat (limited to 'library/std/src')
56 files changed, 919 insertions, 389 deletions
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 27f7191831d..233afa92389 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + #[cfg(test)] mod tests; @@ -842,6 +844,37 @@ where self.base.insert(k, v) } + /// Tries to insert a key-value pair into the map, and returns + /// a mutable reference to the value in the entry. + /// + /// If the map already had this key present, nothing is updated, and + /// an error containing the occupied entry and the value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(map_try_insert)] + /// + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// assert_eq!(map.try_insert(37, "a").unwrap(), &"a"); + /// + /// let err = map.try_insert(37, "b").unwrap_err(); + /// assert_eq!(err.entry.key(), &37); + /// assert_eq!(err.entry.get(), &"a"); + /// assert_eq!(err.value, "b"); + /// ``` + #[unstable(feature = "map_try_insert", issue = "82766")] + pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V>> { + match self.entry(key) { + Occupied(entry) => Err(OccupiedError { entry, value }), + Vacant(entry) => Ok(entry.insert(value)), + } + } + /// Removes a key from the map, returning the value at the key if the key /// was previously in the map. /// @@ -1851,6 +1884,41 @@ impl<K: Debug, V> Debug for VacantEntry<'_, K, V> { } } +/// The error returned by [`try_insert`](HashMap::try_insert) when the key already exists. +/// +/// Contains the occupied entry, and the value that was not inserted. +#[unstable(feature = "map_try_insert", issue = "82766")] +pub struct OccupiedError<'a, K: 'a, V: 'a> { + /// The entry in the map that was already occupied. + pub entry: OccupiedEntry<'a, K, V>, + /// The value which was not inserted, because the entry was already occupied. + pub value: V, +} + +#[unstable(feature = "map_try_insert", issue = "82766")] +impl<K: Debug, V: Debug> Debug for OccupiedError<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedError") + .field("key", self.entry.key()) + .field("old_value", self.entry.get()) + .field("new_value", &self.value) + .finish() + } +} + +#[unstable(feature = "map_try_insert", issue = "82766")] +impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "failed to insert {:?}, key {:?} already exists with value {:?}", + self.value, + self.entry.key(), + self.entry.get(), + ) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S> { type Item = (&'a K, &'a V); diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 467968354e2..819be142227 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -774,11 +774,11 @@ fn test_occupied_entry_key() { let key = "hello there"; let value = "value goes here"; assert!(a.is_empty()); - a.insert(key.clone(), value.clone()); + a.insert(key, value); assert_eq!(a.len(), 1); assert_eq!(a[key], value); - match a.entry(key.clone()) { + match a.entry(key) { Vacant(_) => panic!(), Occupied(e) => assert_eq!(key, *e.key()), } @@ -793,11 +793,11 @@ fn test_vacant_entry_key() { let value = "value goes here"; assert!(a.is_empty()); - match a.entry(key.clone()) { + match a.entry(key) { Occupied(_) => panic!(), Vacant(e) => { assert_eq!(key, *e.key()); - e.insert(value.clone()); + e.insert(value); } } assert_eq!(a.len(), 1); diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 605d953f5da..80c35307d52 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -30,6 +30,7 @@ use crate::mem::transmute; use crate::num; use crate::str; use crate::string; +use crate::sync::Arc; /// `Error` is a trait representing the basic expectations for error values, /// i.e., values of type `E` in [`Result<T, E>`]. Errors must describe @@ -469,6 +470,24 @@ impl Error for char::DecodeUtf16Error { } } +#[unstable(feature = "map_try_insert", issue = "82766")] +impl<'a, K: Debug + Ord, V: Debug> Error + for crate::collections::btree_map::OccupiedError<'a, K, V> +{ + #[allow(deprecated)] + fn description(&self) -> &str { + "key already exists" + } +} + +#[unstable(feature = "map_try_insert", issue = "82766")] +impl<'a, K: Debug, V: Debug> Error for crate::collections::hash_map::OccupiedError<'a, K, V> { + #[allow(deprecated)] + fn description(&self) -> &str { + "key already exists" + } +} + #[stable(feature = "box_error", since = "1.8.0")] impl<T: Error> Error for Box<T> { #[allow(deprecated, deprecated_in_future)] @@ -507,6 +526,27 @@ impl<'a, T: Error + ?Sized> Error for &'a T { } } +#[stable(feature = "arc_error", since = "1.52.0")] +impl<T: Error + ?Sized> Error for Arc<T> { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + Error::description(&**self) + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn Error> { + Error::cause(&**self) + } + + fn source(&self) -> Option<&(dyn Error + 'static)> { + Error::source(&**self) + } + + fn backtrace(&self) -> Option<&Backtrace> { + Error::backtrace(&**self) + } +} + #[stable(feature = "fmt_error", since = "1.11.0")] impl Error for fmt::Error { #[allow(deprecated)] diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index f51b2c24621..c16d27fa1f5 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -503,7 +503,7 @@ impl f32 { unsafe { cmath::fdimf(self, other) } } - /// Returns the cubic root of a number. + /// Returns the cube root of a number. /// /// # Examples /// diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 8c41e448686..4c95df5ffe0 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -505,7 +505,7 @@ impl f64 { unsafe { cmath::fdim(self, other) } } - /// Returns the cubic root of a number. + /// Returns the cube root of a number. /// /// # Examples /// diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index 230ef0b23db..945bfda1b78 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -62,20 +62,18 @@ use crate::sys; /// u8` argument which is not necessarily nul-terminated, plus another /// argument with the length of the string — like C's `strndup()`. /// You can of course get the slice's length with its -/// [`len`][slice.len] method. +/// [`len`][slice::len] method. /// /// If you need a `&[`[`u8`]`]` slice *with* the nul terminator, you /// can use [`CString::as_bytes_with_nul`] instead. /// /// Once you have the kind of slice you need (with or without a nul /// terminator), you can call the slice's own -/// [`as_ptr`][slice.as_ptr] method to get a read-only raw pointer to pass to +/// [`as_ptr`][slice::as_ptr] method to get a read-only raw pointer to pass to /// extern functions. See the documentation for that function for a /// discussion on ensuring the lifetime of the raw pointer. /// /// [`&str`]: prim@str -/// [slice.as_ptr]: ../primitive.slice.html#method.as_ptr -/// [slice.len]: ../primitive.slice.html#method.len /// [`Deref`]: ops::Deref /// [`&CStr`]: CStr /// diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 5bb3f6bdcfd..ce52ffc0241 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -5,6 +5,7 @@ use crate::borrow::{Borrow, Cow}; use crate::cmp; use crate::fmt; use crate::hash::{Hash, Hasher}; +use crate::iter::{Extend, FromIterator}; use crate::ops; use crate::rc::Rc; use crate::str::FromStr; @@ -71,6 +72,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// [`CStr`]: crate::ffi::CStr /// [conversions]: super#conversions #[derive(Clone)] +#[cfg_attr(not(test), rustc_diagnostic_item = "OsString")] #[stable(feature = "rust1", since = "1.0.0")] pub struct OsString { inner: Buf, @@ -93,6 +95,7 @@ impl crate::sealed::Sealed for OsString {} /// /// [`&str`]: str /// [conversions]: super#conversions +#[cfg_attr(not(test), rustc_diagnostic_item = "OsStr")] #[stable(feature = "rust1", since = "1.0.0")] // FIXME: // `OsStr::from_inner` current implementation relies @@ -1190,3 +1193,88 @@ impl FromStr for OsString { Ok(OsString::from(s)) } } + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl Extend<OsString> for OsString { + #[inline] + fn extend<T: IntoIterator<Item = OsString>>(&mut self, iter: T) { + for s in iter { + self.push(&s); + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> Extend<&'a OsStr> for OsString { + #[inline] + fn extend<T: IntoIterator<Item = &'a OsStr>>(&mut self, iter: T) { + for s in iter { + self.push(s); + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> Extend<Cow<'a, OsStr>> for OsString { + #[inline] + fn extend<T: IntoIterator<Item = Cow<'a, OsStr>>>(&mut self, iter: T) { + for s in iter { + self.push(&s); + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl FromIterator<OsString> for OsString { + #[inline] + fn from_iter<I: IntoIterator<Item = OsString>>(iter: I) -> Self { + let mut iterator = iter.into_iter(); + + // Because we're iterating over `OsString`s, we can avoid at least + // one allocation by getting the first string from the iterator + // and appending to it all the subsequent strings. + match iterator.next() { + None => OsString::new(), + Some(mut buf) => { + buf.extend(iterator); + buf + } + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> FromIterator<&'a OsStr> for OsString { + #[inline] + fn from_iter<I: IntoIterator<Item = &'a OsStr>>(iter: I) -> Self { + let mut buf = Self::new(); + for s in iter { + buf.push(s); + } + buf + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> FromIterator<Cow<'a, OsStr>> for OsString { + #[inline] + fn from_iter<I: IntoIterator<Item = Cow<'a, OsStr>>>(iter: I) -> Self { + let mut iterator = iter.into_iter(); + + // Because we're iterating over `OsString`s, we can avoid at least + // one allocation by getting the first owned string from the iterator + // and appending to it all the subsequent strings. + match iterator.next() { + None => OsString::new(), + Some(Cow::Owned(mut buf)) => { + buf.extend(iterator); + buf + } + Some(Cow::Borrowed(buf)) => { + let mut buf = OsString::from(buf); + buf.extend(iterator); + buf + } + } + } +} diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 987371f50ec..02b0fc0c57d 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -1,6 +1,8 @@ use crate::cmp; use crate::fmt; -use crate::io::{self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE}; +use crate::io::{ + self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE, +}; /// The `BufReader<R>` struct adds buffering to any reader. /// @@ -90,10 +92,9 @@ impl<R: Read> BufReader<R> { #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> { unsafe { - let mut buffer = Vec::with_capacity(capacity); - buffer.set_len(capacity); - inner.initializer().initialize(&mut buffer); - BufReader { inner, buf: buffer.into_boxed_slice(), pos: 0, cap: 0 } + let mut buf = Box::new_uninit_slice(capacity).assume_init(); + inner.initializer().initialize(&mut buf); + BufReader { inner, buf, pos: 0, cap: 0 } } } } @@ -435,3 +436,9 @@ impl<R: Seek> Seek for BufReader<R> { }) } } + +impl<T> SizeHint for BufReader<T> { + fn lower_bound(&self) -> usize { + self.buffer().len() + } +} diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index 3780f2044cb..eb60df214c4 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -106,7 +106,7 @@ impl<I: Write> BufferedCopySpec for BufWriter<I> { Ok(0) => return Ok(len), // EOF reached Ok(bytes_read) => { assert!(bytes_read <= spare_cap.len()); - // Safety: The initializer contract guarantees that either it or `read` + // SAFETY: The initializer contract guarantees that either it or `read` // will have initialized these bytes. And we just checked that the number // of bytes is within the buffer capacity. unsafe { buf.set_len(buf.len() + bytes_read) }; diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 00bf8b9af73..9870cfc4c95 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests; +use crate::alloc::Allocator; use crate::cmp; use crate::fmt; use crate::io::{ @@ -357,7 +358,7 @@ impl Write for &mut [u8] { /// Write is implemented for `Vec<u8>` by appending to the vector. /// The vector will grow as needed. #[stable(feature = "rust1", since = "1.0.0")] -impl Write for Vec<u8> { +impl<A: Allocator> Write for Vec<u8, A> { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.extend_from_slice(buf); diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index db3b0e2628f..17002e3b860 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -482,7 +482,7 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [ /// } /// ``` /// -/// Read from [`&str`] because [`&[u8]`][slice] implements `Read`: +/// Read from [`&str`] because [`&[u8]`][prim@slice] implements `Read`: /// /// ```no_run /// # use std::io; @@ -504,7 +504,6 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [ /// [`&str`]: prim@str /// [`std::io`]: self /// [`File`]: crate::fs::File -/// [slice]: ../../std/primitive.slice.html #[stable(feature = "rust1", since = "1.0.0")] #[doc(spotlight)] pub trait Read { @@ -2239,6 +2238,19 @@ impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> { } } +impl<T, U> SizeHint for Chain<T, U> { + fn lower_bound(&self) -> usize { + SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second) + } + + fn upper_bound(&self) -> Option<usize> { + match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) { + (Some(first), Some(second)) => Some(first + second), + _ => None, + } + } +} + /// Reader adaptor which limits the bytes read from an underlying reader. /// /// This struct is generally created by calling [`take`] on a reader. @@ -2465,6 +2477,30 @@ impl<R: Read> Iterator for Bytes<R> { }; } } + + fn size_hint(&self) -> (usize, Option<usize>) { + SizeHint::size_hint(&self.inner) + } +} + +trait SizeHint { + fn lower_bound(&self) -> usize; + + fn upper_bound(&self) -> Option<usize>; + + fn size_hint(&self) -> (usize, Option<usize>) { + (self.lower_bound(), self.upper_bound()) + } +} + +impl<T> SizeHint for T { + default fn lower_bound(&self) -> usize { + 0 + } + + default fn upper_bound(&self) -> Option<usize> { + None + } } /// An iterator over the contents of an instance of `BufRead` split on a diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 1160011f352..1e72c9e0611 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -231,7 +231,7 @@ pub struct Stdin { inner: &'static Mutex<BufReader<StdinRaw>>, } -/// A locked reference to the `Stdin` handle. +/// A locked reference to the [`Stdin`] handle. /// /// This handle implements both the [`Read`] and [`BufRead`] traits, and /// is constructed via the [`Stdin::lock`] method. @@ -251,8 +251,8 @@ pub struct Stdin { /// let mut buffer = String::new(); /// let stdin = io::stdin(); // We get `Stdin` here. /// { -/// let mut stdin_lock = stdin.lock(); // We get `StdinLock` here. -/// stdin_lock.read_to_string(&mut buffer)?; +/// let mut handle = stdin.lock(); // We get `StdinLock` here. +/// handle.read_to_string(&mut buffer)?; /// } // `StdinLock` is dropped here. /// Ok(()) /// } @@ -494,10 +494,10 @@ pub struct Stdout { inner: Pin<&'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>, } -/// A locked reference to the `Stdout` handle. +/// A locked reference to the [`Stdout`] handle. /// /// This handle implements the [`Write`] trait, and is constructed via -/// the [`Stdout::lock`] method. +/// the [`Stdout::lock`] method. See its documentation for more. /// /// ### Note: Windows Portability Consideration /// When operating in a console, the Windows implementation of this stream does not support @@ -708,10 +708,10 @@ pub struct Stderr { inner: Pin<&'static ReentrantMutex<RefCell<StderrRaw>>>, } -/// A locked reference to the `Stderr` handle. +/// A locked reference to the [`Stderr`] handle. /// -/// This handle implements the `Write` trait and is constructed via -/// the [`Stderr::lock`] method. +/// This handle implements the [`Write`] trait and is constructed via +/// the [`Stderr::lock`] method. See its documentation for more. /// /// ### Note: Windows Portability Consideration /// When operating in a console, the Windows implementation of this stream does not support diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index f176c2f088c..a85dd0d9827 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -1,7 +1,7 @@ use super::{repeat, Cursor, SeekFrom}; use crate::cmp::{self, min}; use crate::io::{self, IoSlice, IoSliceMut}; -use crate::io::{BufRead, Read, Seek, Write}; +use crate::io::{BufRead, BufReader, Read, Seek, Write}; use crate::ops::Deref; #[test] @@ -199,6 +199,53 @@ fn chain_bufread() { } #[test] +fn bufreader_size_hint() { + let testdata = b"ABCDEFGHIJKL"; + let mut buf_reader = BufReader::new(&testdata[..]); + assert_eq!(buf_reader.buffer().len(), 0); + + let buffer_length = testdata.len(); + buf_reader.fill_buf().unwrap(); + + // Check that size hint matches buffer contents + let mut buffered_bytes = buf_reader.bytes(); + let (lower_bound, _upper_bound) = buffered_bytes.size_hint(); + assert_eq!(lower_bound, buffer_length); + + // Check that size hint matches buffer contents after advancing + buffered_bytes.next().unwrap().unwrap(); + let (lower_bound, _upper_bound) = buffered_bytes.size_hint(); + assert_eq!(lower_bound, buffer_length - 1); +} + +#[test] +fn empty_size_hint() { + let size_hint = io::empty().bytes().size_hint(); + assert_eq!(size_hint, (0, Some(0))); +} + +#[test] +fn chain_empty_size_hint() { + let chain = io::empty().chain(io::empty()); + let size_hint = chain.bytes().size_hint(); + assert_eq!(size_hint, (0, Some(0))); +} + +#[test] +fn chain_size_hint() { + let testdata = b"ABCDEFGHIJKL"; + let mut buf_reader_1 = BufReader::new(&testdata[..6]); + let mut buf_reader_2 = BufReader::new(&testdata[6..]); + + buf_reader_1.fill_buf().unwrap(); + buf_reader_2.fill_buf().unwrap(); + + let chain = buf_reader_1.chain(buf_reader_2); + let size_hint = chain.bytes().size_hint(); + assert_eq!(size_hint, (testdata.len(), None)); +} + +#[test] fn chain_zero_length_read_is_not_eof() { let a = b"A"; let b = b"B"; diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index e43ce4cdb4b..f472361f916 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -4,7 +4,9 @@ mod tests; use crate::fmt; -use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; +use crate::io::{ + self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, +}; /// A reader which is always at EOF. /// @@ -80,6 +82,12 @@ impl fmt::Debug for Empty { } } +impl SizeHint for Empty { + fn upper_bound(&self) -> Option<usize> { + Some(0) + } +} + /// A reader which yields one byte over and over and over and over and over and... /// /// This struct is generally created by calling [`repeat()`]. Please diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 6b06539a094..383eaf2e3a2 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -42,7 +42,7 @@ /// [Reference]: ../reference/expressions/operator-expr.html#type-cast-expressions /// [`crate`]: keyword.crate.html /// [`use`]: keyword.use.html -/// [const-cast]: primitive.pointer.html#method.cast +/// [const-cast]: pointer::cast /// [mut-cast]: primitive.pointer.html#method.cast-1 mod as_keyword {} @@ -181,9 +181,8 @@ mod break_keyword {} /// The `const` keyword is also used in raw pointers in combination with `mut`, as seen in `*const /// T` and `*mut T`. More about `const` as used in raw pointers can be read at the Rust docs for the [pointer primitive]. /// -/// [pointer primitive]: primitive.pointer.html -/// [Rust Book]: -/// ../book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants +/// [pointer primitive]: pointer +/// [Rust Book]: ../book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants /// [Reference]: ../reference/items/constant-items.html /// [const-eval]: ../reference/const_eval.html mod const_keyword {} @@ -371,7 +370,6 @@ 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")] diff --git a/library/std/src/lazy.rs b/library/std/src/lazy.rs index 68f57958bb2..974851a8bd6 100644 --- a/library/std/src/lazy.rs +++ b/library/std/src/lazy.rs @@ -177,7 +177,10 @@ impl<T> SyncOnceCell<T> { /// Sets the contents of this cell to `value`. /// - /// Returns `Ok(())` if the cell's value was updated. + /// May block if another thread is currently attempting to initialize the cell. The cell is + /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// + /// Returns `Ok(())` if the cell's value was set by this call. /// /// # Examples /// @@ -440,13 +443,17 @@ impl<T> SyncOnceCell<T> { res } - /// Safety: The value must be initialized + /// # Safety + /// + /// The value must be initialized unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.is_initialized()); (&*self.value.get()).assume_init_ref() } - /// Safety: The value must be initialized + /// # Safety + /// + /// The value must be initialized unsafe fn get_unchecked_mut(&mut self) -> &mut T { debug_assert!(self.is_initialized()); (&mut *self.value.get()).assume_init_mut() @@ -456,7 +463,7 @@ impl<T> SyncOnceCell<T> { unsafe impl<#[may_dangle] T> Drop for SyncOnceCell<T> { fn drop(&mut self) { if self.is_initialized() { - // Safety: The cell is initialized and being dropped, so it can't + // SAFETY: The cell is initialized and being dropped, so it can't // be accessed again. We also don't touch the `T` other than // dropping it, which validates our usage of #[may_dangle]. unsafe { (&mut *self.value.get()).assume_init_drop() }; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index bc9b4702941..8149858e103 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -61,14 +61,14 @@ //! type, but not the all-important methods. //! //! So for example there is a [page for the primitive type -//! `i32`](primitive.i32.html) that lists all the methods that can be called on +//! `i32`](primitive::i32) that lists all the methods that can be called on //! 32-bit integers (very useful), and there is a [page for the module //! `std::i32`] that documents the constant values [`MIN`] and [`MAX`] (rarely //! useful). //! -//! Note the documentation for the primitives [`str`] and [`[T]`][slice] (also +//! Note the documentation for the primitives [`str`] and [`[T]`][prim@slice] (also //! called 'slice'). Many method calls on [`String`] and [`Vec<T>`] are actually -//! calls to methods on [`str`] and [`[T]`][slice] respectively, via [deref +//! calls to methods on [`str`] and [`[T]`][prim@slice] respectively, via [deref //! coercions][deref-coercions]. //! //! Third, the standard library defines [The Rust Prelude], a small collection @@ -111,8 +111,8 @@ //! regions of memory: //! //! * [`Vec<T>`] - A heap-allocated *vector* that is resizable at runtime. -//! * [`[T; n]`][array] - An inline *array* with a fixed size at compile time. -//! * [`[T]`][slice] - A dynamically sized *slice* into any other kind of contiguous +//! * [`[T; N]`][prim@array] - An inline *array* with a fixed size at compile time. +//! * [`[T]`][prim@slice] - A dynamically sized *slice* into any other kind of contiguous //! storage, whether heap-allocated or not. //! //! Slices can only be handled through some kind of *pointer*, and as such come @@ -185,8 +185,8 @@ //! [other]: #what-is-in-the-standard-library-documentation //! [primitive types]: ../book/ch03-02-data-types.html //! [rust-discord]: https://discord.gg/rust-lang -#![cfg_attr(not(bootstrap), doc = "[array]: prim@array")] -#![cfg_attr(not(bootstrap), doc = "[slice]: prim@slice")] +//! [array]: prim@array +//! [slice]: prim@slice #![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] #![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))] #![doc( @@ -228,11 +228,13 @@ #![feature(arbitrary_self_types)] #![feature(array_error_internals)] #![feature(asm)] +#![feature(assert_matches)] #![feature(associated_type_bounds)] #![feature(atomic_mut_ptr)] #![feature(box_syntax)] #![feature(c_variadic)] #![feature(cfg_accessible)] +#![cfg_attr(not(bootstrap), feature(cfg_eval))] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] @@ -263,7 +265,7 @@ #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] #![feature(extend_one)] -#![feature(external_doc)] +#![feature(extended_key_value_attributes)] #![feature(fn_traits)] #![feature(format_args_nl)] #![feature(gen_future)] @@ -275,11 +277,13 @@ #![feature(int_error_matching)] #![feature(integer_atomics)] #![feature(into_future)] +#![feature(intra_doc_pointers)] #![feature(lang_items)] #![feature(link_args)] #![feature(linkage)] #![feature(llvm_asm)] #![feature(log_syntax)] +#![feature(map_try_insert)] #![feature(maybe_uninit_extra)] #![feature(maybe_uninit_ref)] #![feature(maybe_uninit_slice)] @@ -288,6 +292,7 @@ #![feature(needs_panic_runtime)] #![feature(negative_impls)] #![feature(never_type)] +#![feature(new_uninit)] #![feature(nll)] #![feature(nonnull_slice_from_raw_parts)] #![feature(once_cell)] @@ -297,6 +302,7 @@ #![feature(panic_internals)] #![feature(panic_unwind)] #![feature(pin_static_ref)] +#![feature(prelude_2021)] #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(raw)] @@ -313,7 +319,6 @@ #![feature(stdsimd)] #![feature(stmt_expr_attributes)] #![feature(str_internals)] -#![feature(str_split_once)] #![feature(test)] #![feature(thread_local)] #![feature(thread_local_internals)] @@ -323,7 +328,7 @@ #![feature(try_blocks)] #![feature(try_reserve)] #![feature(unboxed_closures)] -#![feature(unsafe_block_in_unsafe_fn)] +#![cfg_attr(bootstrap, feature(unsafe_block_in_unsafe_fn))] #![feature(unsafe_cell_raw_get)] #![feature(unwind_attributes)] #![feature(vec_into_raw_parts)] @@ -391,7 +396,11 @@ pub use alloc_crate::vec; #[stable(feature = "rust1", since = "1.0.0")] pub use core::any; #[stable(feature = "simd_arch", since = "1.27.0")] -#[doc(no_inline)] +// The `no_inline`-attribute is required to make the documentation of all +// targets available. +// See https://github.com/rust-lang/rust/pull/57808#issuecomment-457390549 for +// more information. +#[doc(no_inline)] // Note (#82861): required for correct documentation pub use core::arch; #[stable(feature = "core_array", since = "1.36.0")] pub use core::array; @@ -550,8 +559,8 @@ pub use std_detect::detect; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::{ - assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, r#try, todo, - unimplemented, unreachable, write, writeln, + assert_eq, assert_matches, assert_ne, debug_assert, debug_assert_eq, debug_assert_matches, + debug_assert_ne, matches, r#try, todo, unimplemented, unreachable, write, writeln, }; // Re-export built-in macros defined through libcore. diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 0ce6542cb72..b729349cf53 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -4,22 +4,7 @@ //! library. Each macro is available for use when linking against the standard //! library. -#[cfg(bootstrap)] -#[doc(include = "../../core/src/macros/panic.md")] -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow_internal_unstable(libstd_sys_internals)] -#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")] -macro_rules! panic { - () => ({ $crate::panic!("explicit panic") }); - ($msg:expr $(,)?) => ({ $crate::rt::begin_panic($msg) }); - ($fmt:expr, $($arg:tt)+) => ({ - $crate::rt::begin_panic_fmt(&$crate::format_args!($fmt, $($arg)+)) - }); -} - -#[cfg(not(bootstrap))] -#[doc(include = "../../core/src/macros/panic.md")] +#[doc = include_str!("../../core/src/macros/panic.md")] #[macro_export] #[rustc_builtin_macro = "std_panic"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index fd6ee088e96..b95511e43d8 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -3,40 +3,48 @@ #![stable(feature = "os", since = "1.0.0")] #![allow(missing_docs, nonstandard_style, missing_debug_implementations)] -cfg_if::cfg_if! { - if #[cfg(doc)] { +// When documenting libstd we want to show unix/windows/linux/wasi modules as these are the "main +// modules" that are used across platforms, so all modules are enabled when `cfg(doc)` is set. +// This should help show platform-specific functionality in a hopefully cross-platform way in the +// documentation. +// Note that we deliberately avoid `cfg_if!` here to work around a rust-analyzer bug that would make +// `std::os` submodules unusable: https://github.com/rust-analyzer/rust-analyzer/issues/6038 - // When documenting libstd we want to show unix/windows/linux modules as - // these are the "main modules" that are used across platforms. This - // should help show platform-specific functionality in a hopefully - // cross-platform way in the documentation +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::sys::unix_ext as unix; - #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::unix_ext as unix; +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::sys::windows_ext as windows; - #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::windows_ext as windows; +#[cfg(doc)] +#[doc(cfg(target_os = "linux"))] +pub mod linux; - #[doc(cfg(target_os = "linux"))] - pub mod linux; - } else { +#[cfg(doc)] +#[stable(feature = "wasi_ext_doc", since = "1.35.0")] +pub use crate::sys::wasi_ext as wasi; - // If we're not documenting libstd then we just expose the main modules - // as we otherwise would. +// If we're not documenting libstd then we just expose the main modules as we otherwise would. - #[cfg(any(target_os = "redox", unix, target_os = "vxworks", target_os = "hermit"))] - #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::ext as unix; +#[cfg(not(doc))] +#[cfg(any(unix, target_os = "hermit"))] +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::sys::ext as unix; - #[cfg(windows)] - #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::ext as windows; +#[cfg(not(doc))] +#[cfg(windows)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::sys::ext as windows; - #[cfg(any(target_os = "linux", target_os = "l4re"))] - pub mod linux; +#[cfg(not(doc))] +#[cfg(any(target_os = "linux", target_os = "l4re"))] +pub mod linux; - } -} +#[cfg(not(doc))] +#[cfg(target_os = "wasi")] +pub mod wasi; #[cfg(target_os = "android")] pub mod android; @@ -68,7 +76,5 @@ pub mod redox; pub mod solaris; #[cfg(target_os = "vxworks")] pub mod vxworks; -#[cfg(target_os = "wasi")] -pub mod wasi; pub mod raw; diff --git a/library/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs index 16272aa0571..50464a050c7 100644 --- a/library/std/src/os/raw/mod.rs +++ b/library/std/src/os/raw/mod.rs @@ -11,7 +11,41 @@ #[cfg(test)] mod tests; -#[doc(include = "char.md")] +use core::num::*; + +macro_rules! type_alias_no_nz { + { + $Docfile:tt, $Alias:ident = $Real:ty; + $( $Cfg:tt )* + } => { + #[doc = include_str!($Docfile)] + $( $Cfg )* + #[stable(feature = "raw_os", since = "1.1.0")] + pub type $Alias = $Real; + } +} + +// To verify that the NonZero types in this file's macro invocations correspond +// +// perl -n < library/std/src/os/raw/mod.rs -e 'next unless m/type_alias\!/; die "$_ ?" unless m/, (c_\w+) = (\w+), NonZero_(\w+) = NonZero(\w+)/; die "$_ ?" unless $3 eq $1 and $4 eq ucfirst $2' +// +// NB this does not check that the main c_* types are right. + +macro_rules! type_alias { + { + $Docfile:tt, $Alias:ident = $Real:ty, $NZAlias:ident = $NZReal:ty; + $( $Cfg:tt )* + } => { + type_alias_no_nz! { $Docfile, $Alias = $Real; $( $Cfg )* } + + #[doc = concat!("Type alias for `NonZero` version of [`", stringify!($Alias), "`]")] + #[unstable(feature = "raw_os_nonzero", issue = "82363")] + $( $Cfg )* + pub type $NZAlias = $NZReal; + } +} + +type_alias! { "char.md", c_char = u8, NonZero_c_char = NonZeroU8; #[cfg(any( all( target_os = "linux", @@ -52,10 +86,8 @@ mod tests; ) ), all(target_os = "fuchsia", target_arch = "aarch64") -))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_char = u8; -#[doc(include = "char.md")] +))]} +type_alias! { "char.md", c_char = i8, NonZero_c_char = NonZeroI8; #[cfg(not(any( all( target_os = "linux", @@ -96,55 +128,25 @@ pub type c_char = u8; ) ), all(target_os = "fuchsia", target_arch = "aarch64") -)))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_char = i8; -#[doc(include = "schar.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_schar = i8; -#[doc(include = "uchar.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_uchar = u8; -#[doc(include = "short.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_short = i16; -#[doc(include = "ushort.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_ushort = u16; -#[doc(include = "int.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_int = i32; -#[doc(include = "uint.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_uint = u32; -#[doc(include = "long.md")] -#[cfg(any(target_pointer_width = "32", windows))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_long = i32; -#[doc(include = "ulong.md")] -#[cfg(any(target_pointer_width = "32", windows))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_ulong = u32; -#[doc(include = "long.md")] -#[cfg(all(target_pointer_width = "64", not(windows)))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_long = i64; -#[doc(include = "ulong.md")] -#[cfg(all(target_pointer_width = "64", not(windows)))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_ulong = u64; -#[doc(include = "longlong.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_longlong = i64; -#[doc(include = "ulonglong.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_ulonglong = u64; -#[doc(include = "float.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_float = f32; -#[doc(include = "double.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_double = f64; +)))]} +type_alias! { "schar.md", c_schar = i8, NonZero_c_schar = NonZeroI8; } +type_alias! { "uchar.md", c_uchar = u8, NonZero_c_uchar = NonZeroU8; } +type_alias! { "short.md", c_short = i16, NonZero_c_short = NonZeroI16; } +type_alias! { "ushort.md", c_ushort = u16, NonZero_c_ushort = NonZeroU16; } +type_alias! { "int.md", c_int = i32, NonZero_c_int = NonZeroI32; } +type_alias! { "uint.md", c_uint = u32, NonZero_c_uint = NonZeroU32; } +type_alias! { "long.md", c_long = i32, NonZero_c_long = NonZeroI32; +#[cfg(any(target_pointer_width = "32", windows))] } +type_alias! { "ulong.md", c_ulong = u32, NonZero_c_ulong = NonZeroU32; +#[cfg(any(target_pointer_width = "32", windows))] } +type_alias! { "long.md", c_long = i64, NonZero_c_long = NonZeroI64; +#[cfg(all(target_pointer_width = "64", not(windows)))] } +type_alias! { "ulong.md", c_ulong = u64, NonZero_c_ulong = NonZeroU64; +#[cfg(all(target_pointer_width = "64", not(windows)))] } +type_alias! { "longlong.md", c_longlong = i64, NonZero_c_longlong = NonZeroI64; } +type_alias! { "ulonglong.md", c_ulonglong = u64, NonZero_c_ulonglong = NonZeroU64; } +type_alias_no_nz! { "float.md", c_float = f32; } +type_alias_no_nz! { "double.md", c_double = f64; } #[stable(feature = "raw_os", since = "1.1.0")] #[doc(no_inline)] diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 1889e549338..de3b57df44e 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1066,6 +1066,7 @@ impl FusedIterator for Ancestors<'_> {} /// /// Which method works best depends on what kind of situation you're in. #[derive(Clone)] +#[cfg_attr(not(test), rustc_diagnostic_item = "PathBuf")] #[stable(feature = "rust1", since = "1.0.0")] // FIXME: // `PathBuf::as_mut_vec` current implementation relies @@ -1719,6 +1720,7 @@ impl AsRef<OsStr> for PathBuf { /// let extension = path.extension(); /// assert_eq!(extension, Some(OsStr::new("txt"))); /// ``` +#[cfg_attr(not(test), rustc_diagnostic_item = "Path")] #[stable(feature = "rust1", since = "1.0.0")] // FIXME: // `Path::new` current implementation relies @@ -2321,7 +2323,9 @@ impl Path { } /// Returns an object that implements [`Display`] for safely printing paths - /// that may contain non-Unicode data. + /// that may contain non-Unicode data. This may perform lossy conversion, + /// depending on the platform. If you would like an implementation which + /// escapes the path please use [`Debug`] instead. /// /// [`Display`]: fmt::Display /// @@ -2555,7 +2559,9 @@ impl fmt::Debug for Path { /// /// A [`Path`] might contain non-Unicode data. This `struct` implements the /// [`Display`] trait in a way that mitigates that. It is created by the -/// [`display`](Path::display) method on [`Path`]. +/// [`display`](Path::display) method on [`Path`]. This may perform lossy +/// conversion, depending on the platform. If you would like an implementation +/// which escapes the path please use [`Debug`] instead. /// /// # Examples /// diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index eb2095b8196..505b5f3013b 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -84,3 +84,37 @@ #![stable(feature = "rust1", since = "1.0.0")] pub mod v1; + +/// The 2015 version of the prelude of The Rust Standard Library. +/// +/// See the [module-level documentation](self) for more. +#[unstable(feature = "prelude_2015", issue = "none")] +pub mod rust_2015 { + #[unstable(feature = "prelude_2015", issue = "none")] + #[doc(no_inline)] + pub use super::v1::*; +} + +/// The 2018 version of the prelude of The Rust Standard Library. +/// +/// See the [module-level documentation](self) for more. +#[unstable(feature = "prelude_2018", issue = "none")] +pub mod rust_2018 { + #[unstable(feature = "prelude_2018", issue = "none")] + #[doc(no_inline)] + pub use super::v1::*; +} + +/// The 2021 version of the prelude of The Rust Standard Library. +/// +/// See the [module-level documentation](self) for more. +#[unstable(feature = "prelude_2021", issue = "none")] +pub mod rust_2021 { + #[unstable(feature = "prelude_2021", issue = "none")] + #[doc(no_inline)] + pub use super::v1::*; + + #[unstable(feature = "prelude_2021", issue = "none")] + #[doc(no_inline)] + pub use core::prelude::rust_2021::*; +} diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index ef9aec54a4c..ec89bb6d2a4 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -1,6 +1,6 @@ //! The first version of the prelude of The Rust Standard Library. //! -//! See the [module-level documentation](../index.html) for more. +//! See the [module-level documentation](super) for more. #![stable(feature = "rust1", since = "1.0.0")] @@ -67,6 +67,15 @@ pub use core::prelude::v1::derive; #[doc(hidden)] pub use core::prelude::v1::cfg_accessible; +#[cfg(not(bootstrap))] +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +#[doc(hidden)] +pub use core::prelude::v1::cfg_eval; + // The file so far is equivalent to src/libcore/prelude/v1.rs, // and below to src/liballoc/prelude.rs. // Those files are duplicated rather than using glob imports diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 876b2b8a3f6..d4bb2083d00 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -468,8 +468,8 @@ mod prim_unit {} /// /// [`null`]: ptr::null /// [`null_mut`]: ptr::null_mut -/// [`is_null`]: ../std/primitive.pointer.html#method.is_null -/// [`offset`]: ../std/primitive.pointer.html#method.offset +/// [`is_null`]: pointer::is_null +/// [`offset`]: pointer::offset /// [`into_raw`]: Box::into_raw /// [`drop`]: mem::drop /// [`write`]: ptr::write @@ -564,7 +564,7 @@ mod prim_pointer {} /// move_away(roa); /// ``` /// -/// [slice]: primitive.slice.html +/// [slice]: prim@slice /// [`Debug`]: fmt::Debug /// [`Hash`]: hash::Hash /// [`Borrow`]: borrow::Borrow diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 6480e654c55..f9cfd11e906 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -71,11 +71,15 @@ //! .spawn() //! .expect("failed to execute child"); //! -//! { -//! // limited borrow of stdin -//! let stdin = child.stdin.as_mut().expect("failed to get stdin"); +//! // If the child process fills its stdout buffer, it may end up +//! // waiting until the parent reads the stdout, and not be able to +//! // read stdin in the meantime, causing a deadlock. +//! // Writing from another thread ensures that stdout is being read +//! // at the same time, avoiding the problem. +//! let mut stdin = child.stdin.take().expect("failed to get stdin"); +//! std::thread::spawn(move || { //! stdin.write_all(b"test").expect("failed to write to stdin"); -//! } +//! }); //! //! let output = child //! .wait_with_output() @@ -885,7 +889,7 @@ impl Command { } /// Executes a command as a child process, waiting for it to finish and - /// collecting its exit status. + /// collecting its status. /// /// By default, stdin, stdout and stderr are inherited from the parent. /// @@ -899,7 +903,7 @@ impl Command { /// .status() /// .expect("failed to execute process"); /// - /// println!("process exited with: {}", status); + /// println!("process finished with: {}", status); /// /// assert!(status.success()); /// ``` @@ -1145,14 +1149,21 @@ impl Stdio { /// .spawn() /// .expect("Failed to spawn child process"); /// - /// { - /// let stdin = child.stdin.as_mut().expect("Failed to open stdin"); + /// let mut stdin = child.stdin.take().expect("Failed to open stdin"); + /// std::thread::spawn(move || { /// stdin.write_all("Hello, world!".as_bytes()).expect("Failed to write to stdin"); - /// } + /// }); /// /// let output = child.wait_with_output().expect("Failed to read stdout"); /// assert_eq!(String::from_utf8_lossy(&output.stdout), "!dlrow ,olleH"); /// ``` + /// + /// Writing more than a pipe buffer's worth of input to stdin without also reading + /// stdout and stderr at the same time may cause a deadlock. + /// This is an issue when running any program that doesn't guarantee that it reads + /// its entire stdin before writing more than a pipe buffer's worth of output. + /// The size of a pipe buffer varies on different targets. + /// #[stable(feature = "process", since = "1.0.0")] pub fn piped() -> Stdio { Stdio(imp::Stdio::MakePipe) @@ -1368,11 +1379,17 @@ impl From<fs::File> for Stdio { /// Describes the result of a process after it has terminated. /// -/// This `struct` is used to represent the exit status of a child process. +/// This `struct` is used to represent the exit status or other termination of a child process. /// Child processes are created via the [`Command`] struct and their exit /// status is exposed through the [`status`] method, or the [`wait`] method /// of a [`Child`] process. /// +/// An `ExitStatus` represents every possible disposition of a process. On Unix this +/// is the **wait status**. It is *not* simply an *exit status* (a value passed to `exit`). +/// +/// For proper error reporting of failed processes, print the value of `ExitStatus` using its +/// implementation of [`Display`](crate::fmt::Display). +/// /// [`status`]: Command::status /// [`wait`]: Child::wait #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -1400,7 +1417,7 @@ impl ExitStatus { /// if status.success() { /// println!("'projects/' directory created"); /// } else { - /// println!("failed to create 'projects/' directory"); + /// println!("failed to create 'projects/' directory: {}", status); /// } /// ``` #[stable(feature = "process", since = "1.0.0")] @@ -1410,9 +1427,14 @@ impl ExitStatus { /// Returns the exit code of the process, if any. /// - /// On Unix, this will return `None` if the process was terminated - /// by a signal; `std::os::unix` provides an extension trait for - /// extracting the signal and other details from the `ExitStatus`. + /// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the + /// process finished by calling `exit`. Note that on Unix the exit status is truncated to 8 + /// bits, and that values that didn't come from a program's call to `exit` may be invented the + /// runtime system (often, for example, 255, 254, 127 or 126). + /// + /// On Unix, this will return `None` if the process was terminated by a signal. + /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) is an + /// extension trait for extracting any such signal, and other details, from the `ExitStatus`. /// /// # Examples /// diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index db0777ee9f0..b12e7eeb138 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -310,6 +310,7 @@ mod cache_aligned; /// println!("{}", recv.recv().unwrap()); // Received after 2 seconds /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Receiver")] pub struct Receiver<T> { inner: UnsafeCell<Flavor<T>>, } diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index a01ebb316e8..ab61618dc7d 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -219,6 +219,26 @@ impl<T> Mutex<T> { data: UnsafeCell::new(t), } } + + /// Immediately drops the guard, and consequently unlocks the mutex. + /// + /// This function is equivalent to calling [`drop`] on the guard but is more self-documenting. + /// Alternately, the guard will be automatically dropped when it goes out of scope. + /// + /// ``` + /// #![feature(mutex_unlock)] + /// + /// use std::sync::Mutex; + /// let mutex = Mutex::new(0); + /// + /// let mut guard = mutex.lock().unwrap(); + /// *guard += 20; + /// Mutex::unlock(guard); + /// ``` + #[unstable(feature = "mutex_unlock", issue = "81872")] + pub fn unlock(guard: MutexGuard<'_, T>) { + drop(guard); + } } impl<T: ?Sized> Mutex<T> { diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index d967779ce36..0298f59228c 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -23,7 +23,9 @@ use crate::sys_common::rwlock as sys; /// /// The priority policy of the lock is dependent on the underlying operating /// system's implementation, and this type does not guarantee that any -/// particular policy will be used. +/// particular policy will be used. In particular, a writer which is waiting to +/// acquire the lock in `write` might or might not block concurrent calls to +/// `read`. /// /// The type parameter `T` represents the data that this lock protects. It is /// required that `T` satisfies [`Send`] to be shared across threads and diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index d48d9cb0efc..1e79a5c3f9b 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -61,9 +61,9 @@ cfg_if::cfg_if! { #[stable(feature = "rust1", since = "1.0.0")] pub use self::ext as unix_ext; } else if #[cfg(any(target_os = "hermit", - target_arch = "wasm32", + all(target_arch = "wasm32", not(target_os = "wasi")), all(target_vendor = "fortanix", target_env = "sgx")))] { - // On wasm right now the module below doesn't compile + // On non-WASI wasm right now the module below doesn't compile // (missing things in `libc` which is empty) so just omit everything // with an empty module #[unstable(issue = "none", feature = "std_internals")] @@ -85,9 +85,9 @@ cfg_if::cfg_if! { #[stable(feature = "rust1", since = "1.0.0")] pub use self::ext as windows_ext; } else if #[cfg(any(target_os = "hermit", - target_arch = "wasm32", + all(target_arch = "wasm32", not(target_os = "wasi")), all(target_vendor = "fortanix", target_env = "sgx")))] { - // On wasm right now the shim below doesn't compile, so + // On non-WASI wasm right now the shim below doesn't compile, so // just omit it #[unstable(issue = "none", feature = "std_internals")] #[allow(missing_docs)] @@ -106,3 +106,26 @@ cfg_if::cfg_if! { pub mod windows_ext; } } + +#[cfg(doc)] +cfg_if::cfg_if! { + if #[cfg(target_os = "wasi")] { + // On WASI we'll document what's already available + #[stable(feature = "wasi_ext_doc", since = "1.35.0")] + pub use self::ext as wasi_ext; + } else if #[cfg(any(target_os = "hermit", + target_arch = "wasm32", + all(target_vendor = "fortanix", target_env = "sgx")))] { + // On non-WASI wasm right now the module below doesn't compile + // (missing things in `libc` which is empty) so just omit everything + // with an empty module + #[unstable(issue = "none", feature = "std_internals")] + #[allow(missing_docs)] + pub mod wasi_ext {} + } else { + // On other platforms like Windows document the bare bones of WASI + #[path = "wasi/ext/mod.rs"] + #[stable(feature = "wasi_ext_doc", since = "1.35.0")] + pub mod wasi_ext; + } +} diff --git a/library/std/src/sys/sgx/abi/mem.rs b/library/std/src/sys/sgx/abi/mem.rs index da899773dbb..3753fdd3536 100644 --- a/library/std/src/sys/sgx/abi/mem.rs +++ b/library/std/src/sys/sgx/abi/mem.rs @@ -47,20 +47,46 @@ pub fn image_base() -> u64 { /// Returns `true` if the specified memory range is in the enclave. /// -/// `p + len` must not overflow. +/// For safety, this function also checks whether the range given overflows, +/// returning `false` if so. #[unstable(feature = "sgx_platform", issue = "56975")] pub fn is_enclave_range(p: *const u8, len: usize) -> bool { - let start = p as u64; - let end = start + (len as u64); - start >= image_base() && end <= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant + let start = p as usize; + + // Subtract one from `len` when calculating `end` in case `p + len` is + // exactly at the end of addressable memory (`p + len` would overflow, but + // the range is still valid). + let end = if len == 0 { + start + } else if let Some(end) = start.checked_add(len - 1) { + end + } else { + return false; + }; + + let base = image_base() as usize; + start >= base && end <= base + (unsafe { ENCLAVE_SIZE } - 1) // unsafe ok: link-time constant } /// Returns `true` if the specified memory range is in userspace. /// -/// `p + len` must not overflow. +/// For safety, this function also checks whether the range given overflows, +/// returning `false` if so. #[unstable(feature = "sgx_platform", issue = "56975")] pub fn is_user_range(p: *const u8, len: usize) -> bool { - let start = p as u64; - let end = start + (len as u64); - end <= image_base() || start >= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant + let start = p as usize; + + // Subtract one from `len` when calculating `end` in case `p + len` is + // exactly at the end of addressable memory (`p + len` would overflow, but + // the range is still valid). + let end = if len == 0 { + start + } else if let Some(end) = start.checked_add(len - 1) { + end + } else { + return false; + }; + + let base = image_base() as usize; + end < base || start > base + (unsafe { ENCLAVE_SIZE } - 1) // unsafe ok: link-time constant } diff --git a/library/std/src/sys/unix/ext/net/ancillary.rs b/library/std/src/sys/unix/ext/net/ancillary.rs index 0964b6335aa..33d6a39af07 100644 --- a/library/std/src/sys/unix/ext/net/ancillary.rs +++ b/library/std/src/sys/unix/ext/net/ancillary.rs @@ -1,6 +1,6 @@ use super::{sockaddr_un, SocketAddr}; use crate::convert::TryFrom; -use crate::io::{self, IoSliceMut}; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::marker::PhantomData; use crate::mem::{size_of, zeroed}; use crate::os::unix::io::RawFd; @@ -68,7 +68,7 @@ pub(super) fn recv_vectored_with_ancillary_from( pub(super) fn send_vectored_with_ancillary_to( socket: &Socket, path: Option<&Path>, - bufs: &mut [IoSliceMut<'_>], + bufs: &[IoSlice<'_>], ancillary: &mut SocketAncillary<'_>, ) -> io::Result<usize> { unsafe { @@ -78,7 +78,7 @@ pub(super) fn send_vectored_with_ancillary_to( let mut msg: libc::msghdr = zeroed(); msg.msg_name = &mut msg_name as *mut _ as *mut _; msg.msg_namelen = msg_namelen; - msg.msg_iov = bufs.as_mut_ptr().cast(); + msg.msg_iov = bufs.as_ptr() as *mut _; msg.msg_control = ancillary.buffer.as_mut_ptr().cast(); cfg_if::cfg_if! { if #[cfg(any(target_os = "android", all(target_os = "linux", target_env = "gnu")))] { @@ -567,7 +567,7 @@ impl<'a> SocketAncillary<'a> { /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixStream, SocketAncillary}; /// use std::os::unix::io::AsRawFd; - /// use std::io::IoSliceMut; + /// use std::io::IoSlice; /// /// fn main() -> std::io::Result<()> { /// let sock = UnixStream::connect("/tmp/sock")?; @@ -577,7 +577,7 @@ impl<'a> SocketAncillary<'a> { /// ancillary.add_fds(&[sock.as_raw_fd()][..]); /// /// let mut buf = [1; 8]; - /// let mut bufs = &mut [IoSliceMut::new(&mut buf[..])][..]; + /// let mut bufs = &mut [IoSlice::new(&mut buf[..])][..]; /// sock.send_vectored_with_ancillary(bufs, &mut ancillary)?; /// Ok(()) /// } diff --git a/library/std/src/sys/unix/ext/net/datagram.rs b/library/std/src/sys/unix/ext/net/datagram.rs index 0f532c47c8f..a8c13fbb874 100644 --- a/library/std/src/sys/unix/ext/net/datagram.rs +++ b/library/std/src/sys/unix/ext/net/datagram.rs @@ -19,7 +19,7 @@ use super::{sockaddr_un, SocketAddr}; target_os = "netbsd", target_os = "openbsd", ))] -use crate::io::IoSliceMut; +use crate::io::{IoSlice, IoSliceMut}; use crate::net::Shutdown; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; @@ -506,23 +506,24 @@ impl UnixDatagram { /// ```no_run /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixDatagram, SocketAncillary}; - /// use std::io::IoSliceMut; + /// use std::io::IoSlice; /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; - /// let mut buf1 = [1; 8]; - /// let mut buf2 = [2; 16]; - /// let mut buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSliceMut::new(&mut buf1), - /// IoSliceMut::new(&mut buf2), - /// IoSliceMut::new(&mut buf3), + /// let buf1 = [1; 8]; + /// let buf2 = [2; 16]; + /// let buf3 = [3; 8]; + /// let bufs = &[ + /// IoSlice::new(&buf1), + /// IoSlice::new(&buf2), + /// IoSlice::new(&buf3), /// ][..]; /// let fds = [0, 1, 2]; /// let mut ancillary_buffer = [0; 128]; /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&fds[..]); - /// sock.send_vectored_with_ancillary_to(bufs, &mut ancillary, "/some/sock").expect("send_vectored_with_ancillary_to function failed"); + /// sock.send_vectored_with_ancillary_to(bufs, &mut ancillary, "/some/sock") + /// .expect("send_vectored_with_ancillary_to function failed"); /// Ok(()) /// } /// ``` @@ -538,7 +539,7 @@ impl UnixDatagram { #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn send_vectored_with_ancillary_to<P: AsRef<Path>>( &self, - bufs: &mut [IoSliceMut<'_>], + bufs: &[IoSlice<'_>], ancillary: &mut SocketAncillary<'_>, path: P, ) -> io::Result<usize> { @@ -554,23 +555,24 @@ impl UnixDatagram { /// ```no_run /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixDatagram, SocketAncillary}; - /// use std::io::IoSliceMut; + /// use std::io::IoSlice; /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; - /// let mut buf1 = [1; 8]; - /// let mut buf2 = [2; 16]; - /// let mut buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSliceMut::new(&mut buf1), - /// IoSliceMut::new(&mut buf2), - /// IoSliceMut::new(&mut buf3), + /// let buf1 = [1; 8]; + /// let buf2 = [2; 16]; + /// let buf3 = [3; 8]; + /// let bufs = &[ + /// IoSlice::new(&buf1), + /// IoSlice::new(&buf2), + /// IoSlice::new(&buf3), /// ][..]; /// let fds = [0, 1, 2]; /// let mut ancillary_buffer = [0; 128]; /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&fds[..]); - /// sock.send_vectored_with_ancillary(bufs, &mut ancillary).expect("send_vectored_with_ancillary function failed"); + /// sock.send_vectored_with_ancillary(bufs, &mut ancillary) + /// .expect("send_vectored_with_ancillary function failed"); /// Ok(()) /// } /// ``` @@ -586,7 +588,7 @@ impl UnixDatagram { #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn send_vectored_with_ancillary( &self, - bufs: &mut [IoSliceMut<'_>], + bufs: &[IoSlice<'_>], ancillary: &mut SocketAncillary<'_>, ) -> io::Result<usize> { send_vectored_with_ancillary_to(&self.0, None, bufs, ancillary) diff --git a/library/std/src/sys/unix/ext/net/stream.rs b/library/std/src/sys/unix/ext/net/stream.rs index 9fe6b85837e..fc08edacb82 100644 --- a/library/std/src/sys/unix/ext/net/stream.rs +++ b/library/std/src/sys/unix/ext/net/stream.rs @@ -530,23 +530,24 @@ impl UnixStream { /// ```no_run /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixStream, SocketAncillary}; - /// use std::io::IoSliceMut; + /// use std::io::IoSlice; /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// let mut buf1 = [1; 8]; - /// let mut buf2 = [2; 16]; - /// let mut buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSliceMut::new(&mut buf1), - /// IoSliceMut::new(&mut buf2), - /// IoSliceMut::new(&mut buf3), + /// let buf1 = [1; 8]; + /// let buf2 = [2; 16]; + /// let buf3 = [3; 8]; + /// let bufs = &[ + /// IoSlice::new(&buf1), + /// IoSlice::new(&buf2), + /// IoSlice::new(&buf3), /// ][..]; /// let fds = [0, 1, 2]; /// let mut ancillary_buffer = [0; 128]; /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&fds[..]); - /// socket.send_vectored_with_ancillary(bufs, &mut ancillary).expect("send_vectored_with_ancillary function failed"); + /// socket.send_vectored_with_ancillary(bufs, &mut ancillary) + /// .expect("send_vectored_with_ancillary function failed"); /// Ok(()) /// } /// ``` @@ -562,7 +563,7 @@ impl UnixStream { #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn send_vectored_with_ancillary( &self, - bufs: &mut [IoSliceMut<'_>], + bufs: &[IoSlice<'_>], ancillary: &mut SocketAncillary<'_>, ) -> io::Result<usize> { send_vectored_with_ancillary_to(&self.0, None, bufs, ancillary) diff --git a/library/std/src/sys/unix/ext/net/tests.rs b/library/std/src/sys/unix/ext/net/tests.rs index 97a016904b4..bd9b6dd727b 100644 --- a/library/std/src/sys/unix/ext/net/tests.rs +++ b/library/std/src/sys/unix/ext/net/tests.rs @@ -485,14 +485,14 @@ fn test_unix_datagram_peek_from() { fn test_send_vectored_fds_unix_stream() { let (s1, s2) = or_panic!(UnixStream::pair()); - let mut buf1 = [1; 8]; - let mut bufs_send = &mut [IoSliceMut::new(&mut buf1[..])][..]; + let buf1 = [1; 8]; + let bufs_send = &[IoSlice::new(&buf1[..])][..]; let mut ancillary1_buffer = [0; 128]; let mut ancillary1 = SocketAncillary::new(&mut ancillary1_buffer[..]); assert!(ancillary1.add_fds(&[s1.as_raw_fd()][..])); - let usize = or_panic!(s1.send_vectored_with_ancillary(&mut bufs_send, &mut ancillary1)); + let usize = or_panic!(s1.send_vectored_with_ancillary(&bufs_send, &mut ancillary1)); assert_eq!(usize, 8); let mut buf2 = [0; 8]; @@ -542,8 +542,8 @@ fn test_send_vectored_with_ancillary_to_unix_datagram() { or_panic!(bsock2.set_passcred(true)); - let mut buf1 = [1; 8]; - let mut bufs_send = &mut [IoSliceMut::new(&mut buf1[..])][..]; + let buf1 = [1; 8]; + let bufs_send = &[IoSlice::new(&buf1[..])][..]; let mut ancillary1_buffer = [0; 128]; let mut ancillary1 = SocketAncillary::new(&mut ancillary1_buffer[..]); @@ -554,7 +554,7 @@ fn test_send_vectored_with_ancillary_to_unix_datagram() { assert!(ancillary1.add_creds(&[cred1.clone()][..])); let usize = - or_panic!(bsock1.send_vectored_with_ancillary_to(&mut bufs_send, &mut ancillary1, &path2)); + or_panic!(bsock1.send_vectored_with_ancillary_to(&bufs_send, &mut ancillary1, &path2)); assert_eq!(usize, 8); let mut buf2 = [0; 8]; @@ -603,15 +603,15 @@ fn test_send_vectored_with_ancillary_unix_datagram() { let bsock1 = or_panic!(UnixDatagram::bind(&path1)); let bsock2 = or_panic!(UnixDatagram::bind(&path2)); - let mut buf1 = [1; 8]; - let mut bufs_send = &mut [IoSliceMut::new(&mut buf1[..])][..]; + let buf1 = [1; 8]; + let bufs_send = &[IoSlice::new(&buf1[..])][..]; let mut ancillary1_buffer = [0; 128]; let mut ancillary1 = SocketAncillary::new(&mut ancillary1_buffer[..]); assert!(ancillary1.add_fds(&[bsock1.as_raw_fd()][..])); or_panic!(bsock1.connect(&path2)); - let usize = or_panic!(bsock1.send_vectored_with_ancillary(&mut bufs_send, &mut ancillary1)); + let usize = or_panic!(bsock1.send_vectored_with_ancillary(&bufs_send, &mut ancillary1)); assert_eq!(usize, 8); let mut buf2 = [0; 8]; diff --git a/library/std/src/sys/unix/ext/process.rs b/library/std/src/sys/unix/ext/process.rs index 7559c1f1d9e..1276edc6af6 100644 --- a/library/std/src/sys/unix/ext/process.rs +++ b/library/std/src/sys/unix/ext/process.rs @@ -62,9 +62,14 @@ pub trait CommandExt: Sealed { /// `fork`. This primarily means that any modifications made to memory on /// behalf of this closure will **not** be visible to the parent process. /// This is often a very constrained environment where normal operations - /// like `malloc` or acquiring a mutex are not guaranteed to work (due to + /// like `malloc`, accessing environment variables through [`std::env`] + /// or acquiring a mutex are not guaranteed to work (due to /// other threads perhaps still running when the `fork` was run). /// + /// For further details refer to the [POSIX fork() specification] + /// and the equivalent documentation for any targeted + /// platform, especially the requirements around *async-signal-safety*. + /// /// This also means that all resources such as file descriptors and /// memory-mapped regions got duplicated. It is your responsibility to make /// sure that the closure does not violate library invariants by making @@ -73,6 +78,10 @@ pub trait CommandExt: Sealed { /// When this closure is run, aspects such as the stdio file descriptors and /// working directory have successfully been changed, so output to these /// locations may not appear where intended. + /// + /// [POSIX fork() specification]: + /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html + /// [`std::env`]: mod@crate::env #[stable(feature = "process_pre_exec", since = "1.34.0")] unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command where @@ -172,6 +181,8 @@ impl CommandExt for process::Command { } fn exec(&mut self) -> io::Error { + // NOTE: This may *not* be safe to call after `libc::fork`, because it + // may allocate. That may be worth fixing at some point in the future. self.as_inner_mut().exec(sys::process::Stdio::Inherit) } @@ -186,12 +197,20 @@ impl CommandExt for process::Command { /// Unix-specific extensions to [`process::ExitStatus`]. /// +/// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as passed to the +/// `exit` system call or returned by [`ExitStatus::code()`](crate::process::ExitStatus::code). +/// It represents **any wait status**, as returned by one of the `wait` family of system calls. +/// +/// This is because a Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but +/// can also represent other kinds of process event. +/// /// This trait is sealed: it cannot be implemented outside the standard library. /// This is so that future additional methods are not breaking changes. #[stable(feature = "rust1", since = "1.0.0")] pub trait ExitStatusExt: Sealed { - /// Creates a new `ExitStatus` from the raw underlying `i32` return value of - /// a process. + /// Creates a new `ExitStatus` from the raw underlying integer status value from `wait` + /// + /// The value should be a **wait status, not an exit status**. #[stable(feature = "exit_status_from", since = "1.12.0")] fn from_raw(raw: i32) -> Self; @@ -220,6 +239,8 @@ pub trait ExitStatusExt: Sealed { fn continued(&self) -> bool; /// Returns the underlying raw `wait` status. + /// + /// The returned integer is a **wait status, not an exit status**. #[unstable(feature = "unix_process_wait_more", issue = "80695")] fn into_raw(self) -> i32; } diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 200dbf06ff8..9687576bb6a 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -61,6 +61,7 @@ use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; +use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV}; #[cfg(test)] mod tests; @@ -535,7 +536,7 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0)) }; - if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(libc::EBADF))) { + if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(EBADF))) { HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed); } else { HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed); @@ -573,19 +574,20 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> Err(err) => { return match err.raw_os_error() { // when file offset + max_length > u64::MAX - Some(libc::EOVERFLOW) => CopyResult::Fallback(written), - Some( - libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP, - ) => { + Some(EOVERFLOW) => CopyResult::Fallback(written), + Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) => { // Try fallback io::copy if either: // - Kernel version is < 4.5 (ENOSYS¹) // - Files are mounted on different fs (EXDEV) // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP) // - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM) // - copy_file_range cannot be used with pipes or device nodes (EINVAL) + // - the writer fd was opened with O_APPEND (EBADF²) // // ¹ these cases should be detected by the initial probe but we handle them here // anyway in case syscall interception changes during runtime + // ² actually invalid file descriptors would cause this too, but in that case + // the fallback code path is expected to encounter the same error again assert_eq!(written, 0); CopyResult::Fallback(0) } @@ -649,7 +651,7 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> Ok(ret) => written += ret as u64, Err(err) => { return match err.raw_os_error() { - Some(libc::ENOSYS | libc::EPERM) => { + Some(ENOSYS | EPERM) => { // syscall not supported (ENOSYS) // syscall is disallowed, e.g. by seccomp (EPERM) match mode { @@ -659,12 +661,12 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> assert_eq!(written, 0); CopyResult::Fallback(0) } - Some(libc::EINVAL) => { + Some(EINVAL) => { // splice/sendfile do not support this particular file descriptor (EINVAL) assert_eq!(written, 0); CopyResult::Fallback(0) } - Some(os_err) if mode == SpliceMode::Sendfile && os_err == libc::EOVERFLOW => { + Some(os_err) if mode == SpliceMode::Sendfile && os_err == EOVERFLOW => { CopyResult::Fallback(written) } _ => CopyResult::Error(err, written), diff --git a/library/std/src/sys/unix/kernel_copy/tests.rs b/library/std/src/sys/unix/kernel_copy/tests.rs index 77369cdd35f..3fe849e23e2 100644 --- a/library/std/src/sys/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/unix/kernel_copy/tests.rs @@ -65,6 +65,24 @@ fn copy_specialization() -> Result<()> { result.and(rm1).and(rm2) } +#[test] +fn copies_append_mode_sink() -> Result<()> { + let tmp_path = tmpdir(); + let source_path = tmp_path.join("copies_append_mode.source"); + let sink_path = tmp_path.join("copies_append_mode.sink"); + let mut source = + OpenOptions::new().create(true).truncate(true).write(true).read(true).open(&source_path)?; + write!(source, "not empty")?; + source.seek(SeekFrom::Start(0))?; + let mut sink = OpenOptions::new().create(true).append(true).open(&sink_path)?; + + let copied = crate::io::copy(&mut source, &mut sink)?; + + assert_eq!(copied, 9); + + Ok(()) +} + #[bench] fn bench_file_to_file_copy(b: &mut test::Bencher) { const BYTES: usize = 128 * 1024; diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 7198a2f08d6..587ffe15981 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -195,6 +195,7 @@ impl Socket { // glibc 2.10 and musl 0.9.5. cfg_if::cfg_if! { if #[cfg(any( + target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", @@ -206,13 +207,6 @@ impl Socket { libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) })?; Ok(Socket(FileDesc::new(fd))) - // While the Android kernel supports the syscall, - // it is not included in all versions of Android's libc. - } else if #[cfg(target_os = "android")] { - let fd = cvt_r(|| unsafe { - libc::syscall(libc::SYS_accept4, self.0.raw(), storage, len, libc::SOCK_CLOEXEC) - })?; - Ok(Socket(FileDesc::new(fd as c_int))) } else { let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; let fd = FileDesc::new(fd); diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 1d1118aa694..d5e14bec765 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -22,7 +22,6 @@ use crate::str; use crate::sys::cvt; use crate::sys::fd; use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard}; -use crate::sys_common::rwlock::{RWLockReadGuard, StaticRWLock}; use crate::vec; use libc::{c_char, c_int, c_void}; @@ -491,20 +490,20 @@ pub unsafe fn environ() -> *mut *const *const c_char { extern "C" { static mut environ: *const *const c_char; } - ptr::addr_of_mut!(environ) + &mut environ } -static ENV_LOCK: StaticRWLock = StaticRWLock::new(); - -pub fn env_read_lock() -> RWLockReadGuard { - ENV_LOCK.read_with_guard() +pub unsafe fn env_lock() -> StaticMutexGuard { + // It is UB to attempt to acquire this mutex reentrantly! + static ENV_LOCK: StaticMutex = StaticMutex::new(); + ENV_LOCK.lock() } /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. pub fn env() -> Env { unsafe { - let _guard = env_read_lock(); + let _guard = env_lock(); let mut environ = *environ(); let mut result = Vec::new(); if !environ.is_null() { @@ -541,7 +540,7 @@ pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> { // always None as well let k = CString::new(k.as_bytes())?; unsafe { - let _guard = env_read_lock(); + let _guard = env_lock(); let s = libc::getenv(k.as_ptr()) as *const libc::c_char; let ret = if s.is_null() { None @@ -557,7 +556,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let v = CString::new(v.as_bytes())?; unsafe { - let _guard = ENV_LOCK.write_with_guard(); + let _guard = env_lock(); cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) } } @@ -566,7 +565,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> { let nbuf = CString::new(n.as_bytes())?; unsafe { - let _guard = ENV_LOCK.write_with_guard(); + let _guard = env_lock(); cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) } } diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index a96d4aa6a45..b9dcc4e4b9e 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -60,25 +60,13 @@ cfg_if::cfg_if! { //////////////////////////////////////////////////////////////////////////////// pub struct Command { - // Currently we try hard to ensure that the call to `.exec()` doesn't - // actually allocate any memory. While many platforms try to ensure that - // memory allocation works after a fork in a multithreaded process, it's - // been observed to be buggy and somewhat unreliable, so we do our best to - // just not do it at all! - // - // Along those lines, the `argv` and `envp` raw pointers here are exactly - // what's gonna get passed to `execvp`. The `argv` array starts with the - // `program` and ends with a NULL, and the `envp` pointer, if present, is - // also null-terminated. - // - // Right now we don't support removing arguments, so there's no much fancy - // support there, but we support adding and removing environment variables, - // so a side table is used to track where in the `envp` array each key is - // located. Whenever we add a key we update it in place if it's already - // present, and whenever we remove a key we update the locations of all - // other keys. program: CString, args: Vec<CString>, + /// Exactly what will be passed to `execvp`. + /// + /// First element is a pointer to `program`, followed by pointers to + /// `args`, followed by a `null`. Be careful when modifying `program` or + /// `args` to properly update this as well. argv: Argv, env: CommandEnv, diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 9e82df7755e..2eb64a99e59 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -1,6 +1,7 @@ use crate::convert::TryInto; use crate::fmt; use crate::io::{self, Error, ErrorKind}; +use crate::mem; use crate::ptr; use crate::sys; use crate::sys::cvt; @@ -45,15 +46,14 @@ impl Command { // // Note that as soon as we're done with the fork there's no need to hold // a lock any more because the parent won't do anything and the child is - // in its own process. - let result = unsafe { - let _env_lock = sys::os::env_read_lock(); - cvt(libc::fork())? - }; + // in its own process. Thus the parent drops the lock guard while the child + // forgets it to avoid unlocking it on a new thread, which would be invalid. + let (env_lock, result) = unsafe { (sys::os::env_lock(), cvt(libc::fork())?) }; let pid = unsafe { match result { 0 => { + mem::forget(env_lock); drop(input); let Err(err) = self.do_exec(theirs, envp.as_ref()); let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; @@ -74,7 +74,10 @@ impl Command { rtassert!(output.write(&bytes).is_ok()); libc::_exit(1) } - n => n, + n => { + drop(env_lock); + n + } } }; @@ -124,7 +127,7 @@ impl Command { // Similar to when forking, we want to ensure that access to // the environment is synchronized, so make sure to grab the // environment lock before we try to exec. - let _lock = sys::os::env_read_lock(); + let _lock = sys::os::env_lock(); let Err(e) = self.do_exec(theirs, envp.as_ref()); e @@ -404,7 +407,7 @@ impl Command { cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; // Make sure we synchronize access to the global `environ` resource - let _env_lock = sys::os::env_read_lock(); + let _env_lock = sys::os::env_lock(); let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); cvt_nz(libc::posix_spawnp( &mut p.pid, @@ -527,9 +530,22 @@ impl fmt::Display for ExitStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(code) = self.code() { write!(f, "exit code: {}", code) + } else if let Some(signal) = self.signal() { + if self.core_dumped() { + write!(f, "signal: {} (core dumped)", signal) + } else { + write!(f, "signal: {}", signal) + } + } else if let Some(signal) = self.stopped_signal() { + write!(f, "stopped (not terminated) by signal: {}", signal) + } else if self.continued() { + write!(f, "continued (WIFCONTINUED)") } else { - let signal = self.signal().unwrap(); - write!(f, "signal: {}", signal) + write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0) } } } + +#[cfg(test)] +#[path = "process_unix/tests.rs"] +mod tests; diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs new file mode 100644 index 00000000000..5819d2c2a5a --- /dev/null +++ b/library/std/src/sys/unix/process/process_unix/tests.rs @@ -0,0 +1,30 @@ +#[test] +fn exitstatus_display_tests() { + // In practice this is the same on every Unix. + // If some weird platform turns out to be different, and this test fails, use #[cfg]. + use crate::os::unix::process::ExitStatusExt; + use crate::process::ExitStatus; + + let t = |v, s| assert_eq!(s, format!("{}", <ExitStatus as ExitStatusExt>::from_raw(v))); + + t(0x0000f, "signal: 15"); + t(0x0008b, "signal: 11 (core dumped)"); + t(0x00000, "exit code: 0"); + t(0x0ff00, "exit code: 255"); + + // On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED. Probably *BSD is similar. + // https://github.com/rust-lang/rust/pull/82749#issuecomment-790525956 + // The purpose of this test is to test our string formatting, not our understanding of the wait + // status magic numbers. So restrict these to Linux. + if cfg!(target_os = "linux") { + t(0x0137f, "stopped (not terminated) by signal: 19"); + t(0x0ffff, "continued (WIFCONTINUED)"); + } + + // Testing "unrecognised wait status" is hard because the wait.h macros typically + // assume that the value came from wait and isn't mad. With the glibc I have here + // this works: + if cfg!(all(target_os = "linux", target_env = "gnu")) { + t(0x000ff, "unrecognised wait status: 255 0xff"); + } +} diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs index d8474205352..d7bba50c76a 100644 --- a/library/std/src/sys/unix/stack_overflow.rs +++ b/library/std/src/sys/unix/stack_overflow.rs @@ -34,7 +34,7 @@ impl Drop for Handler { target_os = "freebsd", target_os = "solaris", target_os = "illumos", - all(target_os = "netbsd", not(target_vendor = "rumprun")), + target_os = "netbsd", target_os = "openbsd" ))] mod imp { @@ -218,7 +218,7 @@ mod imp { target_os = "freebsd", target_os = "solaris", target_os = "illumos", - all(target_os = "netbsd", not(target_vendor = "rumprun")), + target_os = "netbsd", target_os = "openbsd", )))] mod imp { diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index cda17eb4bd2..40c96307514 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -202,7 +202,7 @@ impl Drop for Thread { not(target_os = "linux"), not(target_os = "freebsd"), not(target_os = "macos"), - not(all(target_os = "netbsd", not(target_vendor = "rumprun"))), + not(target_os = "netbsd"), not(target_os = "openbsd"), not(target_os = "solaris") ))] @@ -222,7 +222,7 @@ pub mod guard { target_os = "linux", target_os = "freebsd", target_os = "macos", - all(target_os = "netbsd", not(target_vendor = "rumprun")), + target_os = "netbsd", target_os = "openbsd", target_os = "solaris" ))] diff --git a/library/std/src/sys/wasi/ext/fs.rs b/library/std/src/sys/wasi/ext/fs.rs index a8da003d550..6472642f034 100644 --- a/library/std/src/sys/wasi/ext/fs.rs +++ b/library/std/src/sys/wasi/ext/fs.rs @@ -3,11 +3,14 @@ #![deny(unsafe_op_in_unsafe_fn)] #![unstable(feature = "wasi_ext", issue = "none")] +use crate::ffi::OsStr; use crate::fs::{self, File, Metadata, OpenOptions}; use crate::io::{self, IoSlice, IoSliceMut}; use crate::path::{Path, PathBuf}; -use crate::sys::fs::osstr2str; use crate::sys_common::{AsInner, AsInnerMut, FromInner}; +// Used for `File::read` on intra-doc links +#[allow(unused_imports)] +use io::{Read, Write}; /// WASI-specific extensions to [`File`]. pub trait FileExt { @@ -54,11 +57,11 @@ pub trait FileExt { /// # Errors /// /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// [`io::ErrorKind::Interrupted`] then the error is ignored and the operation /// will continue. /// /// If this function encounters an "end of file" before completely filling - /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// the buffer, it returns an error of the kind [`io::ErrorKind::UnexpectedEof`]. /// The contents of `buf` are unspecified in this case. /// /// If any other read error is encountered then this function immediately @@ -131,16 +134,16 @@ pub trait FileExt { /// The current file cursor is not affected by this function. /// /// This method will continuously call [`write_at`] until there is no more data - /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// to be written or an error of non-[`io::ErrorKind::Interrupted`] kind is /// returned. This method will not return until the entire buffer has been /// successfully written or such an error occurs. The first error that is - /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// not of [`io::ErrorKind::Interrupted`] kind generated from this method will be /// returned. /// /// # Errors /// /// This function will return the first error of - /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns. + /// non-[`io::ErrorKind::Interrupted`] kind that [`write_at`] returns. /// /// [`write_at`]: FileExt::write_at #[stable(feature = "rw_exact_all_at", since = "1.33.0")] @@ -397,6 +400,8 @@ pub trait MetadataExt { fn ino(&self) -> u64; /// Returns the `st_nlink` field of the internal `filestat_t` fn nlink(&self) -> u64; + /// Returns the `st_size` field of the internal `filestat_t` + fn size(&self) -> u64; /// Returns the `st_atim` field of the internal `filestat_t` fn atim(&self) -> u64; /// Returns the `st_mtim` field of the internal `filestat_t` @@ -415,6 +420,9 @@ impl MetadataExt for fs::Metadata { fn nlink(&self) -> u64 { self.as_inner().as_wasi().nlink } + fn size(&self) -> u64 { + self.as_inner().as_wasi().size + } fn atim(&self) -> u64 { self.as_inner().as_wasi().atim } @@ -426,7 +434,7 @@ impl MetadataExt for fs::Metadata { } } -/// WASI-specific extensions for [`FileType`]. +/// WASI-specific extensions for [`fs::FileType`]. /// /// Adds support for special WASI file types such as block/character devices, /// pipes, and sockets. @@ -517,8 +525,12 @@ pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>( /// Create a symbolic link. /// -/// This is a convenience API similar to [`std::os::unix::fs::symlink`] and -/// [`std::os::windows::fs::symlink_file`] and [`symlink_dir`](std::os::windows::fs::symlink_dir). +/// This is a convenience API similar to `std::os::unix::fs::symlink` and +/// `std::os::windows::fs::symlink_file` and `std::os::windows::fs::symlink_dir`. pub fn symlink_path<P: AsRef<Path>, U: AsRef<Path>>(old_path: P, new_path: U) -> io::Result<()> { crate::sys::fs::symlink(old_path.as_ref(), new_path.as_ref()) } + +fn osstr2str(f: &OsStr) -> io::Result<&str> { + f.to_str().ok_or_else(|| io::Error::new(io::ErrorKind::Other, "input must be utf-8")) +} diff --git a/library/std/src/sys/wasi/ext/io.rs b/library/std/src/sys/wasi/ext/io.rs index 81413f39dc1..3c480aa8e19 100644 --- a/library/std/src/sys/wasi/ext/io.rs +++ b/library/std/src/sys/wasi/ext/io.rs @@ -145,36 +145,36 @@ impl IntoRawFd for fs::File { impl AsRawFd for io::Stdin { fn as_raw_fd(&self) -> RawFd { - sys::stdio::Stdin.as_raw_fd() + libc::STDIN_FILENO as RawFd } } impl AsRawFd for io::Stdout { fn as_raw_fd(&self) -> RawFd { - sys::stdio::Stdout.as_raw_fd() + libc::STDOUT_FILENO as RawFd } } impl AsRawFd for io::Stderr { fn as_raw_fd(&self) -> RawFd { - sys::stdio::Stderr.as_raw_fd() + libc::STDERR_FILENO as RawFd } } impl<'a> AsRawFd for io::StdinLock<'a> { fn as_raw_fd(&self) -> RawFd { - sys::stdio::Stdin.as_raw_fd() + libc::STDIN_FILENO as RawFd } } impl<'a> AsRawFd for io::StdoutLock<'a> { fn as_raw_fd(&self) -> RawFd { - sys::stdio::Stdout.as_raw_fd() + libc::STDOUT_FILENO as RawFd } } impl<'a> AsRawFd for io::StderrLock<'a> { fn as_raw_fd(&self) -> RawFd { - sys::stdio::Stderr.as_raw_fd() + libc::STDERR_FILENO as RawFd } } diff --git a/library/std/src/sys/wasi/ext/mod.rs b/library/std/src/sys/wasi/ext/mod.rs index 1cda30edcad..b08402f0776 100644 --- a/library/std/src/sys/wasi/ext/mod.rs +++ b/library/std/src/sys/wasi/ext/mod.rs @@ -1,4 +1,32 @@ +//! Platform-specific extensions to `std` for WASI. +//! +//! Provides access to platform-level information on WASI, and exposes +//! WASI-specific functions that would otherwise be inappropriate as +//! part of the core `std` library. +//! +//! It exposes more ways to deal with platform-specific strings (`OsStr`, +//! `OsString`), allows to set permissions more granularly, extract low-level +//! file descriptors from files and sockets, and has platform-specific helpers +//! for spawning processes. +//! +//! # Examples +//! +//! ```no_run +//! use std::fs::File; +//! use std::os::wasi::prelude::*; +//! +//! fn main() -> std::io::Result<()> { +//! let f = File::create("foo.txt")?; +//! let fd = f.as_raw_fd(); +//! +//! // use fd with native WASI bindings +//! +//! Ok(()) +//! } +//! ``` + #![deny(unsafe_op_in_unsafe_fn)] +#![doc(cfg(target_os = "wasi"))] pub mod ffi; pub mod fs; @@ -11,14 +39,14 @@ pub mod io; pub mod prelude { #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::ext::ffi::{OsStrExt, OsStringExt}; + pub use super::ffi::{OsStrExt, OsStringExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::ext::fs::FileTypeExt; + pub use super::fs::FileTypeExt; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::ext::fs::{DirEntryExt, FileExt, MetadataExt, OpenOptionsExt}; + pub use super::fs::{DirEntryExt, FileExt, MetadataExt, OpenOptionsExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::ext::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; } diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index 4134ef67671..63c22136273 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -1,5 +1,6 @@ #![deny(unsafe_op_in_unsafe_fn)] +use super::fd::WasiFd; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; @@ -9,7 +10,6 @@ use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; use crate::path::{Path, PathBuf}; use crate::ptr; use crate::sync::Arc; -use crate::sys::fd::WasiFd; use crate::sys::time::SystemTime; use crate::sys::unsupported; use crate::sys_common::FromInner; @@ -557,12 +557,8 @@ pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { pub fn link(original: &Path, link: &Path) -> io::Result<()> { let (original, original_file) = open_parent(original)?; let (link, link_file) = open_parent(link)?; - original.link( - wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, - osstr2str(original_file.as_ref())?, - &link, - osstr2str(link_file.as_ref())?, - ) + // Pass 0 as the flags argument, meaning don't follow symlinks. + original.link(0, osstr2str(original_file.as_ref())?, &link, osstr2str(link_file.as_ref())?) } pub fn stat(p: &Path) -> io::Result<FileAttr> { @@ -654,13 +650,11 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> { ); return Err(io::Error::new(io::ErrorKind::Other, msg)); } - let len = CStr::from_ptr(buf.as_ptr().cast()).to_bytes().len(); - buf.set_len(len); - buf.shrink_to_fit(); + let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); return Ok(( ManuallyDrop::new(WasiFd::from_raw(fd as u32)), - PathBuf::from(OsString::from_vec(buf)), + PathBuf::from(OsString::from_vec(relative)), )); } } diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs index 8fd4bb76d85..3f294e7df41 100644 --- a/library/std/src/sys/wasi/net.rs +++ b/library/std/src/sys/wasi/net.rs @@ -1,10 +1,10 @@ #![deny(unsafe_op_in_unsafe_fn)] +use super::fd::WasiFd; use crate::convert::TryFrom; use crate::fmt; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::fd::WasiFd; use crate::sys::{unsupported, Void}; use crate::sys_common::FromInner; use crate::time::Duration; diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs index d82f6f41186..209d5b996e5 100644 --- a/library/std/src/sys/wasi/stdio.rs +++ b/library/std/src/sys/wasi/stdio.rs @@ -1,8 +1,8 @@ #![deny(unsafe_op_in_unsafe_fn)] +use super::fd::WasiFd; use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem::ManuallyDrop; -use crate::sys::fd::WasiFd; pub struct Stdin; pub struct Stdout; diff --git a/library/std/src/sys/windows/ext/process.rs b/library/std/src/sys/windows/ext/process.rs index 3d680a7f2d9..67412e16779 100644 --- a/library/std/src/sys/windows/ext/process.rs +++ b/library/std/src/sys/windows/ext/process.rs @@ -105,6 +105,22 @@ pub trait CommandExt: Sealed { /// [1]: https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags #[stable(feature = "windows_process_extensions", since = "1.16.0")] fn creation_flags(&mut self, flags: u32) -> &mut process::Command; + + /// Forces all arguments to be wrapped in quote (`"`) characters. + /// + /// This is useful for passing arguments to [MSYS2/Cygwin][1] based + /// executables: these programs will expand unquoted arguments containing + /// wildcard characters (`?` and `*`) by searching for any file paths + /// matching the wildcard pattern. + /// + /// Adding quotes has no effect when passing arguments to programs + /// that use [msvcrt][2]. This includes programs built with both + /// MinGW and MSVC. + /// + /// [1]: <https://github.com/msys2/MSYS2-packages/issues/2176> + /// [2]: <https://msdn.microsoft.com/en-us/library/17w5ykft.aspx> + #[unstable(feature = "windows_process_extensions_force_quotes", issue = "82227")] + fn force_quotes(&mut self, enabled: bool) -> &mut process::Command; } #[stable(feature = "windows_process_extensions", since = "1.16.0")] @@ -113,4 +129,9 @@ impl CommandExt for process::Command { self.as_inner_mut().creation_flags(flags); self } + + fn force_quotes(&mut self, enabled: bool) -> &mut process::Command { + self.as_inner_mut().force_quotes(enabled); + self + } } diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index c10c0df4a3a..b8f512f6a23 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -8,7 +8,9 @@ mod tests; pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; -// Safety: `bytes` must be a valid wtf8 encoded slice +/// # Safety +/// +/// `bytes` must be a valid wtf8 encoded slice #[inline] unsafe fn bytes_as_os_str(bytes: &[u8]) -> &OsStr { // &OsStr is layout compatible with &Slice, which is compatible with &Wtf8, @@ -130,7 +132,7 @@ fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index. let path = &path.bytes()[separator_end..]; - // Safety: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\') + // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\') // is encoded in a single byte, therefore `bytes[separator_start]` and // `bytes[separator_end]` must be code point boundaries and thus // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices. diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index 243065b94b1..83d37795ee5 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -78,6 +78,7 @@ pub struct Command { stdin: Option<Stdio>, stdout: Option<Stdio>, stderr: Option<Stdio>, + force_quotes_enabled: bool, } pub enum Stdio { @@ -109,6 +110,7 @@ impl Command { stdin: None, stdout: None, stderr: None, + force_quotes_enabled: false, } } @@ -134,6 +136,10 @@ impl Command { self.flags = flags; } + pub fn force_quotes(&mut self, enabled: bool) { + self.force_quotes_enabled = enabled; + } + pub fn get_program(&self) -> &OsStr { &self.program } @@ -181,7 +187,7 @@ impl Command { si.dwFlags = c::STARTF_USESTDHANDLES; let program = program.as_ref().unwrap_or(&self.program); - let mut cmd_str = make_command_line(program, &self.args)?; + let mut cmd_str = make_command_line(program, &self.args, self.force_quotes_enabled)?; cmd_str.push(0); // add null terminator // stolen from the libuv code. @@ -467,7 +473,7 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION { // Produces a wide string *without terminating null*; returns an error if // `prog` or any of the `args` contain a nul. -fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> { +fn make_command_line(prog: &OsStr, args: &[OsString], force_quotes: bool) -> io::Result<Vec<u16>> { // Encode the command and arguments in a command line string such // that the spawned process may recover them using CommandLineToArgvW. let mut cmd: Vec<u16> = Vec::new(); @@ -476,7 +482,7 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> { append_arg(&mut cmd, prog, true)?; for arg in args { cmd.push(' ' as u16); - append_arg(&mut cmd, arg, false)?; + append_arg(&mut cmd, arg, force_quotes)?; } return Ok(cmd); diff --git a/library/std/src/sys/windows/process/tests.rs b/library/std/src/sys/windows/process/tests.rs index 81627ad139b..8830ae049c6 100644 --- a/library/std/src/sys/windows/process/tests.rs +++ b/library/std/src/sys/windows/process/tests.rs @@ -3,29 +3,41 @@ use crate::ffi::{OsStr, OsString}; #[test] fn test_make_command_line() { - fn test_wrapper(prog: &str, args: &[&str]) -> String { + fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String { let command_line = &make_command_line( OsStr::new(prog), &args.iter().map(|a| OsString::from(a)).collect::<Vec<OsString>>(), + force_quotes, ) .unwrap(); String::from_utf16(command_line).unwrap() } - assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"]), "\"prog\" aaa bbb ccc"); + assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"], false), "\"prog\" aaa bbb ccc"); assert_eq!( - test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]), + test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"], false), "\"C:\\Program Files\\blah\\blah.exe\" aaa" ); assert_eq!( - test_wrapper("C:\\Program Files\\test", &["aa\"bb"]), + test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa", "v*"], false), + "\"C:\\Program Files\\blah\\blah.exe\" aaa v*" + ); + assert_eq!( + test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa", "v*"], true), + "\"C:\\Program Files\\blah\\blah.exe\" \"aaa\" \"v*\"" + ); + assert_eq!( + test_wrapper("C:\\Program Files\\test", &["aa\"bb"], false), "\"C:\\Program Files\\test\" aa\\\"bb" ); - assert_eq!(test_wrapper("echo", &["a b c"]), "\"echo\" \"a b c\""); - assert_eq!(test_wrapper("echo", &["\" \\\" \\", "\\"]), "\"echo\" \"\\\" \\\\\\\" \\\\\" \\"); + assert_eq!(test_wrapper("echo", &["a b c"], false), "\"echo\" \"a b c\""); + assert_eq!( + test_wrapper("echo", &["\" \\\" \\", "\\"], false), + "\"echo\" \"\\\" \\\\\\\" \\\\\" \\" + ); assert_eq!( - test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]), + test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[], false), "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\"" ); } diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs index 41e8ad77294..3705d641a1b 100644 --- a/library/std/src/sys_common/rwlock.rs +++ b/library/std/src/sys_common/rwlock.rs @@ -86,62 +86,3 @@ impl RWLock { self.0.destroy() } } - -// the cfg annotations only exist due to dead code warnings. the code itself is portable -#[cfg(unix)] -pub struct StaticRWLock(RWLock); - -#[cfg(unix)] -impl StaticRWLock { - pub const fn new() -> StaticRWLock { - StaticRWLock(RWLock::new()) - } - - /// Acquires shared access to the underlying lock, blocking the current - /// thread to do so. - /// - /// The lock is automatically unlocked when the returned guard is dropped. - #[inline] - pub fn read_with_guard(&'static self) -> RWLockReadGuard { - // Safety: All methods require static references, therefore self - // cannot be moved between invocations. - unsafe { - self.0.read(); - } - RWLockReadGuard(&self.0) - } - - /// Acquires write access to the underlying lock, blocking the current thread - /// to do so. - /// - /// The lock is automatically unlocked when the returned guard is dropped. - #[inline] - pub fn write_with_guard(&'static self) -> RWLockWriteGuard { - // Safety: All methods require static references, therefore self - // cannot be moved between invocations. - unsafe { - self.0.write(); - } - RWLockWriteGuard(&self.0) - } -} - -#[cfg(unix)] -pub struct RWLockReadGuard(&'static RWLock); - -#[cfg(unix)] -impl Drop for RWLockReadGuard { - fn drop(&mut self) { - unsafe { self.0.read_unlock() } - } -} - -#[cfg(unix)] -pub struct RWLockWriteGuard(&'static RWLock); - -#[cfg(unix)] -impl Drop for RWLockWriteGuard { - fn drop(&mut self) { - unsafe { self.0.write_unlock() } - } -} diff --git a/library/std/src/sys_common/thread_local_dtor.rs b/library/std/src/sys_common/thread_local_dtor.rs index 6f5ebf4a271..f9971fb6f21 100644 --- a/library/std/src/sys_common/thread_local_dtor.rs +++ b/library/std/src/sys_common/thread_local_dtor.rs @@ -1,6 +1,6 @@ //! Thread-local destructor //! -//! Besides thread-local "keys" (pointer-sized non-adressable thread-local store +//! Besides thread-local "keys" (pointer-sized non-addressable thread-local store //! with an associated destructor), many platforms also provide thread-local //! destructors that are not associated with any particular data. These are //! often more efficient. |
