From cac1768b03c8d6673c51605dca03997876979d68 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 12 Jan 2020 18:14:29 +0100 Subject: [PATCH 01/31] First cut of `std::lazy` module --- src/libstd/lazy.rs | 1002 ++++++++++++++++++++++++++++++++++++++++++++ src/libstd/lib.rs | 6 + 2 files changed, 1008 insertions(+) create mode 100644 src/libstd/lazy.rs diff --git a/src/libstd/lazy.rs b/src/libstd/lazy.rs new file mode 100644 index 0000000000000..bcac2dc311aff --- /dev/null +++ b/src/libstd/lazy.rs @@ -0,0 +1,1002 @@ +//! `lazy` modules provides lazy values and one-time initialization of static data. +//! +//! `lazy` provides two new cell-like types, `OnceCell` and `SyncOnceCell`. `OnceCell` +//! might store arbitrary non-`Copy` types, can be assigned to at most once and provide direct access +//! to the stored contents. In a nutshell, API looks *roughly* like this: +//! +//! ```rust,ignore +//! impl OnceCell { +//! fn new() -> OnceCell { ... } +//! fn set(&self, value: T) -> Result<(), T> { ... } +//! fn get(&self) -> Option<&T> { ... } +//! } +//! ``` +//! +//! Note that, like with `RefCell` and `Mutex`, the `set` method requires only a shared reference. +//! Because of the single assignment restriction `get` can return an `&T` instead of `Ref` +//! or `MutexGuard`. +//! +//! The `SyncOnceCell` flavor is thread-safe (that is, implements [`Sync`]) trait, while `OnceCell` one is not. +//! +//! [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html +//! +//! # Patterns +//! +//! `OnceCell` might be useful for a variety of patterns. +//! +//! ## Safe Initialization of global data +//! +//! ```rust +//! use std::{env, io}; +//! use std::lazy::SyncOnceCell; +//! +//! #[derive(Debug)] +//! pub struct Logger { +//! // ... +//! } +//! static INSTANCE: OnceCell = OnceCell::new(); +//! +//! impl Logger { +//! pub fn global() -> &'static Logger { +//! INSTANCE.get().expect("logger is not initialized") +//! } +//! +//! fn from_cli(args: env::Args) -> Result { +//! // ... +//! # Ok(Logger {}) +//! } +//! } +//! +//! fn main() { +//! let logger = Logger::from_cli(env::args()).unwrap(); +//! INSTANCE.set(logger).unwrap(); +//! // use `Logger::global()` from now on +//! } +//! ``` +//! +//! ## Lazy initialized global data +//! +//! This is essentially `lazy_static!` macro, but without a macro. +//! +//! ```rust +//! use std::{sync::Mutex, collections::HashMap}; +//! use lazy::SyncOnceCell; +//! +//! fn global_data() -> &'static Mutex> { +//! static INSTANCE: OnceCell>> = OnceCell::new(); +//! INSTANCE.get_or_init(|| { +//! let mut m = HashMap::new(); +//! m.insert(13, "Spica".to_string()); +//! m.insert(74, "Hoyten".to_string()); +//! Mutex::new(m) +//! }) +//! } +//! ``` +//! +//! There are also `sync::Lazy` and `unsync::Lazy` convenience types to streamline this pattern: +//! +//! ```rust +//! use std::{sync::Mutex, collections::HashMap}; +//! use lazy::SyncLazy; +//! +//! static GLOBAL_DATA: Lazy>> = Lazy::new(|| { +//! let mut m = HashMap::new(); +//! m.insert(13, "Spica".to_string()); +//! m.insert(74, "Hoyten".to_string()); +//! Mutex::new(m) +//! }); +//! +//! fn main() { +//! println!("{:?}", GLOBAL_DATA.lock().unwrap()); +//! } +//! ``` +//! +//! ## General purpose lazy evaluation +//! +//! `Lazy` also works with local variables. +//! +//! ```rust +//! use std::lazy::Lazy; +//! +//! fn main() { +//! let ctx = vec![1, 2, 3]; +//! let thunk = Lazy::new(|| { +//! ctx.iter().sum::() +//! }); +//! assert_eq!(*thunk, 6); +//! } +//! ``` +//! +//! If you need a lazy field in a struct, you probably should use `OnceCell` +//! directly, because that will allow you to access `self` during initialization. +//! +//! ```rust +//! use std::{fs, path::PathBuf}; +//! +//! use std::lazy::OnceCell; +//! +//! struct Ctx { +//! config_path: PathBuf, +//! config: OnceCell, +//! } +//! +//! impl Ctx { +//! pub fn get_config(&self) -> Result<&str, std::io::Error> { +//! let cfg = self.config.get_or_try_init(|| { +//! fs::read_to_string(&self.config_path) +//! })?; +//! Ok(cfg.as_str()) +//! } +//! } +//! ``` +//! +//! ## Building block +//! +//! Naturally, it is possible to build other abstractions on top of `OnceCell`. +//! For example, this is a `regex!` macro which takes a string literal and returns an +//! *expression* that evaluates to a `&'static Regex`: +//! +//! ``` +//! macro_rules! regex { +//! ($re:literal $(,)?) => {{ +//! static RE: std::lazy::SyncOnceCell = std::lazy::SyncOnceCell::new(); +//! RE.get_or_init(|| regex::Regex::new($re).unwrap()) +//! }}; +//! } +//! ``` +//! +//! This macro can be useful to avoid "compile regex on every loop iteration" problem. +//! +//! # Comparison with other interior mutatbility types +//! +//! |`!Sync` types | Access Mode | Drawbacks | +//! |----------------------|------------------------|-----------------------------------------------| +//! |`Cell` | `T` | requires `T: Copy` for `get` | +//! |`RefCell` | `RefMut` / `Ref` | may panic at runtime | +//! |`OnceCell` | `&T` | assignable only once | +//! +//! |`Sync` types | Access Mode | Drawbacks | +//! |----------------------|------------------------|-----------------------------------------------| +//! |`AtomicT` | `T` | works only with certain `Copy` types | +//! |`Mutex` | `MutexGuard` | may deadlock at runtime, may block the thread | +//! |`SyncOnceCell` | `&T` | assignable only once, may block the thread | +//! +//! Technically, calling `get_or_init` will also cause a panic or a deadlock if it recursively calls +//! itself. However, because the assignment can happen only once, such cases should be more rare than +//! equivalents with `RefCell` and `Mutex`. + +use crate::{ + cell::{Cell, UnsafeCell}, + fmt, + hint::unreachable_unchecked, + marker::PhantomData, + ops::Deref, + panic::{RefUnwindSafe, UnwindSafe}, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, + thread::{self, Thread}, +}; + +/// A cell which can be written to only once. Not thread safe. +/// +/// Unlike `:td::cell::RefCell`, a `OnceCell` provides simple `&` +/// references to the contents. +/// +/// # Example +/// ``` +/// use std::lazy::OnceCell; +/// +/// let cell = OnceCell::new(); +/// assert!(cell.get().is_none()); +/// +/// let value: &String = cell.get_or_init(|| { +/// "Hello, World!".to_string() +/// }); +/// assert_eq!(value, "Hello, World!"); +/// assert!(cell.get().is_some()); +/// ``` +pub struct OnceCell { + // Invariant: written to at most once. + inner: UnsafeCell>, +} + +// Similarly to a `Sync` bound on `SyncOnceCell`, we can use +// `&OnceCell` to sneak a `T` through `catch_unwind`, +// by initializing the cell in closure and extracting the value in the +// `Drop`. +#[cfg(feature = "std")] +impl RefUnwindSafe for OnceCell {} +#[cfg(feature = "std")] +impl UnwindSafe for OnceCell {} + +impl Default for OnceCell { + fn default() -> Self { + Self::new() + } +} + +impl fmt::Debug for OnceCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.get() { + Some(v) => f.debug_tuple("OnceCell").field(v).finish(), + None => f.write_str("OnceCell(Uninit)"), + } + } +} + +impl Clone for OnceCell { + fn clone(&self) -> OnceCell { + let res = OnceCell::new(); + if let Some(value) = self.get() { + match res.set(value.clone()) { + Ok(()) => (), + Err(_) => unreachable!(), + } + } + res + } +} + +impl PartialEq for OnceCell { + fn eq(&self, other: &Self) -> bool { + self.get() == other.get() + } +} + +impl Eq for OnceCell {} + +impl From for OnceCell { + fn from(value: T) -> Self { + OnceCell { inner: UnsafeCell::new(Some(value)) } + } +} + +impl OnceCell { + /// Creates a new empty cell. + pub const fn new() -> OnceCell { + OnceCell { inner: UnsafeCell::new(None) } + } + + /// Gets the reference to the underlying value. + /// + /// Returns `None` if the cell is empty. + pub fn get(&self) -> Option<&T> { + // Safe due to `inner`'s invariant + unsafe { &*self.inner.get() }.as_ref() + } + + /// Gets the mutable reference to the underlying value. + /// + /// Returns `None` if the cell is empty. + pub fn get_mut(&mut self) -> Option<&mut T> { + // Safe because we have unique access + unsafe { &mut *self.inner.get() }.as_mut() + } + + /// Sets the contents of this cell to `value`. + /// + /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was + /// full. + /// + /// # Example + /// ``` + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// assert!(cell.get().is_none()); + /// + /// assert_eq!(cell.set(92), Ok(())); + /// assert_eq!(cell.set(62), Err(62)); + /// + /// assert!(cell.get().is_some()); + /// ``` + pub fn set(&self, value: T) -> Result<(), T> { + let slot = unsafe { &*self.inner.get() }; + if slot.is_some() { + return Err(value); + } + let slot = unsafe { &mut *self.inner.get() }; + // This is the only place where we set the slot, no races + // due to reentrancy/concurrency are possible, and we've + // checked that slot is currently `None`, so this write + // maintains the `inner`'s invariant. + *slot = Some(value); + Ok(()) + } + + /// Gets the contents of the cell, initializing it with `f` + /// if the cell was empty. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. Doing + /// so results in a panic. + /// + /// # Example + /// ``` + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// let value = cell.get_or_init(|| 92); + /// assert_eq!(value, &92); + /// let value = cell.get_or_init(|| unreachable!()); + /// assert_eq!(value, &92); + /// ``` + pub fn get_or_init(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + match self.get_or_try_init(|| Ok::(f())) { + Ok(val) => val, + } + } + + /// Gets the contents of the cell, initializing it with `f` if + /// the cell was empty. If the cell was empty and `f` failed, an + /// error is returned. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. Doing + /// so results in a panic. + /// + /// # Example + /// ``` + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + /// assert!(cell.get().is_none()); + /// let value = cell.get_or_try_init(|| -> Result { + /// Ok(92) + /// }); + /// assert_eq!(value, Ok(&92)); + /// assert_eq!(cell.get(), Some(&92)) + /// ``` + pub fn get_or_try_init(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + if let Some(val) = self.get() { + return Ok(val); + } + let val = f()?; + // Note that *some* forms of reentrant initialization might lead to + // UB (see `reentrant_init` test). I believe that just removing this + // `assert`, while keeping `set/get` would be sound, but it seems + // better to panic, rather than to silently use an old value. + assert!(self.set(val).is_ok(), "reentrant init"); + Ok(self.get().unwrap()) + } + + /// Consumes the `OnceCell`, returning the wrapped value. + /// + /// Returns `None` if the cell was empty. + /// + /// # Examples + /// + /// ``` + /// use std::lazy::OnceCell; + /// + /// let cell: OnceCell = OnceCell::new(); + /// assert_eq!(cell.into_inner(), None); + /// + /// let cell = OnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.into_inner(), Some("hello".to_string())); + /// ``` + pub fn into_inner(self) -> Option { + // Because `into_inner` takes `self` by value, the compiler statically verifies + // that it is not currently borrowed. So it is safe to move out `Option`. + self.inner.into_inner() + } +} + +/// A value which is initialized on the first access. +/// +/// # Example +/// ``` +/// use std::lazy::Lazy; +/// +/// let lazy: Lazy = Lazy::new(|| { +/// println!("initializing"); +/// 92 +/// }); +/// println!("ready"); +/// println!("{}", *lazy); +/// println!("{}", *lazy); +/// +/// // Prints: +/// // ready +/// // initializing +/// // 92 +/// // 92 +/// ``` +pub struct Lazy T> { + cell: OnceCell, + init: Cell>, +} + +#[cfg(feature = "std")] +impl RefUnwindSafe for Lazy where OnceCell: RefUnwindSafe {} + +impl fmt::Debug for Lazy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() + } +} + +impl Lazy { + /// Creates a new lazy value with the given initializing function. + /// + /// # Example + /// ``` + /// # fn main() { + /// use std::lazy::Lazy; + /// + /// let hello = "Hello, World!".to_string(); + /// + /// let lazy = Lazy::new(|| hello.to_uppercase()); + /// + /// assert_eq!(&*lazy, "HELLO, WORLD!"); + /// # } + /// ``` + pub const fn new(init: F) -> Lazy { + Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) } + } +} + +impl T> Lazy { + /// Forces the evaluation of this lazy value and returns a reference to + /// the result. + /// + /// This is equivalent to the `Deref` impl, but is explicit. + /// + /// # Example + /// ``` + /// use std::lazy::Lazy; + /// + /// let lazy = Lazy::new(|| 92); + /// + /// assert_eq!(Lazy::force(&lazy), &92); + /// assert_eq!(&*lazy, &92); + /// ``` + pub fn force(this: &Lazy) -> &T { + this.cell.get_or_init(|| match this.init.take() { + Some(f) => f(), + None => panic!("Lazy instance has previously been poisoned"), + }) + } +} + +impl T> Deref for Lazy { + type Target = T; + fn deref(&self) -> &T { + Lazy::force(self) + } +} + +impl Default for Lazy { + /// Creates a new lazy value using `Default` as the initializing function. + fn default() -> Lazy { + Lazy::new(T::default) + } +} + +/// A thread-safe cell which can be written to only once. +/// +/// `OnceCell` provides `&` references to the contents without RAII guards. +/// +/// Reading a non-`None` value out of `OnceCell` establishes a +/// happens-before relationship with a corresponding write. For example, if +/// thread A initializes the cell with `get_or_init(f)`, and thread B +/// subsequently reads the result of this call, B also observes all the side +/// effects of `f`. +/// +/// # Example +/// ``` +/// use std::lazy::SyncOnceCell; +/// +/// static CELL: OnceCell = OnceCell::new(); +/// assert!(CELL.get().is_none()); +/// +/// std::thread::spawn(|| { +/// let value: &String = CELL.get_or_init(|| { +/// "Hello, World!".to_string() +/// }); +/// assert_eq!(value, "Hello, World!"); +/// }).join().unwrap(); +/// +/// let value: Option<&String> = CELL.get(); +/// assert!(value.is_some()); +/// assert_eq!(value.unwrap().as_str(), "Hello, World!"); +/// ``` +pub struct SyncOnceCell { + // This `state` word is actually an encoded version of just a pointer to a + // `Waiter`, so we add the `PhantomData` appropriately. + state_and_queue: AtomicUsize, + _marker: PhantomData<*mut Waiter>, + // FIXME: switch to `std::mem::MaybeUninit` once we are ready to bump MSRV + // that far. It was stabilized in 1.36.0, so, if you are reading this and + // it's higher than 1.46.0 outside, please send a PR! ;) (and do the same + // for `Lazy`, while we are at it). + pub(crate) value: UnsafeCell>, +} + +// Why do we need `T: Send`? +// Thread A creates a `OnceCell` and shares it with +// scoped thread B, which fills the cell, which is +// then destroyed by A. That is, destructor observes +// a sent value. +unsafe impl Sync for SyncOnceCell {} +unsafe impl Send for SyncOnceCell {} + +impl RefUnwindSafe for SyncOnceCell {} +impl UnwindSafe for SyncOnceCell {} + +impl Default for SyncOnceCell { + fn default() -> SyncOnceCell { + SyncOnceCell::new() + } +} + +impl fmt::Debug for SyncOnceCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.get() { + Some(v) => f.debug_tuple("SyncOnceCell").field(v).finish(), + None => f.write_str("SyncOnceCell(Uninit)"), + } + } +} + +impl Clone for SyncOnceCell { + fn clone(&self) -> SyncOnceCell { + let res = SyncOnceCell::new(); + if let Some(value) = self.get() { + match res.set(value.clone()) { + Ok(()) => (), + Err(_) => unreachable!(), + } + } + res + } +} + +impl From for SyncOnceCell { + fn from(value: T) -> Self { + let cell = Self::new(); + cell.get_or_init(|| value); + cell + } +} + +impl PartialEq for SyncOnceCell { + fn eq(&self, other: &SyncOnceCell) -> bool { + self.get() == other.get() + } +} + +impl Eq for SyncOnceCell {} + +impl SyncOnceCell { + /// Creates a new empty cell. + pub const fn new() -> SyncOnceCell { + SyncOnceCell { + state_and_queue: AtomicUsize::new(INCOMPLETE), + _marker: PhantomData, + value: UnsafeCell::new(None), + } + } + + /// Gets the reference to the underlying value. + /// + /// Returns `None` if the cell is empty, or being initialized. This + /// method never blocks. + pub fn get(&self) -> Option<&T> { + if self.is_initialized() { + // Safe b/c checked is_initialize + Some(unsafe { self.get_unchecked() }) + } else { + None + } + } + + /// Gets the mutable reference to the underlying value. + /// + /// Returns `None` if the cell is empty. + pub fn get_mut(&mut self) -> Option<&mut T> { + // Safe b/c we have a unique access. + unsafe { &mut *self.value.get() }.as_mut() + } + + /// Get the reference to the underlying value, without checking if the + /// cell is initialized. + /// + /// Safety: + /// + /// Caller must ensure that the cell is in initialized state, and that + /// the contents are acquired by (synchronized to) this thread. + pub unsafe fn get_unchecked(&self) -> &T { + debug_assert!(self.is_initialized()); + let slot: &Option = &*self.value.get(); + match slot { + Some(value) => value, + // This unsafe does improve performance, see `examples/bench`. + None => { + debug_assert!(false); + unreachable_unchecked() + } + } + } + + /// Sets the contents of this cell to `value`. + /// + /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was + /// full. + /// + /// # Example + /// ``` + /// use std::lazy::SyncOnceCell; + /// + /// static CELL: SyncOnceCell = SyncOnceCell::new(); + /// + /// fn main() { + /// assert!(CELL.get().is_none()); + /// + /// std::thread::spawn(|| { + /// assert_eq!(CELL.set(92), Ok(())); + /// }).join().unwrap(); + /// + /// assert_eq!(CELL.set(62), Err(62)); + /// assert_eq!(CELL.get(), Some(&92)); + /// } + /// ``` + pub fn set(&self, value: T) -> Result<(), T> { + let mut value = Some(value); + self.get_or_init(|| value.take().unwrap()); + match value { + None => Ok(()), + Some(value) => Err(value), + } + } + + /// Gets the contents of the cell, initializing it with `f` if the cell + /// was empty. + /// + /// Many threads may call `get_or_init` concurrently with different + /// initializing functions, but it is guaranteed that only one function + /// will be executed. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. The + /// exact outcome is unspecified. Current implementation deadlocks, but + /// this may be changed to a panic in the future. + /// + /// # Example + /// ``` + /// use std::lazy::SyncOnceCell; + /// + /// let cell = SyncOnceCell::new(); + /// let value = cell.get_or_init(|| 92); + /// assert_eq!(value, &92); + /// let value = cell.get_or_init(|| unreachable!()); + /// assert_eq!(value, &92); + /// ``` + pub fn get_or_init(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + match self.get_or_try_init(|| Ok::(f())) { + Ok(val) => val, + } + } + + /// Gets the contents of the cell, initializing it with `f` if + /// the cell was empty. If the cell was empty and `f` failed, an + /// error is returned. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and + /// the cell remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. + /// The exact outcome is unspecified. Current implementation + /// deadlocks, but this may be changed to a panic in the future. + /// + /// # Example + /// ``` + /// use std::lazy::SyncOnceCell; + /// + /// let cell = SyncOnceCell::new(); + /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + /// assert!(cell.get().is_none()); + /// let value = cell.get_or_try_init(|| -> Result { + /// Ok(92) + /// }); + /// assert_eq!(value, Ok(&92)); + /// assert_eq!(cell.get(), Some(&92)) + /// ``` + pub fn get_or_try_init(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + // Fast path check + if let Some(value) = self.get() { + return Ok(value); + } + self.initialize(f)?; + + // Safe b/c called initialize + debug_assert!(self.is_initialized()); + Ok(unsafe { self.get_unchecked() }) + } + + /// Consumes the `SyncOnceCell`, returning the wrapped value. Returns + /// `None` if the cell was empty. + /// + /// # Examples + /// + /// ``` + /// use std::lazy::SyncOnceCell; + /// + /// let cell: SyncOnceCell = SyncOnceCell::new(); + /// assert_eq!(cell.into_inner(), None); + /// + /// let cell = SyncOnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.into_inner(), Some("hello".to_string())); + /// ``` + pub fn into_inner(self) -> Option { + // Because `into_inner` takes `self` by value, the compiler statically verifies + // that it is not currently borrowed. So it is safe to move out `Option`. + self.value.into_inner() + } + + /// Safety: synchronizes with store to value via Release/(Acquire|SeqCst). + #[inline] + fn is_initialized(&self) -> bool { + // An `Acquire` load is enough because that makes all the initialization + // operations visible to us, and, this being a fast path, weaker + // ordering helps with performance. This `Acquire` synchronizes with + // `SeqCst` operations on the slow path. + self.state_and_queue.load(Ordering::Acquire) == COMPLETE + } + + /// Safety: synchronizes with store to value via SeqCst read from state, + /// writes value only once because we never get to INCOMPLETE state after a + /// successful write. + #[cold] + fn initialize(&self, f: F) -> Result<(), E> + where + F: FnOnce() -> Result, + { + let mut f = Some(f); + let mut res: Result<(), E> = Ok(()); + let slot = &self.value; + initialize_inner(&self.state_and_queue, &mut || { + let f = f.take().unwrap(); + match f() { + Ok(value) => { + unsafe { *slot.get() = Some(value) }; + true + } + Err(e) => { + res = Err(e); + false + } + } + }); + res + } +} + +// region: copy-paste +// The following code is copied from `sync::Once`. +// This should be uncopypasted once we decide the right way to handle panics. +const INCOMPLETE: usize = 0x0; +const RUNNING: usize = 0x1; +const COMPLETE: usize = 0x2; + +const STATE_MASK: usize = 0x3; + + +#[repr(align(4))] +struct Waiter { + thread: Cell>, + signaled: AtomicBool, + next: *const Waiter, +} + +struct WaiterQueue<'a> { + state_and_queue: &'a AtomicUsize, + set_state_on_drop_to: usize, +} + +impl Drop for WaiterQueue<'_> { + fn drop(&mut self) { + let state_and_queue = + self.state_and_queue.swap(self.set_state_on_drop_to, Ordering::AcqRel); + + assert_eq!(state_and_queue & STATE_MASK, RUNNING); + + unsafe { + let mut queue = (state_and_queue & !STATE_MASK) as *const Waiter; + while !queue.is_null() { + let next = (*queue).next; + let thread = (*queue).thread.replace(None).unwrap(); + (*queue).signaled.store(true, Ordering::Release); + queue = next; + thread.unpark(); + } + } + } +} + +fn initialize_inner(my_state_and_queue: &AtomicUsize, init: &mut dyn FnMut() -> bool) -> bool { + let mut state_and_queue = my_state_and_queue.load(Ordering::Acquire); + + loop { + match state_and_queue { + COMPLETE => return true, + INCOMPLETE => { + let old = my_state_and_queue.compare_and_swap( + state_and_queue, + RUNNING, + Ordering::Acquire, + ); + if old != state_and_queue { + state_and_queue = old; + continue; + } + let mut waiter_queue = WaiterQueue { + state_and_queue: my_state_and_queue, + set_state_on_drop_to: INCOMPLETE, + }; + let success = init(); + + waiter_queue.set_state_on_drop_to = if success { COMPLETE } else { INCOMPLETE }; + return success; + } + _ => { + assert!(state_and_queue & STATE_MASK == RUNNING); + wait(&my_state_and_queue, state_and_queue); + state_and_queue = my_state_and_queue.load(Ordering::Acquire); + } + } + } +} + +fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) { + loop { + if current_state & STATE_MASK != RUNNING { + return; + } + + let node = Waiter { + thread: Cell::new(Some(thread::current())), + signaled: AtomicBool::new(false), + next: (current_state & !STATE_MASK) as *const Waiter, + }; + let me = &node as *const Waiter as usize; + + let old = state_and_queue.compare_and_swap(current_state, me | RUNNING, Ordering::Release); + if old != current_state { + current_state = old; + continue; + } + + while !node.signaled.load(Ordering::Acquire) { + thread::park(); + } + break; + } +} +// endregion: copy-paste + +/// A value which is initialized on the first access. +/// +/// This type is thread-safe and can be used in statics: +/// +/// # Example +/// ``` +/// use std::collections::HashMap; +/// +/// use std::lazy::Lazy; +/// +/// static HASHMAP: Lazy> = Lazy::new(|| { +/// println!("initializing"); +/// let mut m = HashMap::new(); +/// m.insert(13, "Spica".to_string()); +/// m.insert(74, "Hoyten".to_string()); +/// m +/// }); +/// +/// fn main() { +/// println!("ready"); +/// std::thread::spawn(|| { +/// println!("{:?}", HASHMAP.get(&13)); +/// }).join().unwrap(); +/// println!("{:?}", HASHMAP.get(&74)); +/// +/// // Prints: +/// // ready +/// // initializing +/// // Some("Spica") +/// // Some("Hoyten") +/// } +/// ``` +pub struct SyncLazy T> { + cell: SyncOnceCell, + init: Cell>, +} + +impl fmt::Debug for SyncLazy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SyncLazy").field("cell", &self.cell).field("init", &"..").finish() + } +} + +// We never create a `&F` from a `&SyncLazy` so it is fine +// to not impl `Sync` for `F` +// we do create a `&mut Option` in `force`, but this is +// properly synchronized, so it only happens once +// so it also does not contribute to this impl. +unsafe impl Sync for SyncLazy where SyncOnceCell: Sync {} +// auto-derived `Send` impl is OK. + +#[cfg(feature = "std")] +impl RefUnwindSafe for SyncLazy where SyncOnceCell: RefUnwindSafe {} + +impl SyncLazy { + /// Creates a new lazy value with the given initializing + /// function. + pub const fn new(f: F) -> SyncLazy { + SyncLazy { cell: SyncOnceCell::new(), init: Cell::new(Some(f)) } + } +} + +impl T> SyncLazy { + /// Forces the evaluation of this lazy value and + /// returns a reference to result. This is equivalent + /// to the `Deref` impl, but is explicit. + /// + /// # Example + /// ``` + /// use std::lazy::SyncLazy; + /// + /// let lazy = SyncLazy::new(|| 92); + /// + /// assert_eq!(SyncLazy::force(&lazy), &92); + /// assert_eq!(&*lazy, &92); + /// ``` + pub fn force(this: &SyncLazy) -> &T { + this.cell.get_or_init(|| match this.init.take() { + Some(f) => f(), + None => panic!("SyncLazy instance has previously been poisoned"), + }) + } +} + +impl T> Deref for SyncLazy { + type Target = T; + fn deref(&self) -> &T { + SyncLazy::force(self) + } +} + +impl Default for SyncLazy { + /// Creates a new lazy value using `Default` as the initializing function. + fn default() -> SyncLazy { + SyncLazy::new(T::default) + } +} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 4fd5e238eea11..171d6c3f64f87 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -476,6 +476,12 @@ pub mod process; pub mod sync; pub mod time; +#[unstable( + feature = "std_lazy", + issue = "99", +)] +pub mod lazy; + #[stable(feature = "futures_api", since = "1.36.0")] pub mod task { //! Types and Traits for working with asynchronous tasks. From 237a97760ad79a21ce0655b9f5adc0cc5b5cbc79 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 14 Jan 2020 13:34:23 +1000 Subject: [PATCH 02/31] integrate Lazy into std layout This commit refactors the initial implementation to fit into std and makes some other changes: - use MaybeUninit internally in SyncOnceCell - correctly impl Drop for lazy::Once - port Lazy::take from once_cell from: https://github.com/matklad/once_cell/pull/100 Co-Authored-By: Paul Dicker --- Cargo.lock | 2 +- src/libcore/lazy.rs | 379 ++++++++++++++ src/libcore/lib.rs | 2 + src/libcore/tests/lazy.rs | 124 +++++ src/libcore/tests/lib.rs | 2 + src/libstd/lazy.rs | 1032 ++++++++++++++++++------------------- src/libstd/lib.rs | 7 +- 7 files changed, 1002 insertions(+), 546 deletions(-) create mode 100644 src/libcore/lazy.rs create mode 100644 src/libcore/tests/lazy.rs diff --git a/Cargo.lock b/Cargo.lock index 5309c03ee23ae..2027b5200eb70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1372,8 +1372,8 @@ checksum = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" name = "installer" version = "0.0.0" dependencies = [ - "anyhow", "clap", + "failure", "flate2", "lazy_static", "num_cpus", diff --git a/src/libcore/lazy.rs b/src/libcore/lazy.rs new file mode 100644 index 0000000000000..e9af66ff64264 --- /dev/null +++ b/src/libcore/lazy.rs @@ -0,0 +1,379 @@ +//! Lazy values and one-time initialization of static data. + +use crate::cell::{Cell, UnsafeCell}; +use crate::fmt; +use crate::mem; +use crate::ops::Deref; + +/// A cell which can be written to only once. +/// +/// Unlike `RefCell`, a `OnceCell` only provides shared `&T` references to its value. +/// Unlike `Cell`, a `OnceCell` doesn't require copying or replacing the value to access it. +/// +/// # Examples +/// +/// ``` +/// #![feature(once_cell)] +/// +/// use std::lazy::OnceCell; +/// +/// let cell = OnceCell::new(); +/// assert!(cell.get().is_none()); +/// +/// let value: &String = cell.get_or_init(|| { +/// "Hello, World!".to_string() +/// }); +/// assert_eq!(value, "Hello, World!"); +/// assert!(cell.get().is_some()); +/// ``` +#[unstable(feature = "once_cell", issue = "68198")] +pub struct OnceCell { + // Invariant: written to at most once. + inner: UnsafeCell>, +} + +#[unstable(feature = "once_cell", issue = "68198")] +impl Default for OnceCell { + fn default() -> Self { + Self::new() + } +} + +#[unstable(feature = "once_cell", issue = "68198")] +impl fmt::Debug for OnceCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.get() { + Some(v) => f.debug_tuple("OnceCell").field(v).finish(), + None => f.write_str("OnceCell(Uninit)"), + } + } +} + +#[unstable(feature = "once_cell", issue = "68198")] +impl Clone for OnceCell { + fn clone(&self) -> OnceCell { + let res = OnceCell::new(); + if let Some(value) = self.get() { + match res.set(value.clone()) { + Ok(()) => (), + Err(_) => unreachable!(), + } + } + res + } +} + +#[unstable(feature = "once_cell", issue = "68198")] +impl PartialEq for OnceCell { + fn eq(&self, other: &Self) -> bool { + self.get() == other.get() + } +} + +#[unstable(feature = "once_cell", issue = "68198")] +impl Eq for OnceCell {} + +#[unstable(feature = "once_cell", issue = "68198")] +impl From for OnceCell { + fn from(value: T) -> Self { + OnceCell { inner: UnsafeCell::new(Some(value)) } + } +} + +impl OnceCell { + /// Creates a new empty cell. + #[unstable(feature = "once_cell", issue = "68198")] + pub const fn new() -> OnceCell { + OnceCell { inner: UnsafeCell::new(None) } + } + + /// Gets the reference to the underlying value. + /// + /// Returns `None` if the cell is empty. + #[unstable(feature = "once_cell", issue = "68198")] + pub fn get(&self) -> Option<&T> { + // Safety: Safe due to `inner`'s invariant + unsafe { &*self.inner.get() }.as_ref() + } + + /// Gets the mutable reference to the underlying value. + /// + /// Returns `None` if the cell is empty. + #[unstable(feature = "once_cell", issue = "68198")] + pub fn get_mut(&mut self) -> Option<&mut T> { + // Safety: Safe because we have unique access + unsafe { &mut *self.inner.get() }.as_mut() + } + + /// Sets the contents of the cell to `value`. + /// + /// # Errors + /// + /// This method returns `Ok(())` if the cell was empty and `Err(value)` if + /// it was full. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// assert!(cell.get().is_none()); + /// + /// assert_eq!(cell.set(92), Ok(())); + /// assert_eq!(cell.set(62), Err(62)); + /// + /// assert!(cell.get().is_some()); + /// ``` + #[unstable(feature = "once_cell", issue = "68198")] + pub fn set(&self, value: T) -> Result<(), T> { + // Safety: Safe because we cannot have overlapping mutable borrows + let slot = unsafe { &*self.inner.get() }; + if slot.is_some() { + return Err(value); + } + + // Safety: This is the only place where we set the slot, no races + // due to reentrancy/concurrency are possible, and we've + // checked that slot is currently `None`, so this write + // maintains the `inner`'s invariant. + let slot = unsafe { &mut *self.inner.get() }; + *slot = Some(value); + Ok(()) + } + + /// Gets the contents of the cell, initializing it with `f` + /// if the cell was empty. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. Doing + /// so results in a panic. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// let value = cell.get_or_init(|| 92); + /// assert_eq!(value, &92); + /// let value = cell.get_or_init(|| unreachable!()); + /// assert_eq!(value, &92); + /// ``` + #[unstable(feature = "once_cell", issue = "68198")] + pub fn get_or_init(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + match self.get_or_try_init(|| Ok::(f())) { + Ok(val) => val, + } + } + + /// Gets the contents of the cell, initializing it with `f` if + /// the cell was empty. If the cell was empty and `f` failed, an + /// error is returned. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. Doing + /// so results in a panic. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + /// assert!(cell.get().is_none()); + /// let value = cell.get_or_try_init(|| -> Result { + /// Ok(92) + /// }); + /// assert_eq!(value, Ok(&92)); + /// assert_eq!(cell.get(), Some(&92)) + /// ``` + #[unstable(feature = "once_cell", issue = "68198")] + pub fn get_or_try_init(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + if let Some(val) = self.get() { + return Ok(val); + } + let val = f()?; + // Note that *some* forms of reentrant initialization might lead to + // UB (see `reentrant_init` test). I believe that just removing this + // `assert`, while keeping `set/get` would be sound, but it seems + // better to panic, rather than to silently use an old value. + assert!(self.set(val).is_ok(), "reentrant init"); + Ok(self.get().unwrap()) + } + + /// Consumes the cell, returning the wrapped value. + /// + /// Returns `None` if the cell was empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell: OnceCell = OnceCell::new(); + /// assert_eq!(cell.into_inner(), None); + /// + /// let cell = OnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.into_inner(), Some("hello".to_string())); + /// ``` + #[unstable(feature = "once_cell", issue = "68198")] + pub fn into_inner(self) -> Option { + // Because `into_inner` takes `self` by value, the compiler statically verifies + // that it is not currently borrowed. So it is safe to move out `Option`. + self.inner.into_inner() + } + + /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state. + /// + /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized. + /// + /// Safety is guaranteed by requiring a mutable reference. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let mut cell: OnceCell = OnceCell::new(); + /// assert_eq!(cell.take(), None); + /// + /// let mut cell = OnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.take(), Some("hello".to_string())); + /// assert_eq!(cell.get(), None); + /// ``` + #[unstable(feature = "once_cell", issue = "68198")] + pub fn take(&mut self) -> Option { + mem::take(self).into_inner() + } +} + +/// A value which is initialized on the first access. +/// +/// # Examples +/// +/// ``` +/// #![feature(once_cell)] +/// +/// use std::lazy::Lazy; +/// +/// let lazy: Lazy = Lazy::new(|| { +/// println!("initializing"); +/// 92 +/// }); +/// println!("ready"); +/// println!("{}", *lazy); +/// println!("{}", *lazy); +/// +/// // Prints: +/// // ready +/// // initializing +/// // 92 +/// // 92 +/// ``` +#[unstable(feature = "once_cell", issue = "68198")] +pub struct Lazy T> { + cell: OnceCell, + init: Cell>, +} + +#[unstable(feature = "once_cell", issue = "68198")] +impl fmt::Debug for Lazy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() + } +} + +impl Lazy { + /// Creates a new lazy value with the given initializing function. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// # fn main() { + /// use std::lazy::Lazy; + /// + /// let hello = "Hello, World!".to_string(); + /// + /// let lazy = Lazy::new(|| hello.to_uppercase()); + /// + /// assert_eq!(&*lazy, "HELLO, WORLD!"); + /// # } + /// ``` + #[unstable(feature = "once_cell", issue = "68198")] + pub const fn new(init: F) -> Lazy { + Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) } + } +} + +impl T> Lazy { + /// Forces the evaluation of this lazy value and returns a reference to + /// the result. + /// + /// This is equivalent to the `Deref` impl, but is explicit. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::Lazy; + /// + /// let lazy = Lazy::new(|| 92); + /// + /// assert_eq!(Lazy::force(&lazy), &92); + /// assert_eq!(&*lazy, &92); + /// ``` + #[unstable(feature = "once_cell", issue = "68198")] + pub fn force(this: &Lazy) -> &T { + this.cell.get_or_init(|| match this.init.take() { + Some(f) => f(), + None => panic!("`Lazy` instance has previously been poisoned"), + }) + } +} + +#[unstable(feature = "once_cell", issue = "68198")] +impl T> Deref for Lazy { + type Target = T; + fn deref(&self) -> &T { + Lazy::force(self) + } +} + +#[unstable(feature = "once_cell", issue = "68198")] +impl Default for Lazy { + /// Creates a new lazy value using `Default` as the initializing function. + fn default() -> Lazy { + Lazy::new(T::default) + } +} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 88991dea7d43e..1e6a27cff0ce1 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -239,6 +239,8 @@ pub mod char; pub mod ffi; #[cfg(not(test))] // See #65860 pub mod iter; +#[unstable(feature = "once_cell", issue = "68198")] +pub mod lazy; pub mod option; pub mod panic; pub mod panicking; diff --git a/src/libcore/tests/lazy.rs b/src/libcore/tests/lazy.rs new file mode 100644 index 0000000000000..1c0bddb9aef62 --- /dev/null +++ b/src/libcore/tests/lazy.rs @@ -0,0 +1,124 @@ +use core::{ + cell::Cell, + lazy::{Lazy, OnceCell}, + sync::atomic::{AtomicUsize, Ordering::SeqCst}, +}; + +#[test] +fn once_cell() { + let c = OnceCell::new(); + assert!(c.get().is_none()); + c.get_or_init(|| 92); + assert_eq!(c.get(), Some(&92)); + + c.get_or_init(|| panic!("Kabom!")); + assert_eq!(c.get(), Some(&92)); +} + +#[test] +fn once_cell_get_mut() { + let mut c = OnceCell::new(); + assert!(c.get_mut().is_none()); + c.set(90).unwrap(); + *c.get_mut().unwrap() += 2; + assert_eq!(c.get_mut(), Some(&mut 92)); +} + +#[test] +fn once_cell_drop() { + static DROP_CNT: AtomicUsize = AtomicUsize::new(0); + struct Dropper; + impl Drop for Dropper { + fn drop(&mut self) { + DROP_CNT.fetch_add(1, SeqCst); + } + } + + let x = OnceCell::new(); + x.get_or_init(|| Dropper); + assert_eq!(DROP_CNT.load(SeqCst), 0); + drop(x); + assert_eq!(DROP_CNT.load(SeqCst), 1); +} + +#[test] +fn unsync_once_cell_drop_empty() { + let x = OnceCell::<&'static str>::new(); + drop(x); +} + +#[test] +fn clone() { + let s = OnceCell::new(); + let c = s.clone(); + assert!(c.get().is_none()); + + s.set("hello").unwrap(); + let c = s.clone(); + assert_eq!(c.get().map(|c| *c), Some("hello")); +} + +#[test] +fn from_impl() { + assert_eq!(OnceCell::from("value").get(), Some(&"value")); + assert_ne!(OnceCell::from("foo").get(), Some(&"bar")); +} + +#[test] +fn partialeq_impl() { + assert!(OnceCell::from("value") == OnceCell::from("value")); + assert!(OnceCell::from("foo") != OnceCell::from("bar")); + + assert!(OnceCell::<&'static str>::new() == OnceCell::new()); + assert!(OnceCell::<&'static str>::new() != OnceCell::from("value")); +} + +#[test] +fn into_inner() { + let cell: OnceCell<&'static str> = OnceCell::new(); + assert_eq!(cell.into_inner(), None); + let cell = OnceCell::new(); + cell.set("hello").unwrap(); + assert_eq!(cell.into_inner(), Some("hello")); +} + +#[test] +fn lazy_new() { + let called = Cell::new(0); + let x = Lazy::new(|| { + called.set(called.get() + 1); + 92 + }); + + assert_eq!(called.get(), 0); + + let y = *x - 30; + assert_eq!(y, 62); + assert_eq!(called.get(), 1); + + let y = *x - 30; + assert_eq!(y, 62); + assert_eq!(called.get(), 1); +} + +#[test] +fn aliasing_in_get() { + let x = OnceCell::new(); + x.set(42).unwrap(); + let at_x = x.get().unwrap(); // --- (shared) borrow of inner `Option` --+ + let _ = x.set(27); // <-- temporary (unique) borrow of inner `Option` | + println!("{}", at_x); // <------- up until here ---------------------------+ +} + +#[test] +#[should_panic(expected = "reentrant init")] +fn reentrant_init() { + let x: OnceCell> = OnceCell::new(); + let dangling_ref: Cell> = Cell::new(None); + x.get_or_init(|| { + let r = x.get_or_init(|| Box::new(92)); + dangling_ref.set(Some(r)); + Box::new(62) + }); + eprintln!("use after free: {:?}", dangling_ref.get().unwrap()); +} diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 090ce47174566..47ed6db6c677b 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -43,6 +43,7 @@ #![feature(option_unwrap_none)] #![feature(peekable_next_if)] #![feature(partition_point)] +#![feature(once_cell)] #![feature(unsafe_block_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)] @@ -62,6 +63,7 @@ mod fmt; mod hash; mod intrinsics; mod iter; +mod lazy; mod manually_drop; mod mem; mod nonzero; diff --git a/src/libstd/lazy.rs b/src/libstd/lazy.rs index bcac2dc311aff..c7553caa4c056 100644 --- a/src/libstd/lazy.rs +++ b/src/libstd/lazy.rs @@ -1,508 +1,32 @@ -//! `lazy` modules provides lazy values and one-time initialization of static data. -//! -//! `lazy` provides two new cell-like types, `OnceCell` and `SyncOnceCell`. `OnceCell` -//! might store arbitrary non-`Copy` types, can be assigned to at most once and provide direct access -//! to the stored contents. In a nutshell, API looks *roughly* like this: -//! -//! ```rust,ignore -//! impl OnceCell { -//! fn new() -> OnceCell { ... } -//! fn set(&self, value: T) -> Result<(), T> { ... } -//! fn get(&self) -> Option<&T> { ... } -//! } -//! ``` -//! -//! Note that, like with `RefCell` and `Mutex`, the `set` method requires only a shared reference. -//! Because of the single assignment restriction `get` can return an `&T` instead of `Ref` -//! or `MutexGuard`. -//! -//! The `SyncOnceCell` flavor is thread-safe (that is, implements [`Sync`]) trait, while `OnceCell` one is not. -//! -//! [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html -//! -//! # Patterns -//! -//! `OnceCell` might be useful for a variety of patterns. -//! -//! ## Safe Initialization of global data -//! -//! ```rust -//! use std::{env, io}; -//! use std::lazy::SyncOnceCell; -//! -//! #[derive(Debug)] -//! pub struct Logger { -//! // ... -//! } -//! static INSTANCE: OnceCell = OnceCell::new(); -//! -//! impl Logger { -//! pub fn global() -> &'static Logger { -//! INSTANCE.get().expect("logger is not initialized") -//! } -//! -//! fn from_cli(args: env::Args) -> Result { -//! // ... -//! # Ok(Logger {}) -//! } -//! } -//! -//! fn main() { -//! let logger = Logger::from_cli(env::args()).unwrap(); -//! INSTANCE.set(logger).unwrap(); -//! // use `Logger::global()` from now on -//! } -//! ``` -//! -//! ## Lazy initialized global data -//! -//! This is essentially `lazy_static!` macro, but without a macro. -//! -//! ```rust -//! use std::{sync::Mutex, collections::HashMap}; -//! use lazy::SyncOnceCell; -//! -//! fn global_data() -> &'static Mutex> { -//! static INSTANCE: OnceCell>> = OnceCell::new(); -//! INSTANCE.get_or_init(|| { -//! let mut m = HashMap::new(); -//! m.insert(13, "Spica".to_string()); -//! m.insert(74, "Hoyten".to_string()); -//! Mutex::new(m) -//! }) -//! } -//! ``` -//! -//! There are also `sync::Lazy` and `unsync::Lazy` convenience types to streamline this pattern: -//! -//! ```rust -//! use std::{sync::Mutex, collections::HashMap}; -//! use lazy::SyncLazy; -//! -//! static GLOBAL_DATA: Lazy>> = Lazy::new(|| { -//! let mut m = HashMap::new(); -//! m.insert(13, "Spica".to_string()); -//! m.insert(74, "Hoyten".to_string()); -//! Mutex::new(m) -//! }); -//! -//! fn main() { -//! println!("{:?}", GLOBAL_DATA.lock().unwrap()); -//! } -//! ``` -//! -//! ## General purpose lazy evaluation -//! -//! `Lazy` also works with local variables. -//! -//! ```rust -//! use std::lazy::Lazy; -//! -//! fn main() { -//! let ctx = vec![1, 2, 3]; -//! let thunk = Lazy::new(|| { -//! ctx.iter().sum::() -//! }); -//! assert_eq!(*thunk, 6); -//! } -//! ``` -//! -//! If you need a lazy field in a struct, you probably should use `OnceCell` -//! directly, because that will allow you to access `self` during initialization. -//! -//! ```rust -//! use std::{fs, path::PathBuf}; -//! -//! use std::lazy::OnceCell; -//! -//! struct Ctx { -//! config_path: PathBuf, -//! config: OnceCell, -//! } -//! -//! impl Ctx { -//! pub fn get_config(&self) -> Result<&str, std::io::Error> { -//! let cfg = self.config.get_or_try_init(|| { -//! fs::read_to_string(&self.config_path) -//! })?; -//! Ok(cfg.as_str()) -//! } -//! } -//! ``` -//! -//! ## Building block -//! -//! Naturally, it is possible to build other abstractions on top of `OnceCell`. -//! For example, this is a `regex!` macro which takes a string literal and returns an -//! *expression* that evaluates to a `&'static Regex`: -//! -//! ``` -//! macro_rules! regex { -//! ($re:literal $(,)?) => {{ -//! static RE: std::lazy::SyncOnceCell = std::lazy::SyncOnceCell::new(); -//! RE.get_or_init(|| regex::Regex::new($re).unwrap()) -//! }}; -//! } -//! ``` -//! -//! This macro can be useful to avoid "compile regex on every loop iteration" problem. -//! -//! # Comparison with other interior mutatbility types -//! -//! |`!Sync` types | Access Mode | Drawbacks | -//! |----------------------|------------------------|-----------------------------------------------| -//! |`Cell` | `T` | requires `T: Copy` for `get` | -//! |`RefCell` | `RefMut` / `Ref` | may panic at runtime | -//! |`OnceCell` | `&T` | assignable only once | -//! -//! |`Sync` types | Access Mode | Drawbacks | -//! |----------------------|------------------------|-----------------------------------------------| -//! |`AtomicT` | `T` | works only with certain `Copy` types | -//! |`Mutex` | `MutexGuard` | may deadlock at runtime, may block the thread | -//! |`SyncOnceCell` | `&T` | assignable only once, may block the thread | -//! -//! Technically, calling `get_or_init` will also cause a panic or a deadlock if it recursively calls -//! itself. However, because the assignment can happen only once, such cases should be more rare than -//! equivalents with `RefCell` and `Mutex`. +//! Lazy values and one-time initialization of static data. use crate::{ cell::{Cell, UnsafeCell}, fmt, - hint::unreachable_unchecked, marker::PhantomData, - ops::Deref, + mem::{self, MaybeUninit}, + ops::{Deref, Drop}, panic::{RefUnwindSafe, UnwindSafe}, sync::atomic::{AtomicBool, AtomicUsize, Ordering}, thread::{self, Thread}, }; -/// A cell which can be written to only once. Not thread safe. -/// -/// Unlike `:td::cell::RefCell`, a `OnceCell` provides simple `&` -/// references to the contents. -/// -/// # Example -/// ``` -/// use std::lazy::OnceCell; -/// -/// let cell = OnceCell::new(); -/// assert!(cell.get().is_none()); -/// -/// let value: &String = cell.get_or_init(|| { -/// "Hello, World!".to_string() -/// }); -/// assert_eq!(value, "Hello, World!"); -/// assert!(cell.get().is_some()); -/// ``` -pub struct OnceCell { - // Invariant: written to at most once. - inner: UnsafeCell>, -} - -// Similarly to a `Sync` bound on `SyncOnceCell`, we can use -// `&OnceCell` to sneak a `T` through `catch_unwind`, -// by initializing the cell in closure and extracting the value in the -// `Drop`. -#[cfg(feature = "std")] -impl RefUnwindSafe for OnceCell {} -#[cfg(feature = "std")] -impl UnwindSafe for OnceCell {} - -impl Default for OnceCell { - fn default() -> Self { - Self::new() - } -} - -impl fmt::Debug for OnceCell { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.get() { - Some(v) => f.debug_tuple("OnceCell").field(v).finish(), - None => f.write_str("OnceCell(Uninit)"), - } - } -} - -impl Clone for OnceCell { - fn clone(&self) -> OnceCell { - let res = OnceCell::new(); - if let Some(value) = self.get() { - match res.set(value.clone()) { - Ok(()) => (), - Err(_) => unreachable!(), - } - } - res - } -} - -impl PartialEq for OnceCell { - fn eq(&self, other: &Self) -> bool { - self.get() == other.get() - } -} - -impl Eq for OnceCell {} - -impl From for OnceCell { - fn from(value: T) -> Self { - OnceCell { inner: UnsafeCell::new(Some(value)) } - } -} - -impl OnceCell { - /// Creates a new empty cell. - pub const fn new() -> OnceCell { - OnceCell { inner: UnsafeCell::new(None) } - } - - /// Gets the reference to the underlying value. - /// - /// Returns `None` if the cell is empty. - pub fn get(&self) -> Option<&T> { - // Safe due to `inner`'s invariant - unsafe { &*self.inner.get() }.as_ref() - } - - /// Gets the mutable reference to the underlying value. - /// - /// Returns `None` if the cell is empty. - pub fn get_mut(&mut self) -> Option<&mut T> { - // Safe because we have unique access - unsafe { &mut *self.inner.get() }.as_mut() - } - - /// Sets the contents of this cell to `value`. - /// - /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was - /// full. - /// - /// # Example - /// ``` - /// use std::lazy::OnceCell; - /// - /// let cell = OnceCell::new(); - /// assert!(cell.get().is_none()); - /// - /// assert_eq!(cell.set(92), Ok(())); - /// assert_eq!(cell.set(62), Err(62)); - /// - /// assert!(cell.get().is_some()); - /// ``` - pub fn set(&self, value: T) -> Result<(), T> { - let slot = unsafe { &*self.inner.get() }; - if slot.is_some() { - return Err(value); - } - let slot = unsafe { &mut *self.inner.get() }; - // This is the only place where we set the slot, no races - // due to reentrancy/concurrency are possible, and we've - // checked that slot is currently `None`, so this write - // maintains the `inner`'s invariant. - *slot = Some(value); - Ok(()) - } +#[doc(inline)] +#[unstable(feature = "once_cell", issue = "68198")] +pub use core::lazy::*; - /// Gets the contents of the cell, initializing it with `f` - /// if the cell was empty. - /// - /// # Panics - /// - /// If `f` panics, the panic is propagated to the caller, and the cell - /// remains uninitialized. - /// - /// It is an error to reentrantly initialize the cell from `f`. Doing - /// so results in a panic. - /// - /// # Example - /// ``` - /// use std::lazy::OnceCell; - /// - /// let cell = OnceCell::new(); - /// let value = cell.get_or_init(|| 92); - /// assert_eq!(value, &92); - /// let value = cell.get_or_init(|| unreachable!()); - /// assert_eq!(value, &92); - /// ``` - pub fn get_or_init(&self, f: F) -> &T - where - F: FnOnce() -> T, - { - match self.get_or_try_init(|| Ok::(f())) { - Ok(val) => val, - } - } - - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. - /// - /// # Panics - /// - /// If `f` panics, the panic is propagated to the caller, and the cell - /// remains uninitialized. - /// - /// It is an error to reentrantly initialize the cell from `f`. Doing - /// so results in a panic. - /// - /// # Example - /// ``` - /// use std::lazy::OnceCell; - /// - /// let cell = OnceCell::new(); - /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); - /// assert!(cell.get().is_none()); - /// let value = cell.get_or_try_init(|| -> Result { - /// Ok(92) - /// }); - /// assert_eq!(value, Ok(&92)); - /// assert_eq!(cell.get(), Some(&92)) - /// ``` - pub fn get_or_try_init(&self, f: F) -> Result<&T, E> - where - F: FnOnce() -> Result, - { - if let Some(val) = self.get() { - return Ok(val); - } - let val = f()?; - // Note that *some* forms of reentrant initialization might lead to - // UB (see `reentrant_init` test). I believe that just removing this - // `assert`, while keeping `set/get` would be sound, but it seems - // better to panic, rather than to silently use an old value. - assert!(self.set(val).is_ok(), "reentrant init"); - Ok(self.get().unwrap()) - } - - /// Consumes the `OnceCell`, returning the wrapped value. - /// - /// Returns `None` if the cell was empty. - /// - /// # Examples - /// - /// ``` - /// use std::lazy::OnceCell; - /// - /// let cell: OnceCell = OnceCell::new(); - /// assert_eq!(cell.into_inner(), None); - /// - /// let cell = OnceCell::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.into_inner(), Some("hello".to_string())); - /// ``` - pub fn into_inner(self) -> Option { - // Because `into_inner` takes `self` by value, the compiler statically verifies - // that it is not currently borrowed. So it is safe to move out `Option`. - self.inner.into_inner() - } -} - -/// A value which is initialized on the first access. +/// A synchronization primitive which can be written to only once. /// -/// # Example -/// ``` -/// use std::lazy::Lazy; +/// This type is a thread-safe `OnceCell`. /// -/// let lazy: Lazy = Lazy::new(|| { -/// println!("initializing"); -/// 92 -/// }); -/// println!("ready"); -/// println!("{}", *lazy); -/// println!("{}", *lazy); +/// # Examples /// -/// // Prints: -/// // ready -/// // initializing -/// // 92 -/// // 92 /// ``` -pub struct Lazy T> { - cell: OnceCell, - init: Cell>, -} - -#[cfg(feature = "std")] -impl RefUnwindSafe for Lazy where OnceCell: RefUnwindSafe {} - -impl fmt::Debug for Lazy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() - } -} - -impl Lazy { - /// Creates a new lazy value with the given initializing function. - /// - /// # Example - /// ``` - /// # fn main() { - /// use std::lazy::Lazy; - /// - /// let hello = "Hello, World!".to_string(); - /// - /// let lazy = Lazy::new(|| hello.to_uppercase()); - /// - /// assert_eq!(&*lazy, "HELLO, WORLD!"); - /// # } - /// ``` - pub const fn new(init: F) -> Lazy { - Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) } - } -} - -impl T> Lazy { - /// Forces the evaluation of this lazy value and returns a reference to - /// the result. - /// - /// This is equivalent to the `Deref` impl, but is explicit. - /// - /// # Example - /// ``` - /// use std::lazy::Lazy; - /// - /// let lazy = Lazy::new(|| 92); - /// - /// assert_eq!(Lazy::force(&lazy), &92); - /// assert_eq!(&*lazy, &92); - /// ``` - pub fn force(this: &Lazy) -> &T { - this.cell.get_or_init(|| match this.init.take() { - Some(f) => f(), - None => panic!("Lazy instance has previously been poisoned"), - }) - } -} - -impl T> Deref for Lazy { - type Target = T; - fn deref(&self) -> &T { - Lazy::force(self) - } -} - -impl Default for Lazy { - /// Creates a new lazy value using `Default` as the initializing function. - fn default() -> Lazy { - Lazy::new(T::default) - } -} - -/// A thread-safe cell which can be written to only once. +/// #![feature(once_cell)] /// -/// `OnceCell` provides `&` references to the contents without RAII guards. -/// -/// Reading a non-`None` value out of `OnceCell` establishes a -/// happens-before relationship with a corresponding write. For example, if -/// thread A initializes the cell with `get_or_init(f)`, and thread B -/// subsequently reads the result of this call, B also observes all the side -/// effects of `f`. -/// -/// # Example -/// ``` /// use std::lazy::SyncOnceCell; /// -/// static CELL: OnceCell = OnceCell::new(); +/// static CELL: SyncOnceCell = SyncOnceCell::new(); /// assert!(CELL.get().is_none()); /// /// std::thread::spawn(|| { @@ -516,44 +40,49 @@ impl Default for Lazy { /// assert!(value.is_some()); /// assert_eq!(value.unwrap().as_str(), "Hello, World!"); /// ``` +#[unstable(feature = "once_cell", issue = "68198")] pub struct SyncOnceCell { // This `state` word is actually an encoded version of just a pointer to a // `Waiter`, so we add the `PhantomData` appropriately. state_and_queue: AtomicUsize, _marker: PhantomData<*mut Waiter>, - // FIXME: switch to `std::mem::MaybeUninit` once we are ready to bump MSRV - // that far. It was stabilized in 1.36.0, so, if you are reading this and - // it's higher than 1.46.0 outside, please send a PR! ;) (and do the same - // for `Lazy`, while we are at it). - pub(crate) value: UnsafeCell>, + // Whether or not the value is initialized is tracked by `state_and_queue`. + value: UnsafeCell>, } // Why do we need `T: Send`? -// Thread A creates a `OnceCell` and shares it with +// Thread A creates a `SyncOnceCell` and shares it with // scoped thread B, which fills the cell, which is // then destroyed by A. That is, destructor observes // a sent value. +#[unstable(feature = "once_cell", issue = "68198")] unsafe impl Sync for SyncOnceCell {} +#[unstable(feature = "once_cell", issue = "68198")] unsafe impl Send for SyncOnceCell {} +#[unstable(feature = "once_cell", issue = "68198")] impl RefUnwindSafe for SyncOnceCell {} +#[unstable(feature = "once_cell", issue = "68198")] impl UnwindSafe for SyncOnceCell {} +#[unstable(feature = "once_cell", issue = "68198")] impl Default for SyncOnceCell { fn default() -> SyncOnceCell { SyncOnceCell::new() } } +#[unstable(feature = "once_cell", issue = "68198")] impl fmt::Debug for SyncOnceCell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.get() { - Some(v) => f.debug_tuple("SyncOnceCell").field(v).finish(), - None => f.write_str("SyncOnceCell(Uninit)"), + Some(v) => f.debug_tuple("Once").field(v).finish(), + None => f.write_str("Once(Uninit)"), } } } +#[unstable(feature = "once_cell", issue = "68198")] impl Clone for SyncOnceCell { fn clone(&self) -> SyncOnceCell { let res = SyncOnceCell::new(); @@ -567,6 +96,7 @@ impl Clone for SyncOnceCell { } } +#[unstable(feature = "once_cell", issue = "68198")] impl From for SyncOnceCell { fn from(value: T) -> Self { let cell = Self::new(); @@ -575,21 +105,24 @@ impl From for SyncOnceCell { } } +#[unstable(feature = "once_cell", issue = "68198")] impl PartialEq for SyncOnceCell { fn eq(&self, other: &SyncOnceCell) -> bool { self.get() == other.get() } } +#[unstable(feature = "once_cell", issue = "68198")] impl Eq for SyncOnceCell {} impl SyncOnceCell { /// Creates a new empty cell. + #[unstable(feature = "once_cell", issue = "68198")] pub const fn new() -> SyncOnceCell { SyncOnceCell { state_and_queue: AtomicUsize::new(INCOMPLETE), _marker: PhantomData, - value: UnsafeCell::new(None), + value: UnsafeCell::new(MaybeUninit::uninit()), } } @@ -597,6 +130,7 @@ impl SyncOnceCell { /// /// Returns `None` if the cell is empty, or being initialized. This /// method never blocks. + #[unstable(feature = "once_cell", issue = "68198")] pub fn get(&self) -> Option<&T> { if self.is_initialized() { // Safe b/c checked is_initialize @@ -608,29 +142,14 @@ impl SyncOnceCell { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is empty. + /// Returns `None` if the cell is empty. This method never blocks. + #[unstable(feature = "once_cell", issue = "68198")] pub fn get_mut(&mut self) -> Option<&mut T> { - // Safe b/c we have a unique access. - unsafe { &mut *self.value.get() }.as_mut() - } - - /// Get the reference to the underlying value, without checking if the - /// cell is initialized. - /// - /// Safety: - /// - /// Caller must ensure that the cell is in initialized state, and that - /// the contents are acquired by (synchronized to) this thread. - pub unsafe fn get_unchecked(&self) -> &T { - debug_assert!(self.is_initialized()); - let slot: &Option = &*self.value.get(); - match slot { - Some(value) => value, - // This unsafe does improve performance, see `examples/bench`. - None => { - debug_assert!(false); - unreachable_unchecked() - } + if self.is_initialized() { + // Safe b/c checked is_initialize and we have a unique access + Some(unsafe { self.get_unchecked_mut() }) + } else { + None } } @@ -639,8 +158,11 @@ impl SyncOnceCell { /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was /// full. /// - /// # Example + /// # Examples + /// /// ``` + /// #![feature(once_cell)] + /// /// use std::lazy::SyncOnceCell; /// /// static CELL: SyncOnceCell = SyncOnceCell::new(); @@ -656,6 +178,7 @@ impl SyncOnceCell { /// assert_eq!(CELL.get(), Some(&92)); /// } /// ``` + #[unstable(feature = "once_cell", issue = "68198")] pub fn set(&self, value: T) -> Result<(), T> { let mut value = Some(value); self.get_or_init(|| value.take().unwrap()); @@ -681,8 +204,11 @@ impl SyncOnceCell { /// exact outcome is unspecified. Current implementation deadlocks, but /// this may be changed to a panic in the future. /// - /// # Example + /// # Examples + /// /// ``` + /// #![feature(once_cell)] + /// /// use std::lazy::SyncOnceCell; /// /// let cell = SyncOnceCell::new(); @@ -691,6 +217,7 @@ impl SyncOnceCell { /// let value = cell.get_or_init(|| unreachable!()); /// assert_eq!(value, &92); /// ``` + #[unstable(feature = "once_cell", issue = "68198")] pub fn get_or_init(&self, f: F) -> &T where F: FnOnce() -> T, @@ -713,8 +240,11 @@ impl SyncOnceCell { /// The exact outcome is unspecified. Current implementation /// deadlocks, but this may be changed to a panic in the future. /// - /// # Example + /// # Examples + /// /// ``` + /// #![feature(once_cell)] + /// /// use std::lazy::SyncOnceCell; /// /// let cell = SyncOnceCell::new(); @@ -726,18 +256,22 @@ impl SyncOnceCell { /// assert_eq!(value, Ok(&92)); /// assert_eq!(cell.get(), Some(&92)) /// ``` + #[unstable(feature = "once_cell", issue = "68198")] pub fn get_or_try_init(&self, f: F) -> Result<&T, E> where F: FnOnce() -> Result, { // Fast path check + // NOTE: This acquire here is important to ensure + // `SyncLazy::force` is correctly synchronized if let Some(value) = self.get() { return Ok(value); } self.initialize(f)?; - // Safe b/c called initialize debug_assert!(self.is_initialized()); + + // Safety: The inner value has been initialized Ok(unsafe { self.get_unchecked() }) } @@ -747,6 +281,8 @@ impl SyncOnceCell { /// # Examples /// /// ``` + /// #![feature(once_cell)] + /// /// use std::lazy::SyncOnceCell; /// /// let cell: SyncOnceCell = SyncOnceCell::new(); @@ -756,10 +292,59 @@ impl SyncOnceCell { /// cell.set("hello".to_string()).unwrap(); /// assert_eq!(cell.into_inner(), Some("hello".to_string())); /// ``` - pub fn into_inner(self) -> Option { - // Because `into_inner` takes `self` by value, the compiler statically verifies - // that it is not currently borrowed. So it is safe to move out `Option`. - self.value.into_inner() + #[unstable(feature = "once_cell", issue = "68198")] + pub fn into_inner(mut self) -> Option { + // Safety: Safe because we immediately free `self` without dropping + let inner = unsafe { self.take_inner() }; + + // Don't drop this `SyncOnceCell`. We just moved out one of the fields, but didn't set + // the state to uninitialized. + mem::ManuallyDrop::new(self); + inner + } + + /// Takes the value out of this `SyncOnceCell`, moving it back to an uninitialized state. + /// + /// Has no effect and returns `None` if the `SyncOnceCell` hasn't been initialized. + /// + /// Safety is guaranteed by requiring a mutable reference. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::SyncOnceCell; + /// + /// let mut cell: SyncOnceCell = SyncOnceCell::new(); + /// assert_eq!(cell.take(), None); + /// + /// let mut cell = SyncOnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.take(), Some("hello".to_string())); + /// assert_eq!(cell.get(), None); + /// ``` + #[unstable(feature = "once_cell", issue = "68198")] + pub fn take(&mut self) -> Option { + mem::take(self).into_inner() + } + + /// Takes the wrapped value out of a `SyncOnceCell`. + /// Afterwards the cell is no longer initialized. + /// + /// Safety: The cell must now be free'd WITHOUT dropping. No other usages of the cell + /// are valid. Only used by `into_inner` and `drop`. + unsafe fn take_inner(&mut self) -> Option { + // The mutable reference guarantees there are no other threads that can observe us + // taking out the wrapped value. + // Right after this function `self` is supposed to be freed, so it makes little sense + // to atomically set the state to uninitialized. + if self.is_initialized() { + let value = mem::replace(&mut self.value, UnsafeCell::new(MaybeUninit::uninit())); + Some(value.into_inner().assume_init()) + } else { + None + } } /// Safety: synchronizes with store to value via Release/(Acquire|SeqCst). @@ -787,7 +372,7 @@ impl SyncOnceCell { let f = f.take().unwrap(); match f() { Ok(value) => { - unsafe { *slot.get() = Some(value) }; + unsafe { (&mut *slot.get()).write(value) }; true } Err(e) => { @@ -798,18 +383,33 @@ impl SyncOnceCell { }); res } + + /// Safety: The value must be initialized + unsafe fn get_unchecked(&self) -> &T { + debug_assert!(self.is_initialized()); + (&*self.value.get()).get_ref() + } + + /// Safety: The value must be initialized + unsafe fn get_unchecked_mut(&mut self) -> &mut T { + debug_assert!(self.is_initialized()); + (&mut *self.value.get()).get_mut() + } +} + +impl Drop for SyncOnceCell { + fn drop(&mut self) { + // Safety: The cell is being dropped, so it can't be accessed again + unsafe { self.take_inner() }; + } } -// region: copy-paste -// The following code is copied from `sync::Once`. -// This should be uncopypasted once we decide the right way to handle panics. const INCOMPLETE: usize = 0x0; const RUNNING: usize = 0x1; const COMPLETE: usize = 0x2; const STATE_MASK: usize = 0x3; - #[repr(align(4))] struct Waiter { thread: Cell>, @@ -901,19 +501,21 @@ fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) { break; } } -// endregion: copy-paste /// A value which is initialized on the first access. /// -/// This type is thread-safe and can be used in statics: +/// This type is a thread-safe `Lazy`, and can be used in statics. +/// +/// # Examples /// -/// # Example /// ``` +/// #![feature(once_cell)] +/// /// use std::collections::HashMap; /// -/// use std::lazy::Lazy; +/// use std::lazy::SyncLazy; /// -/// static HASHMAP: Lazy> = Lazy::new(|| { +/// static HASHMAP: SyncLazy> = SyncLazy::new(|| { /// println!("initializing"); /// let mut m = HashMap::new(); /// m.insert(13, "Spica".to_string()); @@ -935,14 +537,16 @@ fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) { /// // Some("Hoyten") /// } /// ``` +#[unstable(feature = "once_cell", issue = "68198")] pub struct SyncLazy T> { cell: SyncOnceCell, init: Cell>, } -impl fmt::Debug for SyncLazy { +#[unstable(feature = "once_cell", issue = "68198")] +impl fmt::Debug for SyncLazy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SyncLazy").field("cell", &self.cell).field("init", &"..").finish() + f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() } } @@ -951,15 +555,17 @@ impl fmt::Debug for SyncLazy { // we do create a `&mut Option` in `force`, but this is // properly synchronized, so it only happens once // so it also does not contribute to this impl. +#[unstable(feature = "once_cell", issue = "68198")] unsafe impl Sync for SyncLazy where SyncOnceCell: Sync {} // auto-derived `Send` impl is OK. -#[cfg(feature = "std")] +#[unstable(feature = "once_cell", issue = "68198")] impl RefUnwindSafe for SyncLazy where SyncOnceCell: RefUnwindSafe {} impl SyncLazy { /// Creates a new lazy value with the given initializing /// function. + #[unstable(feature = "once_cell", issue = "68198")] pub const fn new(f: F) -> SyncLazy { SyncLazy { cell: SyncOnceCell::new(), init: Cell::new(Some(f)) } } @@ -970,8 +576,11 @@ impl T> SyncLazy { /// returns a reference to result. This is equivalent /// to the `Deref` impl, but is explicit. /// - /// # Example + /// # Examples + /// /// ``` + /// #![feature(once_cell)] + /// /// use std::lazy::SyncLazy; /// /// let lazy = SyncLazy::new(|| 92); @@ -979,14 +588,16 @@ impl T> SyncLazy { /// assert_eq!(SyncLazy::force(&lazy), &92); /// assert_eq!(&*lazy, &92); /// ``` + #[unstable(feature = "once_cell", issue = "68198")] pub fn force(this: &SyncLazy) -> &T { this.cell.get_or_init(|| match this.init.take() { Some(f) => f(), - None => panic!("SyncLazy instance has previously been poisoned"), + None => panic!("Lazy instance has previously been poisoned"), }) } } +#[unstable(feature = "once_cell", issue = "68198")] impl T> Deref for SyncLazy { type Target = T; fn deref(&self) -> &T { @@ -994,9 +605,348 @@ impl T> Deref for SyncLazy { } } +#[unstable(feature = "once_cell", issue = "68198")] impl Default for SyncLazy { /// Creates a new lazy value using `Default` as the initializing function. fn default() -> SyncLazy { SyncLazy::new(T::default) } } + +#[cfg(test)] +mod tests { + use crate::{ + lazy::{Lazy, SyncLazy, SyncOnceCell}, + panic, + sync::{ + atomic::{AtomicUsize, Ordering::SeqCst}, + mpsc::channel, + Mutex, + }, + }; + + #[test] + fn lazy_default() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + + struct Foo(u8); + impl Default for Foo { + fn default() -> Self { + CALLED.fetch_add(1, SeqCst); + Foo(42) + } + } + + let lazy: Lazy> = <_>::default(); + + assert_eq!(CALLED.load(SeqCst), 0); + + assert_eq!(lazy.lock().unwrap().0, 42); + assert_eq!(CALLED.load(SeqCst), 1); + + lazy.lock().unwrap().0 = 21; + + assert_eq!(lazy.lock().unwrap().0, 21); + assert_eq!(CALLED.load(SeqCst), 1); + } + + #[test] + fn lazy_poisoning() { + let x: Lazy = Lazy::new(|| panic!("kaboom")); + for _ in 0..2 { + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len())); + assert!(res.is_err()); + } + } + + // miri doesn't support threads + #[cfg(not(miri))] + fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { + crate::thread::spawn(f).join().unwrap() + } + + #[cfg(not(miri))] + fn spawn(f: impl FnOnce() + Send + 'static) { + let _ = crate::thread::spawn(f); + } + + // "stub threads" for Miri + #[cfg(miri)] + fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { + f(()) + } + + #[cfg(miri)] + fn spawn(f: impl FnOnce() + Send + 'static) { + f(()) + } + + #[test] + fn sync_once_cell() { + static ONCE_CELL: SyncOnceCell = SyncOnceCell::new(); + + assert!(ONCE_CELL.get().is_none()); + + spawn_and_wait(|| { + ONCE_CELL.get_or_init(|| 92); + assert_eq!(ONCE_CELL.get(), Some(&92)); + }); + + ONCE_CELL.get_or_init(|| panic!("Kabom!")); + assert_eq!(ONCE_CELL.get(), Some(&92)); + } + + #[test] + fn sync_once_cell_get_mut() { + let mut c = SyncOnceCell::new(); + assert!(c.get_mut().is_none()); + c.set(90).unwrap(); + *c.get_mut().unwrap() += 2; + assert_eq!(c.get_mut(), Some(&mut 92)); + } + + #[test] + fn sync_once_cell_get_unchecked() { + let c = SyncOnceCell::new(); + c.set(92).unwrap(); + unsafe { + assert_eq!(c.get_unchecked(), &92); + } + } + + #[test] + fn sync_once_cell_drop() { + static DROP_CNT: AtomicUsize = AtomicUsize::new(0); + struct Dropper; + impl Drop for Dropper { + fn drop(&mut self) { + DROP_CNT.fetch_add(1, SeqCst); + } + } + + let x = SyncOnceCell::new(); + spawn_and_wait(move || { + x.get_or_init(|| Dropper); + assert_eq!(DROP_CNT.load(SeqCst), 0); + drop(x); + }); + + assert_eq!(DROP_CNT.load(SeqCst), 1); + } + + #[test] + fn sync_once_cell_drop_empty() { + let x = SyncOnceCell::::new(); + drop(x); + } + + #[test] + fn clone() { + let s = SyncOnceCell::new(); + let c = s.clone(); + assert!(c.get().is_none()); + + s.set("hello".to_string()).unwrap(); + let c = s.clone(); + assert_eq!(c.get().map(String::as_str), Some("hello")); + } + + #[test] + fn get_or_try_init() { + let cell: SyncOnceCell = SyncOnceCell::new(); + assert!(cell.get().is_none()); + + let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); + assert!(res.is_err()); + assert!(cell.get().is_none()); + + assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + + assert_eq!( + cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), + Ok(&"hello".to_string()) + ); + assert_eq!(cell.get(), Some(&"hello".to_string())); + } + + #[test] + fn from_impl() { + assert_eq!(SyncOnceCell::from("value").get(), Some(&"value")); + assert_ne!(SyncOnceCell::from("foo").get(), Some(&"bar")); + } + + #[test] + fn partialeq_impl() { + assert!(SyncOnceCell::from("value") == SyncOnceCell::from("value")); + assert!(SyncOnceCell::from("foo") != SyncOnceCell::from("bar")); + + assert!(SyncOnceCell::::new() == SyncOnceCell::new()); + assert!(SyncOnceCell::::new() != SyncOnceCell::from("value".to_owned())); + } + + #[test] + fn into_inner() { + let cell: SyncOnceCell = SyncOnceCell::new(); + assert_eq!(cell.into_inner(), None); + let cell = SyncOnceCell::new(); + cell.set("hello".to_string()).unwrap(); + assert_eq!(cell.into_inner(), Some("hello".to_string())); + } + + #[test] + fn sync_lazy_new() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + static SYNC_LAZY: SyncLazy = SyncLazy::new(|| { + CALLED.fetch_add(1, SeqCst); + 92 + }); + + assert_eq!(CALLED.load(SeqCst), 0); + + spawn_and_wait(|| { + let y = *SYNC_LAZY - 30; + assert_eq!(y, 62); + assert_eq!(CALLED.load(SeqCst), 1); + }); + + let y = *SYNC_LAZY - 30; + assert_eq!(y, 62); + assert_eq!(CALLED.load(SeqCst), 1); + } + + #[test] + fn sync_lazy_default() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + + struct Foo(u8); + impl Default for Foo { + fn default() -> Self { + CALLED.fetch_add(1, SeqCst); + Foo(42) + } + } + + let lazy: SyncLazy> = <_>::default(); + + assert_eq!(CALLED.load(SeqCst), 0); + + assert_eq!(lazy.lock().unwrap().0, 42); + assert_eq!(CALLED.load(SeqCst), 1); + + lazy.lock().unwrap().0 = 21; + + assert_eq!(lazy.lock().unwrap().0, 21); + assert_eq!(CALLED.load(SeqCst), 1); + } + + #[test] + #[cfg_attr(miri, ignore)] // leaks memory + fn static_sync_lazy() { + static XS: SyncLazy> = SyncLazy::new(|| { + let mut xs = Vec::new(); + xs.push(1); + xs.push(2); + xs.push(3); + xs + }); + + spawn_and_wait(|| { + assert_eq!(&*XS, &vec![1, 2, 3]); + }); + + assert_eq!(&*XS, &vec![1, 2, 3]); + } + + #[test] + #[cfg_attr(miri, ignore)] // leaks memory + fn static_sync_lazy_via_fn() { + fn xs() -> &'static Vec { + static XS: SyncOnceCell> = SyncOnceCell::new(); + XS.get_or_init(|| { + let mut xs = Vec::new(); + xs.push(1); + xs.push(2); + xs.push(3); + xs + }) + } + assert_eq!(xs(), &vec![1, 2, 3]); + } + + #[test] + fn sync_lazy_poisoning() { + let x: SyncLazy = SyncLazy::new(|| panic!("kaboom")); + for _ in 0..2 { + let res = panic::catch_unwind(|| x.len()); + assert!(res.is_err()); + } + } + + #[test] + fn is_sync_send() { + fn assert_traits() {} + assert_traits::>(); + assert_traits::>(); + } + + #[test] + fn eval_once_macro() { + macro_rules! eval_once { + (|| -> $ty:ty { + $($body:tt)* + }) => {{ + static ONCE_CELL: SyncOnceCell<$ty> = SyncOnceCell::new(); + fn init() -> $ty { + $($body)* + } + ONCE_CELL.get_or_init(init) + }}; + } + + let fib: &'static Vec = eval_once! { + || -> Vec { + let mut res = vec![1, 1]; + for i in 0..10 { + let next = res[i] + res[i + 1]; + res.push(next); + } + res + } + }; + assert_eq!(fib[5], 8) + } + + #[test] + #[cfg_attr(miri, ignore)] // deadlocks without real threads + fn sync_once_cell_does_not_leak_partially_constructed_boxes() { + static ONCE_CELL: SyncOnceCell = SyncOnceCell::new(); + + let n_readers = 10; + let n_writers = 3; + const MSG: &str = "Hello, World"; + + let (tx, rx) = channel(); + + for _ in 0..n_readers { + let tx = tx.clone(); + spawn(move || { + loop { + if let Some(msg) = ONCE_CELL.get() { + tx.send(msg).unwrap(); + break; + } + } + }); + } + for _ in 0..n_writers { + spawn(move || { + let _ = ONCE_CELL.set(MSG.to_owned()); + }); + } + + for _ in 0..n_readers { + let msg = rx.recv().unwrap(); + assert_eq!(msg, MSG); + } + } +} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 171d6c3f64f87..8178719ccc40a 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -285,6 +285,7 @@ #![feature(linkage)] #![feature(llvm_asm)] #![feature(log_syntax)] +#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_ref)] #![feature(maybe_uninit_slice)] #![feature(min_specialization)] @@ -292,6 +293,7 @@ #![feature(negative_impls)] #![feature(never_type)] #![feature(nll)] +#![feature(once_cell)] #![feature(optin_builtin_traits)] #![feature(or_patterns)] #![feature(panic_info_message)] @@ -476,10 +478,7 @@ pub mod process; pub mod sync; pub mod time; -#[unstable( - feature = "std_lazy", - issue = "99", -)] +#[unstable(feature = "once_cell", issue = "68198")] pub mod lazy; #[stable(feature = "futures_api", since = "1.36.0")] From d1263f5e66d31ad170c524a70aa5f21e08474326 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 30 Jun 2020 16:27:35 +1000 Subject: [PATCH 03/31] use set() in SyncOnceCell::from --- src/libstd/lazy.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/libstd/lazy.rs b/src/libstd/lazy.rs index c7553caa4c056..aed91df47449f 100644 --- a/src/libstd/lazy.rs +++ b/src/libstd/lazy.rs @@ -85,14 +85,14 @@ impl fmt::Debug for SyncOnceCell { #[unstable(feature = "once_cell", issue = "68198")] impl Clone for SyncOnceCell { fn clone(&self) -> SyncOnceCell { - let res = SyncOnceCell::new(); + let cell = Self::new(); if let Some(value) = self.get() { - match res.set(value.clone()) { + match cell.set(value.clone()) { Ok(()) => (), Err(_) => unreachable!(), } } - res + cell } } @@ -100,8 +100,10 @@ impl Clone for SyncOnceCell { impl From for SyncOnceCell { fn from(value: T) -> Self { let cell = Self::new(); - cell.get_or_init(|| value); - cell + match cell.set(value) { + Ok(()) => cell, + Err(_) => unreachable!(), + } } } @@ -155,8 +157,7 @@ impl SyncOnceCell { /// Sets the contents of this cell to `value`. /// - /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was - /// full. + /// Returns `Ok(())` if the cell's value was updated. /// /// # Examples /// @@ -262,8 +263,10 @@ impl SyncOnceCell { F: FnOnce() -> Result, { // Fast path check - // NOTE: This acquire here is important to ensure - // `SyncLazy::force` is correctly synchronized + // NOTE: We need to perform an acquire on the state in this method + // in order to correctly synchronize `SyncLazy::force`. This is + // currently done by calling `self.get()`, which in turn calls + // `self.is_initialized()`, which in turn performs the acquire. if let Some(value) = self.get() { return Ok(value); } @@ -410,6 +413,8 @@ const COMPLETE: usize = 0x2; const STATE_MASK: usize = 0x3; +// The alignment here is so that we can stash the state in the lower +// bits of the `next` pointer #[repr(align(4))] struct Waiter { thread: Cell>, From d1017940d77f35f841008c3e108e3da5e48a592f Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 30 Jun 2020 18:27:21 +1000 Subject: [PATCH 04/31] remove inlined lazy::Waiter in favor of sync::Once --- src/libstd/lazy.rs | 140 +++++----------------------------------- src/libstd/sync/once.rs | 18 ++++-- 2 files changed, 29 insertions(+), 129 deletions(-) diff --git a/src/libstd/lazy.rs b/src/libstd/lazy.rs index aed91df47449f..761cc2b439f67 100644 --- a/src/libstd/lazy.rs +++ b/src/libstd/lazy.rs @@ -3,12 +3,10 @@ use crate::{ cell::{Cell, UnsafeCell}, fmt, - marker::PhantomData, mem::{self, MaybeUninit}, ops::{Deref, Drop}, panic::{RefUnwindSafe, UnwindSafe}, - sync::atomic::{AtomicBool, AtomicUsize, Ordering}, - thread::{self, Thread}, + sync::Once, }; #[doc(inline)] @@ -42,10 +40,7 @@ pub use core::lazy::*; /// ``` #[unstable(feature = "once_cell", issue = "68198")] pub struct SyncOnceCell { - // This `state` word is actually an encoded version of just a pointer to a - // `Waiter`, so we add the `PhantomData` appropriately. - state_and_queue: AtomicUsize, - _marker: PhantomData<*mut Waiter>, + once: Once, // Whether or not the value is initialized is tracked by `state_and_queue`. value: UnsafeCell>, } @@ -122,8 +117,7 @@ impl SyncOnceCell { #[unstable(feature = "once_cell", issue = "68198")] pub const fn new() -> SyncOnceCell { SyncOnceCell { - state_and_queue: AtomicUsize::new(INCOMPLETE), - _marker: PhantomData, + once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()), } } @@ -135,7 +129,7 @@ impl SyncOnceCell { #[unstable(feature = "once_cell", issue = "68198")] pub fn get(&self) -> Option<&T> { if self.is_initialized() { - // Safe b/c checked is_initialize + // Safe b/c checked is_initialized Some(unsafe { self.get_unchecked() }) } else { None @@ -148,7 +142,7 @@ impl SyncOnceCell { #[unstable(feature = "once_cell", issue = "68198")] pub fn get_mut(&mut self) -> Option<&mut T> { if self.is_initialized() { - // Safe b/c checked is_initialize and we have a unique access + // Safe b/c checked is_initialized and we have a unique access Some(unsafe { self.get_unchecked_mut() }) } else { None @@ -350,37 +344,32 @@ impl SyncOnceCell { } } - /// Safety: synchronizes with store to value via Release/(Acquire|SeqCst). #[inline] fn is_initialized(&self) -> bool { - // An `Acquire` load is enough because that makes all the initialization - // operations visible to us, and, this being a fast path, weaker - // ordering helps with performance. This `Acquire` synchronizes with - // `SeqCst` operations on the slow path. - self.state_and_queue.load(Ordering::Acquire) == COMPLETE + self.once.is_completed() } - /// Safety: synchronizes with store to value via SeqCst read from state, - /// writes value only once because we never get to INCOMPLETE state after a - /// successful write. #[cold] fn initialize(&self, f: F) -> Result<(), E> where F: FnOnce() -> Result, { - let mut f = Some(f); let mut res: Result<(), E> = Ok(()); let slot = &self.value; - initialize_inner(&self.state_and_queue, &mut || { - let f = f.take().unwrap(); + + // Ignore poisoning from other threads + // If another thread panics, then we'll be able to run our closure + self.once.call_once_force(|p| { match f() { Ok(value) => { unsafe { (&mut *slot.get()).write(value) }; - true } Err(e) => { res = Err(e); - false + + // Treat the underlying `Once` as poisoned since we + // failed to initialize our value. Calls + p.poison(); } } }); @@ -407,106 +396,6 @@ impl Drop for SyncOnceCell { } } -const INCOMPLETE: usize = 0x0; -const RUNNING: usize = 0x1; -const COMPLETE: usize = 0x2; - -const STATE_MASK: usize = 0x3; - -// The alignment here is so that we can stash the state in the lower -// bits of the `next` pointer -#[repr(align(4))] -struct Waiter { - thread: Cell>, - signaled: AtomicBool, - next: *const Waiter, -} - -struct WaiterQueue<'a> { - state_and_queue: &'a AtomicUsize, - set_state_on_drop_to: usize, -} - -impl Drop for WaiterQueue<'_> { - fn drop(&mut self) { - let state_and_queue = - self.state_and_queue.swap(self.set_state_on_drop_to, Ordering::AcqRel); - - assert_eq!(state_and_queue & STATE_MASK, RUNNING); - - unsafe { - let mut queue = (state_and_queue & !STATE_MASK) as *const Waiter; - while !queue.is_null() { - let next = (*queue).next; - let thread = (*queue).thread.replace(None).unwrap(); - (*queue).signaled.store(true, Ordering::Release); - queue = next; - thread.unpark(); - } - } - } -} - -fn initialize_inner(my_state_and_queue: &AtomicUsize, init: &mut dyn FnMut() -> bool) -> bool { - let mut state_and_queue = my_state_and_queue.load(Ordering::Acquire); - - loop { - match state_and_queue { - COMPLETE => return true, - INCOMPLETE => { - let old = my_state_and_queue.compare_and_swap( - state_and_queue, - RUNNING, - Ordering::Acquire, - ); - if old != state_and_queue { - state_and_queue = old; - continue; - } - let mut waiter_queue = WaiterQueue { - state_and_queue: my_state_and_queue, - set_state_on_drop_to: INCOMPLETE, - }; - let success = init(); - - waiter_queue.set_state_on_drop_to = if success { COMPLETE } else { INCOMPLETE }; - return success; - } - _ => { - assert!(state_and_queue & STATE_MASK == RUNNING); - wait(&my_state_and_queue, state_and_queue); - state_and_queue = my_state_and_queue.load(Ordering::Acquire); - } - } - } -} - -fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) { - loop { - if current_state & STATE_MASK != RUNNING { - return; - } - - let node = Waiter { - thread: Cell::new(Some(thread::current())), - signaled: AtomicBool::new(false), - next: (current_state & !STATE_MASK) as *const Waiter, - }; - let me = &node as *const Waiter as usize; - - let old = state_and_queue.compare_and_swap(current_state, me | RUNNING, Ordering::Release); - if old != current_state { - current_state = old; - continue; - } - - while !node.signaled.load(Ordering::Acquire) { - thread::park(); - } - break; - } -} - /// A value which is initialized on the first access. /// /// This type is a thread-safe `Lazy`, and can be used in statics. @@ -763,6 +652,7 @@ mod tests { let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); assert!(res.is_err()); + assert!(!cell.is_initialized()); assert!(cell.get().is_none()); assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 7dc822db3d027..1fce5dc035224 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -132,6 +132,7 @@ unsafe impl Send for Once {} #[derive(Debug)] pub struct OnceState { poisoned: bool, + set_state_on_drop_to: Cell, } /// Initialization value for static [`Once`] values. @@ -321,7 +322,7 @@ impl Once { } let mut f = Some(f); - self.call_inner(true, &mut |p| f.take().unwrap()(&OnceState { poisoned: p })); + self.call_inner(true, &mut |p| f.take().unwrap()(p)); } /// Returns `true` if some `call_once` call has completed @@ -385,7 +386,7 @@ impl Once { // currently no way to take an `FnOnce` and call it via virtual dispatch // without some allocation overhead. #[cold] - fn call_inner(&self, ignore_poisoning: bool, init: &mut dyn FnMut(bool)) { + fn call_inner(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&OnceState)) { let mut state_and_queue = self.state_and_queue.load(Ordering::Acquire); loop { match state_and_queue { @@ -413,8 +414,9 @@ impl Once { }; // Run the initialization function, letting it know if we're // poisoned or not. - init(state_and_queue == POISONED); - waiter_queue.set_state_on_drop_to = COMPLETE; + let init_state = OnceState { poisoned: state_and_queue == POISONED, set_state_on_drop_to: Cell::new(COMPLETE) }; + init(&init_state); + waiter_queue.set_state_on_drop_to = init_state.set_state_on_drop_to.get(); break; } _ => { @@ -554,6 +556,14 @@ impl OnceState { pub fn poisoned(&self) -> bool { self.poisoned } + + /// Poison the associated [`Once`] without explicitly panicking. + /// + /// [`Once`]: struct.Once.html + // NOTE: This is currently only exposed for the `lazy` module + pub(crate) fn poison(&self) { + self.set_state_on_drop_to.set(POISONED); + } } #[cfg(all(test, not(target_os = "emscripten")))] From 1f1cda65d9a5c88855d3fbcb3912095474e557de Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 30 Jun 2020 18:36:10 +1000 Subject: [PATCH 05/31] appease tidy --- src/libstd/lazy.rs | 5 +---- src/libstd/sync/once.rs | 5 ++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libstd/lazy.rs b/src/libstd/lazy.rs index 761cc2b439f67..094eff17f8916 100644 --- a/src/libstd/lazy.rs +++ b/src/libstd/lazy.rs @@ -116,10 +116,7 @@ impl SyncOnceCell { /// Creates a new empty cell. #[unstable(feature = "once_cell", issue = "68198")] pub const fn new() -> SyncOnceCell { - SyncOnceCell { - once: Once::new(), - value: UnsafeCell::new(MaybeUninit::uninit()), - } + SyncOnceCell { once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()) } } /// Gets the reference to the underlying value. diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 1fce5dc035224..64260990824b8 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -414,7 +414,10 @@ impl Once { }; // Run the initialization function, letting it know if we're // poisoned or not. - let init_state = OnceState { poisoned: state_and_queue == POISONED, set_state_on_drop_to: Cell::new(COMPLETE) }; + let init_state = OnceState { + poisoned: state_and_queue == POISONED, + set_state_on_drop_to: Cell::new(COMPLETE), + }; init(&init_state); waiter_queue.set_state_on_drop_to = init_state.set_state_on_drop_to.get(); break; From ab23a2a9c54fed2acbd553407a761f19af6e8ed4 Mon Sep 17 00:00:00 2001 From: Kristofer Rye Date: Thu, 16 Jul 2020 13:32:46 -0500 Subject: [PATCH 06/31] ci: Set `shell: bash` as a default, remove duplicates A follow-up to #74406, this commit merely removes the `shell: bash` lines where they are explicitly added in favor of setting defaults for *all* "run" steps. Signed-off-by: Kristofer Rye --- .github/workflows/ci.yml | 92 +++++------------------------------- src/ci/github-actions/ci.yml | 23 ++------- 2 files changed, 15 insertions(+), 100 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99140bec17029..9cf0f4dbd1e4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,10 +51,12 @@ jobs: os: ubuntu-latest-xl timeout-minutes: 600 runs-on: "${{ matrix.os }}" + defaults: + run: + shell: bash steps: - name: disable git crlf conversion run: git config --global core.autocrlf false - shell: bash - name: checkout the source code uses: actions/checkout@v1 with: @@ -66,77 +68,59 @@ jobs: if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" - name: add extra environment variables run: src/ci/scripts/setup-environment.sh - shell: bash env: EXTRA_VARIABLES: "${{ toJson(matrix.env) }}" if: success() && !env.SKIP_JOB - name: decide whether to skip this job run: src/ci/scripts/should-skip-this.sh - shell: bash if: success() && !env.SKIP_JOB - name: collect CPU statistics run: src/ci/scripts/collect-cpu-stats.sh - shell: bash if: success() && !env.SKIP_JOB - name: show the current environment run: src/ci/scripts/dump-environment.sh - shell: bash if: success() && !env.SKIP_JOB - name: install awscli run: src/ci/scripts/install-awscli.sh - shell: bash if: success() && !env.SKIP_JOB - name: install sccache run: src/ci/scripts/install-sccache.sh - shell: bash if: success() && !env.SKIP_JOB - name: install clang run: src/ci/scripts/install-clang.sh - shell: bash if: success() && !env.SKIP_JOB - name: install WIX run: src/ci/scripts/install-wix.sh - shell: bash if: success() && !env.SKIP_JOB - name: ensure the build happens on a partition with enough space run: src/ci/scripts/symlink-build-dir.sh - shell: bash if: success() && !env.SKIP_JOB - name: disable git crlf conversion run: src/ci/scripts/disable-git-crlf-conversion.sh - shell: bash if: success() && !env.SKIP_JOB - name: install MSYS2 run: src/ci/scripts/install-msys2.sh - shell: bash if: success() && !env.SKIP_JOB - name: install MinGW run: src/ci/scripts/install-mingw.sh - shell: bash if: success() && !env.SKIP_JOB - name: install ninja run: src/ci/scripts/install-ninja.sh - shell: bash if: success() && !env.SKIP_JOB - name: enable ipv6 on Docker run: src/ci/scripts/enable-docker-ipv6.sh - shell: bash if: success() && !env.SKIP_JOB - name: disable git crlf conversion run: src/ci/scripts/disable-git-crlf-conversion.sh - shell: bash if: success() && !env.SKIP_JOB - name: checkout submodules run: src/ci/scripts/checkout-submodules.sh - shell: bash if: success() && !env.SKIP_JOB - name: ensure line endings are correct run: src/ci/scripts/verify-line-endings.sh - shell: bash if: success() && !env.SKIP_JOB - name: run the build run: src/ci/scripts/run-build-from-ci.sh - shell: bash env: AWS_ACCESS_KEY_ID: "${{ env.CACHES_AWS_ACCESS_KEY_ID }}" AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }}" @@ -144,7 +128,6 @@ jobs: if: success() && !env.SKIP_JOB - name: upload artifacts to S3 run: src/ci/scripts/upload-artifacts.sh - shell: bash env: AWS_ACCESS_KEY_ID: "${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}" AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}" @@ -170,10 +153,12 @@ jobs: env: {} timeout-minutes: 600 runs-on: "${{ matrix.os }}" + defaults: + run: + shell: bash steps: - name: disable git crlf conversion run: git config --global core.autocrlf false - shell: bash - name: checkout the source code uses: actions/checkout@v1 with: @@ -185,77 +170,59 @@ jobs: if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" - name: add extra environment variables run: src/ci/scripts/setup-environment.sh - shell: bash env: EXTRA_VARIABLES: "${{ toJson(matrix.env) }}" if: success() && !env.SKIP_JOB - name: decide whether to skip this job run: src/ci/scripts/should-skip-this.sh - shell: bash if: success() && !env.SKIP_JOB - name: collect CPU statistics run: src/ci/scripts/collect-cpu-stats.sh - shell: bash if: success() && !env.SKIP_JOB - name: show the current environment run: src/ci/scripts/dump-environment.sh - shell: bash if: success() && !env.SKIP_JOB - name: install awscli run: src/ci/scripts/install-awscli.sh - shell: bash if: success() && !env.SKIP_JOB - name: install sccache run: src/ci/scripts/install-sccache.sh - shell: bash if: success() && !env.SKIP_JOB - name: install clang run: src/ci/scripts/install-clang.sh - shell: bash if: success() && !env.SKIP_JOB - name: install WIX run: src/ci/scripts/install-wix.sh - shell: bash if: success() && !env.SKIP_JOB - name: ensure the build happens on a partition with enough space run: src/ci/scripts/symlink-build-dir.sh - shell: bash if: success() && !env.SKIP_JOB - name: disable git crlf conversion run: src/ci/scripts/disable-git-crlf-conversion.sh - shell: bash if: success() && !env.SKIP_JOB - name: install MSYS2 run: src/ci/scripts/install-msys2.sh - shell: bash if: success() && !env.SKIP_JOB - name: install MinGW run: src/ci/scripts/install-mingw.sh - shell: bash if: success() && !env.SKIP_JOB - name: install ninja run: src/ci/scripts/install-ninja.sh - shell: bash if: success() && !env.SKIP_JOB - name: enable ipv6 on Docker run: src/ci/scripts/enable-docker-ipv6.sh - shell: bash if: success() && !env.SKIP_JOB - name: disable git crlf conversion run: src/ci/scripts/disable-git-crlf-conversion.sh - shell: bash if: success() && !env.SKIP_JOB - name: checkout submodules run: src/ci/scripts/checkout-submodules.sh - shell: bash if: success() && !env.SKIP_JOB - name: ensure line endings are correct run: src/ci/scripts/verify-line-endings.sh - shell: bash if: success() && !env.SKIP_JOB - name: run the build run: src/ci/scripts/run-build-from-ci.sh - shell: bash env: AWS_ACCESS_KEY_ID: "${{ env.CACHES_AWS_ACCESS_KEY_ID }}" AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }}" @@ -263,7 +230,6 @@ jobs: if: success() && !env.SKIP_JOB - name: upload artifacts to S3 run: src/ci/scripts/upload-artifacts.sh - shell: bash env: AWS_ACCESS_KEY_ID: "${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}" AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}" @@ -507,10 +473,12 @@ jobs: os: windows-latest-xl timeout-minutes: 600 runs-on: "${{ matrix.os }}" + defaults: + run: + shell: bash steps: - name: disable git crlf conversion run: git config --global core.autocrlf false - shell: bash - name: checkout the source code uses: actions/checkout@v1 with: @@ -522,77 +490,59 @@ jobs: if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" - name: add extra environment variables run: src/ci/scripts/setup-environment.sh - shell: bash env: EXTRA_VARIABLES: "${{ toJson(matrix.env) }}" if: success() && !env.SKIP_JOB - name: decide whether to skip this job run: src/ci/scripts/should-skip-this.sh - shell: bash if: success() && !env.SKIP_JOB - name: collect CPU statistics run: src/ci/scripts/collect-cpu-stats.sh - shell: bash if: success() && !env.SKIP_JOB - name: show the current environment run: src/ci/scripts/dump-environment.sh - shell: bash if: success() && !env.SKIP_JOB - name: install awscli run: src/ci/scripts/install-awscli.sh - shell: bash if: success() && !env.SKIP_JOB - name: install sccache run: src/ci/scripts/install-sccache.sh - shell: bash if: success() && !env.SKIP_JOB - name: install clang run: src/ci/scripts/install-clang.sh - shell: bash if: success() && !env.SKIP_JOB - name: install WIX run: src/ci/scripts/install-wix.sh - shell: bash if: success() && !env.SKIP_JOB - name: ensure the build happens on a partition with enough space run: src/ci/scripts/symlink-build-dir.sh - shell: bash if: success() && !env.SKIP_JOB - name: disable git crlf conversion run: src/ci/scripts/disable-git-crlf-conversion.sh - shell: bash if: success() && !env.SKIP_JOB - name: install MSYS2 run: src/ci/scripts/install-msys2.sh - shell: bash if: success() && !env.SKIP_JOB - name: install MinGW run: src/ci/scripts/install-mingw.sh - shell: bash if: success() && !env.SKIP_JOB - name: install ninja run: src/ci/scripts/install-ninja.sh - shell: bash if: success() && !env.SKIP_JOB - name: enable ipv6 on Docker run: src/ci/scripts/enable-docker-ipv6.sh - shell: bash if: success() && !env.SKIP_JOB - name: disable git crlf conversion run: src/ci/scripts/disable-git-crlf-conversion.sh - shell: bash if: success() && !env.SKIP_JOB - name: checkout submodules run: src/ci/scripts/checkout-submodules.sh - shell: bash if: success() && !env.SKIP_JOB - name: ensure line endings are correct run: src/ci/scripts/verify-line-endings.sh - shell: bash if: success() && !env.SKIP_JOB - name: run the build run: src/ci/scripts/run-build-from-ci.sh - shell: bash env: AWS_ACCESS_KEY_ID: "${{ env.CACHES_AWS_ACCESS_KEY_ID }}" AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }}" @@ -600,7 +550,6 @@ jobs: if: success() && !env.SKIP_JOB - name: upload artifacts to S3 run: src/ci/scripts/upload-artifacts.sh - shell: bash env: AWS_ACCESS_KEY_ID: "${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}" AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}" @@ -652,10 +601,12 @@ jobs: os: macos-latest timeout-minutes: 600 runs-on: "${{ matrix.os }}" + defaults: + run: + shell: bash steps: - name: disable git crlf conversion run: git config --global core.autocrlf false - shell: bash - name: checkout the source code uses: actions/checkout@v1 with: @@ -667,77 +618,59 @@ jobs: if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" - name: add extra environment variables run: src/ci/scripts/setup-environment.sh - shell: bash env: EXTRA_VARIABLES: "${{ toJson(matrix.env) }}" if: success() && !env.SKIP_JOB - name: decide whether to skip this job run: src/ci/scripts/should-skip-this.sh - shell: bash if: success() && !env.SKIP_JOB - name: collect CPU statistics run: src/ci/scripts/collect-cpu-stats.sh - shell: bash if: success() && !env.SKIP_JOB - name: show the current environment run: src/ci/scripts/dump-environment.sh - shell: bash if: success() && !env.SKIP_JOB - name: install awscli run: src/ci/scripts/install-awscli.sh - shell: bash if: success() && !env.SKIP_JOB - name: install sccache run: src/ci/scripts/install-sccache.sh - shell: bash if: success() && !env.SKIP_JOB - name: install clang run: src/ci/scripts/install-clang.sh - shell: bash if: success() && !env.SKIP_JOB - name: install WIX run: src/ci/scripts/install-wix.sh - shell: bash if: success() && !env.SKIP_JOB - name: ensure the build happens on a partition with enough space run: src/ci/scripts/symlink-build-dir.sh - shell: bash if: success() && !env.SKIP_JOB - name: disable git crlf conversion run: src/ci/scripts/disable-git-crlf-conversion.sh - shell: bash if: success() && !env.SKIP_JOB - name: install MSYS2 run: src/ci/scripts/install-msys2.sh - shell: bash if: success() && !env.SKIP_JOB - name: install MinGW run: src/ci/scripts/install-mingw.sh - shell: bash if: success() && !env.SKIP_JOB - name: install ninja run: src/ci/scripts/install-ninja.sh - shell: bash if: success() && !env.SKIP_JOB - name: enable ipv6 on Docker run: src/ci/scripts/enable-docker-ipv6.sh - shell: bash if: success() && !env.SKIP_JOB - name: disable git crlf conversion run: src/ci/scripts/disable-git-crlf-conversion.sh - shell: bash if: success() && !env.SKIP_JOB - name: checkout submodules run: src/ci/scripts/checkout-submodules.sh - shell: bash if: success() && !env.SKIP_JOB - name: ensure line endings are correct run: src/ci/scripts/verify-line-endings.sh - shell: bash if: success() && !env.SKIP_JOB - name: run the build run: src/ci/scripts/run-build-from-ci.sh - shell: bash env: AWS_ACCESS_KEY_ID: "${{ env.CACHES_AWS_ACCESS_KEY_ID }}" AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }}" @@ -745,7 +678,6 @@ jobs: if: success() && !env.SKIP_JOB - name: upload artifacts to S3 run: src/ci/scripts/upload-artifacts.sh - shell: bash env: AWS_ACCESS_KEY_ID: "${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}" AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}" diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 0b3b77f217dbd..e8036b8c12075 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -75,11 +75,13 @@ x--expand-yaml-anchors--remove: - &base-ci-job timeout-minutes: 600 runs-on: "${{ matrix.os }}" + defaults: + run: + shell: bash env: *shared-ci-variables steps: - name: disable git crlf conversion run: git config --global core.autocrlf false - shell: bash - name: checkout the source code uses: actions/checkout@v1 @@ -95,7 +97,6 @@ x--expand-yaml-anchors--remove: - name: add extra environment variables run: src/ci/scripts/setup-environment.sh - shell: bash env: # Since it's not possible to merge `${{ matrix.env }}` with the other # variables in `job..env`, the variables defined in the matrix @@ -106,67 +107,54 @@ x--expand-yaml-anchors--remove: - name: decide whether to skip this job run: src/ci/scripts/should-skip-this.sh - shell: bash <<: *step - name: collect CPU statistics run: src/ci/scripts/collect-cpu-stats.sh - shell: bash <<: *step - name: show the current environment run: src/ci/scripts/dump-environment.sh - shell: bash <<: *step - name: install awscli run: src/ci/scripts/install-awscli.sh - shell: bash <<: *step - name: install sccache run: src/ci/scripts/install-sccache.sh - shell: bash <<: *step - name: install clang run: src/ci/scripts/install-clang.sh - shell: bash <<: *step - name: install WIX run: src/ci/scripts/install-wix.sh - shell: bash <<: *step - name: ensure the build happens on a partition with enough space run: src/ci/scripts/symlink-build-dir.sh - shell: bash <<: *step - name: disable git crlf conversion run: src/ci/scripts/disable-git-crlf-conversion.sh - shell: bash <<: *step - name: install MSYS2 run: src/ci/scripts/install-msys2.sh - shell: bash <<: *step - name: install MinGW run: src/ci/scripts/install-mingw.sh - shell: bash <<: *step - name: install ninja run: src/ci/scripts/install-ninja.sh - shell: bash <<: *step - name: enable ipv6 on Docker run: src/ci/scripts/enable-docker-ipv6.sh - shell: bash <<: *step # Disable automatic line ending conversion (again). On Windows, when we're @@ -176,22 +164,18 @@ x--expand-yaml-anchors--remove: # appropriate line endings. - name: disable git crlf conversion run: src/ci/scripts/disable-git-crlf-conversion.sh - shell: bash <<: *step - name: checkout submodules run: src/ci/scripts/checkout-submodules.sh - shell: bash <<: *step - name: ensure line endings are correct run: src/ci/scripts/verify-line-endings.sh - shell: bash <<: *step - name: run the build run: src/ci/scripts/run-build-from-ci.sh - shell: bash env: AWS_ACCESS_KEY_ID: ${{ env.CACHES_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }} @@ -200,7 +184,6 @@ x--expand-yaml-anchors--remove: - name: upload artifacts to S3 run: src/ci/scripts/upload-artifacts.sh - shell: bash env: AWS_ACCESS_KEY_ID: ${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }} From b26ecd261b827837df8bb01c8fd5bf80dced8909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 17 Jul 2020 00:00:00 +0000 Subject: [PATCH 07/31] Test codegen of compare_exchange operations --- src/test/codegen/atomic-operations.rs | 60 +++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/test/codegen/atomic-operations.rs diff --git a/src/test/codegen/atomic-operations.rs b/src/test/codegen/atomic-operations.rs new file mode 100644 index 0000000000000..ff94ac8543f8b --- /dev/null +++ b/src/test/codegen/atomic-operations.rs @@ -0,0 +1,60 @@ +// Code generation of atomic operations. +// +// compile-flags: -O +#![crate_type = "lib"] + +use std::sync::atomic::{AtomicI32, Ordering::*}; + +// CHECK-LABEL: @compare_exchange +#[no_mangle] +pub fn compare_exchange(a: &AtomicI32) { + // CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 10 monotonic monotonic + let _ = a.compare_exchange(0, 10, Relaxed, Relaxed); + + // CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 20 release monotonic + let _ = a.compare_exchange(0, 20, Release, Relaxed); + + // CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 30 acquire monotonic + // CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 31 acquire acquire + let _ = a.compare_exchange(0, 30, Acquire, Relaxed); + let _ = a.compare_exchange(0, 31, Acquire, Acquire); + + // CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 40 acq_rel monotonic + // CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 41 acq_rel acquire + let _ = a.compare_exchange(0, 40, AcqRel, Relaxed); + let _ = a.compare_exchange(0, 41, AcqRel, Acquire); + + // CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 50 seq_cst monotonic + // CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 51 seq_cst acquire + // CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 52 seq_cst seq_cst + let _ = a.compare_exchange(0, 50, SeqCst, Relaxed); + let _ = a.compare_exchange(0, 51, SeqCst, Acquire); + let _ = a.compare_exchange(0, 52, SeqCst, SeqCst); +} + +// CHECK-LABEL: @compare_exchange_weak +#[no_mangle] +pub fn compare_exchange_weak(w: &AtomicI32) { + // CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 10 monotonic monotonic + let _ = w.compare_exchange_weak(1, 10, Relaxed, Relaxed); + + // CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 20 release monotonic + let _ = w.compare_exchange_weak(1, 20, Release, Relaxed); + + // CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 30 acquire monotonic + // CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 31 acquire acquire + let _ = w.compare_exchange_weak(1, 30, Acquire, Relaxed); + let _ = w.compare_exchange_weak(1, 31, Acquire, Acquire); + + // CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 40 acq_rel monotonic + // CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 41 acq_rel acquire + let _ = w.compare_exchange_weak(1, 40, AcqRel, Relaxed); + let _ = w.compare_exchange_weak(1, 41, AcqRel, Acquire); + + // CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 50 seq_cst monotonic + // CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 51 seq_cst acquire + // CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 52 seq_cst seq_cst + let _ = w.compare_exchange_weak(1, 50, SeqCst, Relaxed); + let _ = w.compare_exchange_weak(1, 51, SeqCst, Acquire); + let _ = w.compare_exchange_weak(1, 52, SeqCst, SeqCst); +} From 48844fed2bdc7c40d213131d5e13ab28e47df083 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 17 Jul 2020 17:54:22 +1000 Subject: [PATCH 08/31] include changes to Cargo.lock --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 2027b5200eb70..5309c03ee23ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1372,8 +1372,8 @@ checksum = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" name = "installer" version = "0.0.0" dependencies = [ + "anyhow", "clap", - "failure", "flate2", "lazy_static", "num_cpus", From d866160b85107551c339c35995db4b35c0364264 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 17 Jul 2020 15:35:14 +0300 Subject: [PATCH 09/31] bootstrap.py: guard against GC in NixOS patching support. --- src/bootstrap/bootstrap.py | 73 ++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 1dfa635c694ac..bdf3bdf80b6d5 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -349,6 +349,7 @@ def __init__(self): self.use_vendored_sources = '' self.verbose = False self.git_version = None + self.nix_deps_dir = None def download_stage0(self): """Fetch the build system for Rust, written in Rust @@ -440,8 +441,7 @@ def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None): get("{}/{}".format(url, filename), tarball, verbose=self.verbose) unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose) - @staticmethod - def fix_executable(fname): + def fix_executable(self, fname): """Modifies the interpreter section of 'fname' to fix the dynamic linker This method is only required on NixOS and uses the PatchELF utility to @@ -472,38 +472,49 @@ def fix_executable(fname): nix_os_msg = "info: you seem to be running NixOS. Attempting to patch" print(nix_os_msg, fname) - try: - interpreter = subprocess.check_output( - ["patchelf", "--print-interpreter", fname]) - interpreter = interpreter.strip().decode(default_encoding) - except subprocess.CalledProcessError as reason: - print("warning: failed to call patchelf:", reason) - return - - loader = interpreter.split("/")[-1] - - try: - ldd_output = subprocess.check_output( - ['ldd', '/run/current-system/sw/bin/sh']) - ldd_output = ldd_output.strip().decode(default_encoding) - except subprocess.CalledProcessError as reason: - print("warning: unable to call ldd:", reason) - return - - for line in ldd_output.splitlines(): - libname = line.split()[0] - if libname.endswith(loader): - loader_path = libname[:len(libname) - len(loader)] - break - else: - print("warning: unable to find the path to the dynamic linker") - return - - correct_interpreter = loader_path + loader + # Only build `stage0/.nix-deps` once. + nix_deps_dir = self.nix_deps_dir + if not nix_deps_dir: + nix_deps_dir = "{}/.nix-deps".format(self.bin_root()) + if not os.path.exists(nix_deps_dir): + os.makedirs(nix_deps_dir) + + nix_deps = [ + # Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). + "stdenv.cc.bintools", + + # Needed for patching ELF binaries (see doc comment above). + "patchelf", + ] + + # Run `nix-build` to "build" each dependency (which will likely reuse + # the existing `/nix/store` copy, or at most download a pre-built copy). + # Importantly, we don't rely on `nix-build` printing the `/nix/store` + # path on stdout, but use `-o` to symlink it into `stage0/.nix-deps/$dep`, + # ensuring garbage collection will never remove the `/nix/store` path + # (which would break our patched binaries that hardcode those paths). + for dep in nix_deps: + try: + subprocess.check_output([ + "nix-build", "", + "-A", dep, + "-o", "{}/{}".format(nix_deps_dir, dep), + ]) + except subprocess.CalledProcessError as reason: + print("warning: failed to call nix-build:", reason) + return + + self.nix_deps_dir = nix_deps_dir + + patchelf = "{}/patchelf/bin/patchelf".format(nix_deps_dir) + bintools_dir = "{}/stdenv.cc.bintools".format(nix_deps_dir) + + with open("{}/nix-support/dynamic-linker".format(bintools_dir)) as dynamic_linker: + interpreter = dynamic_linker.read().rstrip() try: subprocess.check_output( - ["patchelf", "--set-interpreter", correct_interpreter, fname]) + [patchelf, "--set-interpreter", interpreter, fname]) except subprocess.CalledProcessError as reason: print("warning: failed to call patchelf:", reason) return From b5076fbb9669bd8e29ad58160a2d60ddd6813df7 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 17 Jul 2020 16:25:05 +0300 Subject: [PATCH 10/31] bootstrap.py: patch RPATH on NixOS to handle the new zlib dependency. --- src/bootstrap/bootstrap.py | 43 ++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index bdf3bdf80b6d5..c3f1bac177de7 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -389,8 +389,12 @@ def support_xz(): filename = "rustc-{}-{}{}".format(rustc_channel, self.build, tarball_suffix) self._download_stage0_helper(filename, "rustc", tarball_suffix) - self.fix_executable("{}/bin/rustc".format(self.bin_root())) - self.fix_executable("{}/bin/rustdoc".format(self.bin_root())) + self.fix_bin_or_dylib("{}/bin/rustc".format(self.bin_root())) + self.fix_bin_or_dylib("{}/bin/rustdoc".format(self.bin_root())) + lib_dir = "{}/lib".format(self.bin_root()) + for lib in os.listdir(lib_dir): + if lib.endswith(".so"): + self.fix_bin_or_dylib("{}/{}".format(lib_dir, lib)) with output(self.rustc_stamp()) as rust_stamp: rust_stamp.write(self.date) @@ -409,7 +413,7 @@ def support_xz(): filename = "cargo-{}-{}{}".format(cargo_channel, self.build, tarball_suffix) self._download_stage0_helper(filename, "cargo", tarball_suffix) - self.fix_executable("{}/bin/cargo".format(self.bin_root())) + self.fix_bin_or_dylib("{}/bin/cargo".format(self.bin_root())) with output(self.cargo_stamp()) as cargo_stamp: cargo_stamp.write(self.date) @@ -422,8 +426,8 @@ def support_xz(): [channel, date] = rustfmt_channel.split('-', 1) filename = "rustfmt-{}-{}{}".format(channel, self.build, tarball_suffix) self._download_stage0_helper(filename, "rustfmt-preview", tarball_suffix, date) - self.fix_executable("{}/bin/rustfmt".format(self.bin_root())) - self.fix_executable("{}/bin/cargo-fmt".format(self.bin_root())) + self.fix_bin_or_dylib("{}/bin/rustfmt".format(self.bin_root())) + self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(self.bin_root())) with output(self.rustfmt_stamp()) as rustfmt_stamp: rustfmt_stamp.write(self.date + self.rustfmt_channel) @@ -441,11 +445,12 @@ def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None): get("{}/{}".format(url, filename), tarball, verbose=self.verbose) unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose) - def fix_executable(self, fname): - """Modifies the interpreter section of 'fname' to fix the dynamic linker + def fix_bin_or_dylib(self, fname): + """Modifies the interpreter section of 'fname' to fix the dynamic linker, + or the RPATH section, to fix the dynamic library search path This method is only required on NixOS and uses the PatchELF utility to - change the dynamic linker of ELF executables. + change the interpreter/RPATH of ELF executables. Please see https://nixos.org/patchelf.html for more information """ @@ -483,6 +488,9 @@ def fix_executable(self, fname): # Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). "stdenv.cc.bintools", + # Needed as a system dependency of `libLLVM-*.so`. + "zlib", + # Needed for patching ELF binaries (see doc comment above). "patchelf", ] @@ -507,14 +515,23 @@ def fix_executable(self, fname): self.nix_deps_dir = nix_deps_dir patchelf = "{}/patchelf/bin/patchelf".format(nix_deps_dir) - bintools_dir = "{}/stdenv.cc.bintools".format(nix_deps_dir) - with open("{}/nix-support/dynamic-linker".format(bintools_dir)) as dynamic_linker: - interpreter = dynamic_linker.read().rstrip() + if fname.endswith(".so"): + # Dynamic library, patch RPATH to point to system dependencies. + dylib_deps = ["zlib"] + rpath_entries = [ + # Relative default, all binary and dynamic libraries we ship + # appear to have this (even when `../lib` is redundant). + "$ORIGIN/../lib", + ] + ["{}/{}/lib".format(nix_deps_dir, dep) for dep in dylib_deps] + patchelf_args = ["--set-rpath", ":".join(rpath_entries)] + else: + bintools_dir = "{}/stdenv.cc.bintools".format(nix_deps_dir) + with open("{}/nix-support/dynamic-linker".format(bintools_dir)) as dynamic_linker: + patchelf_args = ["--set-interpreter", dynamic_linker.read().rstrip()] try: - subprocess.check_output( - [patchelf, "--set-interpreter", interpreter, fname]) + subprocess.check_output([patchelf] + patchelf_args + [fname]) except subprocess.CalledProcessError as reason: print("warning: failed to call patchelf:", reason) return From 49f50780480421e524adf6262a285003d6f8281e Mon Sep 17 00:00:00 2001 From: Kristofer Rye Date: Fri, 17 Jul 2020 08:29:01 -0500 Subject: [PATCH 11/31] ci: Stop setting CI_OVERRIDE_SHELL environment variable This will render the src/ci/exec-with-shell.py script more or less useless, but we're going to replace that by just using the system bash instead. Signed-off-by: Kristofer Rye --- src/ci/scripts/install-msys2.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ci/scripts/install-msys2.sh b/src/ci/scripts/install-msys2.sh index 3a0c965a67710..185d361582505 100755 --- a/src/ci/scripts/install-msys2.sh +++ b/src/ci/scripts/install-msys2.sh @@ -12,9 +12,6 @@ if isWindows; then mkdir -p "${msys2Path}/home/${USERNAME}" ciCommandAddPath "${msys2Path}/usr/bin" - echo "switching shell to use our own bash" - ciCommandSetEnv CI_OVERRIDE_SHELL "${msys2Path}/usr/bin/bash.exe" - # Detect the native Python version installed on the agent. On GitHub # Actions, the C:\hostedtoolcache\windows\Python directory contains a # subdirectory for each installed Python version. From 586629c0b607f34981a893cf856bb35279f58542 Mon Sep 17 00:00:00 2001 From: Kristofer Rye Date: Fri, 17 Jul 2020 08:44:14 -0500 Subject: [PATCH 12/31] ci: Replace exec-with-shell wrapper with "plain bash" Also, promote defaults.run.shell from inside only the primary jobs to the top level. The src/ci/exec-with-shell.py wrapper script was formerly used to change out the shell mid-job by intercepting a CI_OVERRIDE_SHELL environment variable. Now, instead, we just set `bash` as the global default across all jobs, and we also delete the exec-with-shell.py script. Signed-off-by: Kristofer Rye --- .github/workflows/ci.yml | 14 +------------- src/ci/exec-with-shell.py | 16 ---------------- src/ci/github-actions/ci.yml | 19 ++++--------------- 3 files changed, 5 insertions(+), 44 deletions(-) delete mode 100755 src/ci/exec-with-shell.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9cf0f4dbd1e4c..86de37820003a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ name: CI - "**" defaults: run: - shell: "python src/ci/exec-with-shell.py {0}" + shell: bash jobs: pr: name: PR @@ -51,9 +51,6 @@ jobs: os: ubuntu-latest-xl timeout-minutes: 600 runs-on: "${{ matrix.os }}" - defaults: - run: - shell: bash steps: - name: disable git crlf conversion run: git config --global core.autocrlf false @@ -153,9 +150,6 @@ jobs: env: {} timeout-minutes: 600 runs-on: "${{ matrix.os }}" - defaults: - run: - shell: bash steps: - name: disable git crlf conversion run: git config --global core.autocrlf false @@ -473,9 +467,6 @@ jobs: os: windows-latest-xl timeout-minutes: 600 runs-on: "${{ matrix.os }}" - defaults: - run: - shell: bash steps: - name: disable git crlf conversion run: git config --global core.autocrlf false @@ -601,9 +592,6 @@ jobs: os: macos-latest timeout-minutes: 600 runs-on: "${{ matrix.os }}" - defaults: - run: - shell: bash steps: - name: disable git crlf conversion run: git config --global core.autocrlf false diff --git a/src/ci/exec-with-shell.py b/src/ci/exec-with-shell.py deleted file mode 100755 index 26ce69e33d9c3..0000000000000 --- a/src/ci/exec-with-shell.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python -# A simple wrapper that forwards the arguments to bash, unless the -# CI_OVERRIDE_SHELL environment variable is present: in that case the content -# of that environment variable is used as the shell path. - -import os -import sys -import subprocess - -try: - shell = os.environ["CI_OVERRIDE_SHELL"] -except KeyError: - shell = "bash" - -res = subprocess.call([shell] + sys.argv[1:]) -sys.exit(res) diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index e8036b8c12075..5573d87aa2e55 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -75,9 +75,6 @@ x--expand-yaml-anchors--remove: - &base-ci-job timeout-minutes: 600 runs-on: "${{ matrix.os }}" - defaults: - run: - shell: bash env: *shared-ci-variables steps: - name: disable git crlf conversion @@ -233,18 +230,10 @@ on: defaults: run: - # While on Linux and macOS builders it just forwards the arguments to the - # system bash, this wrapper allows switching from the host's bash.exe to - # the one we install along with MSYS2 mid-build on Windows. - # - # Once the step to install MSYS2 is executed, the CI_OVERRIDE_SHELL - # environment variable is set pointing to our MSYS2's bash.exe. From that - # moment the host's bash.exe will not be called anymore. - # - # This is needed because we can't launch our own bash.exe from the host - # bash.exe, as that would load two different cygwin1.dll in memory, causing - # "cygwin heap mismatch" errors. - shell: python src/ci/exec-with-shell.py {0} + # On Linux, macOS, and Windows, use the system-provided bash as the default + # shell. (This should only make a difference on Windows, where the default + # shell is PowerShell.) + shell: bash jobs: pr: From f7979d3c9360ae6a4a688729fa518601029a7c76 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 17 Jul 2020 15:45:50 +0100 Subject: [PATCH 13/31] Add regression test for #69414 Closes #69414 (no longer ICEs after #74159) --- .../const-param-type-depends-on-type-param-ungated.rs | 3 +++ .../const-param-type-depends-on-type-param-ungated.stderr | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs index db15ececfa436..ea75a3d040358 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs @@ -1,3 +1,6 @@ +// compile-flags: -Zsave-analysis +// Regression test for #69414 ^ + use std::marker::PhantomData; struct B(PhantomData<[T; N]>); //~ ERROR const generics are unstable diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr index 35996e833610d..616f0fa8f1af0 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr @@ -1,11 +1,11 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:22 + --> $DIR/const-param-type-depends-on-type-param-ungated.rs:6:22 | LL | struct B(PhantomData<[T; N]>); | ^ the type must not depend on the parameter `T` error[E0658]: const generics are unstable - --> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:19 + --> $DIR/const-param-type-depends-on-type-param-ungated.rs:6:19 | LL | struct B(PhantomData<[T; N]>); | ^ From b0a7fbd91f387c7f4421bbd253a7a2f3536a7ae6 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sun, 12 Apr 2020 00:39:43 +0300 Subject: [PATCH 14/31] [experiment] ty/layout: compute both niche-filling and tagged layouts for enums. --- src/librustc_middle/ty/layout.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs index 82daae7d921b2..daa9918a39612 100644 --- a/src/librustc_middle/ty/layout.rs +++ b/src/librustc_middle/ty/layout.rs @@ -876,6 +876,8 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { .iter_enumerated() .all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i.as_u32())); + let mut niche_filling_layout = None; + // Niche-filling enum optimization. if !def.repr.inhibit_enum_layout_opt() && no_explicit_discriminants { let mut dataful_variant = None; @@ -972,7 +974,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let largest_niche = Niche::from_scalar(dl, offset, niche_scalar.clone()); - return Ok(tcx.intern_layout(Layout { + niche_filling_layout = Some(Layout { variants: Variants::Multiple { tag: niche_scalar, tag_encoding: TagEncoding::Niche { @@ -991,7 +993,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { largest_niche, size, align, - })); + }); } } } @@ -1214,7 +1216,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag.clone()); - tcx.intern_layout(Layout { + let tagged_layout = Layout { variants: Variants::Multiple { tag, tag_encoding: TagEncoding::Direct, @@ -1229,7 +1231,9 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { abi, align, size, - }) + }; + + tcx.intern_layout(niche_filling_layout.unwrap_or(tagged_layout)) } // Types with no meaningful known layout. From 2e431c62b6455d8b352b6cd1be69409a31e8de4b Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Sun, 5 Jul 2020 14:29:30 -0400 Subject: [PATCH 15/31] compare tagged/niche-filling layout and pick the best one --- src/librustc_middle/lib.rs | 1 + src/librustc_middle/ty/layout.rs | 16 +++++++++++++++- .../ui/print_type_sizes/niche-filling.stdout | 6 +++--- src/test/ui/type-sizes.rs | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/librustc_middle/lib.rs b/src/librustc_middle/lib.rs index b7dccb8d8ce6d..a68301385b7a5 100644 --- a/src/librustc_middle/lib.rs +++ b/src/librustc_middle/lib.rs @@ -27,6 +27,7 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(cmp_min_max_by)] #![feature(const_fn)] #![feature(const_panic)] #![feature(const_fn_transmute)] diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs index daa9918a39612..8ae9269a6bf68 100644 --- a/src/librustc_middle/ty/layout.rs +++ b/src/librustc_middle/ty/layout.rs @@ -1233,7 +1233,21 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { size, }; - tcx.intern_layout(niche_filling_layout.unwrap_or(tagged_layout)) + let best_layout = match (tagged_layout, niche_filling_layout) { + (tagged_layout, Some(niche_filling_layout)) => { + // Pick the smaller layout; otherwise, + // pick the layout with the larger niche; otherwise, + // pick tagged as it has simpler codegen. + cmp::min_by_key(tagged_layout, niche_filling_layout, |layout| { + let niche_size = + layout.largest_niche.as_ref().map_or(0, |n| n.available(dl)); + (layout.size, cmp::Reverse(niche_size)) + }) + } + (tagged_layout, None) => tagged_layout, + }; + + tcx.intern_layout(best_layout) } // Types with no meaningful known layout. diff --git a/src/test/ui/print_type_sizes/niche-filling.stdout b/src/test/ui/print_type_sizes/niche-filling.stdout index 301edc0d086b1..1894cd218ee34 100644 --- a/src/test/ui/print_type_sizes/niche-filling.stdout +++ b/src/test/ui/print_type_sizes/niche-filling.stdout @@ -8,12 +8,12 @@ print-type-size variant `Some`: 12 bytes print-type-size field `.0`: 12 bytes print-type-size variant `None`: 0 bytes print-type-size type: `EmbeddedDiscr`: 8 bytes, alignment: 4 bytes +print-type-size discriminant: 1 bytes print-type-size variant `Record`: 7 bytes -print-type-size field `.val`: 4 bytes -print-type-size field `.post`: 2 bytes print-type-size field `.pre`: 1 bytes +print-type-size field `.post`: 2 bytes +print-type-size field `.val`: 4 bytes print-type-size variant `None`: 0 bytes -print-type-size end padding: 1 bytes print-type-size type: `MyOption>`: 8 bytes, alignment: 4 bytes print-type-size discriminant: 4 bytes print-type-size variant `Some`: 4 bytes diff --git a/src/test/ui/type-sizes.rs b/src/test/ui/type-sizes.rs index 6a3f3c98f127a..0ae9bfcf0bd5a 100644 --- a/src/test/ui/type-sizes.rs +++ b/src/test/ui/type-sizes.rs @@ -5,6 +5,7 @@ #![feature(never_type)] use std::mem::size_of; +use std::num::NonZeroU8; struct t {a: u8, b: i8} struct u {a: u8, b: i8, c: u8} @@ -102,6 +103,15 @@ enum Option2 { None } +pub enum CanBeNicheFilledButShouldnt { + A(NonZeroU8, u32), + B +} +pub enum AlwaysTagged { + A(u8, u32), + B +} + pub fn main() { assert_eq!(size_of::(), 1 as usize); assert_eq!(size_of::(), 4 as usize); @@ -145,4 +155,11 @@ pub fn main() { assert_eq!(size_of::>>(), size_of::<(bool, &())>()); assert_eq!(size_of::>>(), size_of::<(bool, &())>()); assert_eq!(size_of::>>(), size_of::<(bool, &())>()); + + assert_eq!(size_of::(), 8); + assert_eq!(size_of::>(), 8); + assert_eq!(size_of::>>(), 8); + assert_eq!(size_of::(), 8); + assert_eq!(size_of::>(), 8); + assert_eq!(size_of::>>(), 8); } From 3924672cccb7fb273d197aa2a6f5e130bca96323 Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Thu, 16 Jul 2020 18:54:26 -0400 Subject: [PATCH 16/31] document test changes --- src/test/ui/type-sizes.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/test/ui/type-sizes.rs b/src/test/ui/type-sizes.rs index 0ae9bfcf0bd5a..73a11a5e743f6 100644 --- a/src/test/ui/type-sizes.rs +++ b/src/test/ui/type-sizes.rs @@ -103,11 +103,19 @@ enum Option2 { None } +// Two layouts are considered for `CanBeNicheFilledButShouldnt`: +// Niche-filling: +// { u32 (4 bytes), NonZeroU8 + tag in niche (1 byte), padding (3 bytes) } +// Tagged: +// { tag (1 byte), NonZeroU8 (1 byte), padding (2 bytes), u32 (4 bytes) } +// Both are the same size (due to padding), +// but the tagged layout is better as the tag creates a niche with 254 invalid values, +// allowing types like `Option>` to fit into 8 bytes. pub enum CanBeNicheFilledButShouldnt { A(NonZeroU8, u32), B } -pub enum AlwaysTagged { +pub enum AlwaysTaggedBecauseItHasNoNiche { A(u8, u32), B } @@ -159,7 +167,7 @@ pub fn main() { assert_eq!(size_of::(), 8); assert_eq!(size_of::>(), 8); assert_eq!(size_of::>>(), 8); - assert_eq!(size_of::(), 8); - assert_eq!(size_of::>(), 8); - assert_eq!(size_of::>>(), 8); + assert_eq!(size_of::(), 8); + assert_eq!(size_of::>(), 8); + assert_eq!(size_of::>>(), 8); } From 7f4752b1ed2fd5d6dec81ef35e40928b046bcaff Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 11 Jul 2020 08:45:26 +0000 Subject: [PATCH 17/31] compiletest: Rewrite extract_lldb_version function This makes extract_lldb_version has the same version type like extract_gdb_version. This is technically a breaking change for rustc-dev users. But note that rustc-dev is a nightly component. --- src/tools/compiletest/src/common.rs | 2 +- src/tools/compiletest/src/header.rs | 23 +++----- src/tools/compiletest/src/main.rs | 90 ++++++++++------------------- src/tools/compiletest/src/tests.rs | 11 ++++ 4 files changed, 50 insertions(+), 76 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 703b87634cec3..4abb1db35a02f 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -268,7 +268,7 @@ pub struct Config { pub gdb_native_rust: bool, /// Version of LLDB - pub lldb_version: Option, + pub lldb_version: Option, /// Whether LLDB has native rust support pub lldb_native_rust: bool, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 571e7a59113ad..7f3e684a5d607 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -188,16 +188,17 @@ impl EarlyProps { } fn ignore_lldb(config: &Config, line: &str) -> bool { - if let Some(ref actual_version) = config.lldb_version { - if line.starts_with("min-lldb-version") { - let min_version = line - .trim_end() - .rsplit(' ') - .next() - .expect("Malformed lldb version directive"); + if let Some(actual_version) = config.lldb_version { + if let Some(min_version) = line.strip_prefix("min-lldb-version:").map(str::trim) { + let min_version = min_version.parse().unwrap_or_else(|e| { + panic!( + "Unexpected format of LLDB version string: {}\n{:?}", + min_version, e + ); + }); // Ignore if actual version is smaller the minimum required // version - lldb_version_to_int(actual_version) < lldb_version_to_int(min_version) + actual_version < min_version } else if line.starts_with("rust-lldb") && !config.lldb_native_rust { true } else { @@ -943,12 +944,6 @@ impl Config { } } -pub fn lldb_version_to_int(version_string: &str) -> isize { - let error_string = - format!("Encountered LLDB version string with unexpected format: {}", version_string); - version_string.parse().expect(&error_string) -} - fn expand_variables(mut value: String, config: &Config) -> String { const CWD: &'static str = "{{cwd}}"; const SRC_BASE: &'static str = "{{src-base}}"; diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 97272f1a9c1b6..2b0ff0da9f5c5 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -165,8 +165,12 @@ pub fn parse_config(args: Vec) -> Config { let cdb = analyze_cdb(matches.opt_str("cdb"), &target); let (gdb, gdb_version, gdb_native_rust) = analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path); - let (lldb_version, lldb_native_rust) = extract_lldb_version(matches.opt_str("lldb-version")); - + let (lldb_version, lldb_native_rust) = matches + .opt_str("lldb-version") + .as_deref() + .and_then(extract_lldb_version) + .map(|(v, b)| (Some(v), b)) + .unwrap_or((None, false)); let color = match matches.opt_str("color").as_ref().map(|x| &**x) { Some("auto") | None => ColorConfig::AutoColor, Some("always") => ColorConfig::AlwaysColor, @@ -400,17 +404,14 @@ fn configure_lldb(config: &Config) -> Option { return None; } - if let Some(lldb_version) = config.lldb_version.as_ref() { - if lldb_version == "350" { - println!( - "WARNING: The used version of LLDB ({}) has a \ - known issue that breaks debuginfo tests. See \ - issue #32520 for more information. Skipping all \ - LLDB-based tests!", - lldb_version - ); - return None; - } + if let Some(350) = config.lldb_version { + println!( + "WARNING: The used version of LLDB (350) has a \ + known issue that breaks debuginfo tests. See \ + issue #32520 for more information. Skipping all \ + LLDB-based tests!", + ); + return None; } // Some older versions of LLDB seem to have problems with multiple @@ -908,7 +909,7 @@ fn extract_gdb_version(full_version_line: &str) -> Option { } /// Returns (LLDB version, LLDB is rust-enabled) -fn extract_lldb_version(full_version_line: Option) -> (Option, bool) { +fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> { // Extract the major LLDB version from the given version string. // LLDB version strings are different for Apple and non-Apple platforms. // The Apple variant looks like this: @@ -917,7 +918,7 @@ fn extract_lldb_version(full_version_line: Option) -> (Option, b // lldb-300.2.51 (new versions) // // We are only interested in the major version number, so this function - // will return `Some("179")` and `Some("300")` respectively. + // will return `Some(179)` and `Some(300)` respectively. // // Upstream versions look like: // lldb version 6.0.1 @@ -929,53 +930,20 @@ fn extract_lldb_version(full_version_line: Option) -> (Option, b // normally fine because the only non-Apple version we test is // rust-enabled. - if let Some(ref full_version_line) = full_version_line { - if !full_version_line.trim().is_empty() { - let full_version_line = full_version_line.trim(); - - for (pos, l) in full_version_line.char_indices() { - if l != 'l' && l != 'L' { - continue; - } - if pos + 5 >= full_version_line.len() { - continue; - } - let l = full_version_line[pos + 1..].chars().next().unwrap(); - if l != 'l' && l != 'L' { - continue; - } - let d = full_version_line[pos + 2..].chars().next().unwrap(); - if d != 'd' && d != 'D' { - continue; - } - let b = full_version_line[pos + 3..].chars().next().unwrap(); - if b != 'b' && b != 'B' { - continue; - } - let dash = full_version_line[pos + 4..].chars().next().unwrap(); - if dash != '-' { - continue; - } - - let vers = full_version_line[pos + 5..] - .chars() - .take_while(|c| c.is_digit(10)) - .collect::(); - if !vers.is_empty() { - return (Some(vers), full_version_line.contains("rust-enabled")); - } - } + let full_version_line = full_version_line.trim(); - if full_version_line.starts_with("lldb version ") { - let vers = full_version_line[13..] - .chars() - .take_while(|c| c.is_digit(10)) - .collect::(); - if !vers.is_empty() { - return (Some(vers + "00"), full_version_line.contains("rust-enabled")); - } - } + if let Some(apple_ver) = + full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-")) + { + if let Some(idx) = apple_ver.find(|c: char| !c.is_digit(10)) { + let version: u32 = apple_ver[..idx].parse().unwrap(); + return Some((version, full_version_line.contains("rust-enabled"))); + } + } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") { + if let Some(idx) = lldb_ver.find(|c: char| !c.is_digit(10)) { + let version: u32 = lldb_ver[..idx].parse().unwrap(); + return Some((version * 100, full_version_line.contains("rust-enabled"))); } } - (None, false) + None } diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index 31c151d29e916..7669ec53bf72f 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -41,6 +41,17 @@ fn test_extract_gdb_version() { } } +#[test] +fn test_extract_lldb_version() { + // Apple variants + assert_eq!(extract_lldb_version("LLDB-179.5"), Some((179, false))); + assert_eq!(extract_lldb_version("lldb-300.2.51"), Some((300, false))); + + // Upstream versions + assert_eq!(extract_lldb_version("lldb version 6.0.1"), Some((600, false))); + assert_eq!(extract_lldb_version("lldb version 9.0.0"), Some((900, false))); +} + #[test] fn is_test_test() { assert_eq!(true, is_test(&OsString::from("a_test.rs"))); From 629f3993f5561b449e9ec480384dbf2c20f81705 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 11 Jul 2020 08:45:26 +0000 Subject: [PATCH 18/31] compiletest: Rewrite extract_gdb_version function --- src/tools/compiletest/src/main.rs | 89 +++++++++--------------------- src/tools/compiletest/src/tests.rs | 2 +- 2 files changed, 28 insertions(+), 63 deletions(-) diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 2b0ff0da9f5c5..80f611d2ad6a4 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -841,71 +841,36 @@ fn extract_gdb_version(full_version_line: &str) -> Option { // This particular form is documented in the GNU coding standards: // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion - // don't start parsing in the middle of a number - let mut prev_was_digit = false; - let mut in_parens = false; - for (pos, c) in full_version_line.char_indices() { - if in_parens { - if c == ')' { - in_parens = false; - } - continue; - } else if c == '(' { - in_parens = true; - continue; - } - - if prev_was_digit || !c.is_digit(10) { - prev_was_digit = c.is_digit(10); - continue; + let mut splits = full_version_line.rsplit(' '); + let version_string = splits.next().unwrap(); + + let mut splits = version_string.split('.'); + let major = splits.next().unwrap(); + let minor = splits.next().unwrap(); + let patch = splits.next(); + + let major: u32 = major.parse().unwrap(); + let (minor, patch): (u32, u32) = match minor.find(|c: char| !c.is_digit(10)) { + None => { + let minor = minor.parse().unwrap(); + let patch: u32 = match patch { + Some(patch) => match patch.find(|c: char| !c.is_digit(10)) { + None => patch.parse().unwrap(), + Some(idx) if idx > 3 => 0, + Some(idx) => patch[..idx].parse().unwrap(), + }, + None => 0, + }; + (minor, patch) } - - prev_was_digit = true; - - let line = &full_version_line[pos..]; - - let next_split = match line.find(|c: char| !c.is_digit(10)) { - Some(idx) => idx, - None => continue, // no minor version - }; - - if line.as_bytes()[next_split] != b'.' { - continue; // no minor version + // There is no patch version after minor-date (e.g. "4-2012"). + Some(idx) => { + let minor = minor[..idx].parse().unwrap(); + (minor, 0) } + }; - let major = &line[..next_split]; - let line = &line[next_split + 1..]; - - let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) { - Some(idx) => { - if line.as_bytes()[idx] == b'.' { - let patch = &line[idx + 1..]; - - let patch_len = - patch.find(|c: char| !c.is_digit(10)).unwrap_or_else(|| patch.len()); - let patch = &patch[..patch_len]; - let patch = if patch_len > 3 || patch_len == 0 { None } else { Some(patch) }; - - (&line[..idx], patch) - } else { - (&line[..idx], None) - } - } - None => (line, None), - }; - - if minor.is_empty() { - continue; - } - - let major: u32 = major.parse().unwrap(); - let minor: u32 = minor.parse().unwrap(); - let patch: u32 = patch.unwrap_or("0").parse().unwrap(); - - return Some(((major * 1000) + minor) * 1000 + patch); - } - - None + Some(((major * 1000) + minor) * 1000 + patch) } /// Returns (LLDB version, LLDB is rust-enabled) diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index 7669ec53bf72f..237bb756597a3 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -2,7 +2,7 @@ use super::*; #[test] fn test_extract_gdb_version() { - macro_rules! test { ($($expectation:tt: $input:tt,)*) => {{$( + macro_rules! test { ($($expectation:literal: $input:literal,)*) => {{$( assert_eq!(extract_gdb_version($input), Some($expectation)); )*}}} From 835fcb129768c94f60bf40ebb364fc2d3dc82669 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 11 Jul 2020 13:48:21 +0000 Subject: [PATCH 19/31] Fix panic as passing wrong format to `extract_gdb_version` --- src/tools/compiletest/src/header.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 7f3e684a5d607..0fc42396e7949 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -132,32 +132,29 @@ impl EarlyProps { fn ignore_gdb(config: &Config, line: &str) -> bool { if let Some(actual_version) = config.gdb_version { - if line.starts_with("min-gdb-version") { - let (start_ver, end_ver) = extract_gdb_version_range(line); + if let Some(rest) = line.strip_prefix("min-gdb-version:").map(str::trim) { + let (start_ver, end_ver) = extract_gdb_version_range(rest); if start_ver != end_ver { panic!("Expected single GDB version") } // Ignore if actual version is smaller the minimum required // version - actual_version < start_ver - } else if line.starts_with("ignore-gdb-version") { - let (min_version, max_version) = extract_gdb_version_range(line); + return actual_version < start_ver; + } else if let Some(rest) = line.strip_prefix("ignore-gdb-version:").map(str::trim) { + let (min_version, max_version) = extract_gdb_version_range(rest); if max_version < min_version { panic!("Malformed GDB version range: max < min") } - actual_version >= min_version && actual_version <= max_version - } else { - false + return actual_version >= min_version && actual_version <= max_version; } - } else { - false } + false } - // Takes a directive of the form "ignore-gdb-version [- ]", + // Takes a directive of the form " [- ]", // returns the numeric representation of and as // tuple: ( as u32, as u32) // If the part is omitted, the second component of the tuple From bf06e8e0bd991b759e53c4b290f6eef897715418 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 11 Jul 2020 08:45:26 +0000 Subject: [PATCH 20/31] Use Option::as_deref --- src/tools/compiletest/src/main.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 80f611d2ad6a4..3929ce28ab0a6 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -171,7 +171,7 @@ pub fn parse_config(args: Vec) -> Config { .and_then(extract_lldb_version) .map(|(v, b)| (Some(v), b)) .unwrap_or((None, false)); - let color = match matches.opt_str("color").as_ref().map(|x| &**x) { + let color = match matches.opt_str("color").as_deref() { Some("auto") | None => ColorConfig::AutoColor, Some("always") => ColorConfig::AlwaysColor, Some("never") => ColorConfig::NeverColor, @@ -255,7 +255,7 @@ pub fn log_config(config: &Config) { logv(c, format!("stage_id: {}", config.stage_id)); logv(c, format!("mode: {}", config.mode)); logv(c, format!("run_ignored: {}", config.run_ignored)); - logv(c, format!("filter: {}", opt_str(&config.filter.as_ref().map(|re| re.to_owned())))); + logv(c, format!("filter: {}", opt_str(&config.filter))); logv(c, format!("filter_exact: {}", config.filter_exact)); logv( c, @@ -723,9 +723,7 @@ fn make_test_closure( let config = config.clone(); let testpaths = testpaths.clone(); let revision = revision.cloned(); - test::DynTestFn(Box::new(move || { - runtest::run(config, &testpaths, revision.as_ref().map(|s| s.as_str())) - })) + test::DynTestFn(Box::new(move || runtest::run(config, &testpaths, revision.as_deref()))) } /// Returns `true` if the given target is an Android target for the From 0306ffbae6aec4fdb004c1ff1faf8bc8491387a9 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 11 Jul 2020 09:06:59 +0000 Subject: [PATCH 21/31] Extract closure to function --- src/tools/compiletest/src/main.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 3929ce28ab0a6..049bf44d6fd96 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -848,11 +848,11 @@ fn extract_gdb_version(full_version_line: &str) -> Option { let patch = splits.next(); let major: u32 = major.parse().unwrap(); - let (minor, patch): (u32, u32) = match minor.find(|c: char| !c.is_digit(10)) { + let (minor, patch): (u32, u32) = match minor.find(not_a_digit) { None => { let minor = minor.parse().unwrap(); let patch: u32 = match patch { - Some(patch) => match patch.find(|c: char| !c.is_digit(10)) { + Some(patch) => match patch.find(not_a_digit) { None => patch.parse().unwrap(), Some(idx) if idx > 3 => 0, Some(idx) => patch[..idx].parse().unwrap(), @@ -898,15 +898,19 @@ fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> { if let Some(apple_ver) = full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-")) { - if let Some(idx) = apple_ver.find(|c: char| !c.is_digit(10)) { + if let Some(idx) = apple_ver.find(not_a_digit) { let version: u32 = apple_ver[..idx].parse().unwrap(); return Some((version, full_version_line.contains("rust-enabled"))); } } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") { - if let Some(idx) = lldb_ver.find(|c: char| !c.is_digit(10)) { + if let Some(idx) = lldb_ver.find(not_a_digit) { let version: u32 = lldb_ver[..idx].parse().unwrap(); return Some((version * 100, full_version_line.contains("rust-enabled"))); } } None } + +fn not_a_digit(c: char) -> bool { + !c.is_digit(10) +} From 47a0f692250bb988005b031952ac4ddc8d4b474d Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 11 Jul 2020 13:50:46 +0000 Subject: [PATCH 22/31] Use subslice pattern --- src/tools/compiletest/src/header.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 0fc42396e7949..310858bb3379f 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -170,14 +170,14 @@ impl EarlyProps { .take(3) // 3 or more = invalid, so take at most 3. .collect::>>(); - match range_components.len() { - 1 => { - let v = range_components[0].unwrap(); + match *range_components { + [v] => { + let v = v.unwrap(); (v, v) } - 2 => { - let v_min = range_components[0].unwrap(); - let v_max = range_components[1].expect(ERROR_MESSAGE); + [min, max] => { + let v_min = min.unwrap(); + let v_max = max.expect(ERROR_MESSAGE); (v_min, v_max) } _ => panic!(ERROR_MESSAGE), From 95df8024e7e934aec9f985a6680b65fd0d40a428 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 17 Jul 2020 17:23:18 +0100 Subject: [PATCH 23/31] improper_ctypes_definitions: allow `Box` This commit stops linting against `Box` in `extern "C" fn`s for the `improper_ctypes_definitions` lint - boxes are documented to be FFI-safe. Signed-off-by: David Wood --- src/librustc_lint/types.rs | 13 +++++- src/test/ui/lint/lint-ctypes-fn.rs | 5 +-- src/test/ui/lint/lint-ctypes-fn.stderr | 55 +++++++------------------- src/test/ui/lint/lint-ctypes.rs | 2 + src/test/ui/lint/lint-ctypes.stderr | 47 +++++++++++++--------- 5 files changed, 58 insertions(+), 64 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 46741fcf2ba0c..7014722bab824 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -531,6 +531,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match ty.kind { ty::FnPtr(_) => true, ty::Ref(..) => true, + ty::Adt(def, _) + if def.is_box() && matches!(self.mode, ImproperCTypesMode::Definitions) => + { + true + } ty::Adt(def, substs) if def.repr.transparent() && !def.is_union() => { let guaranteed_nonnull_optimization = self .cx @@ -558,7 +563,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } /// Check if this enum can be safely exported based on the "nullable pointer optimization". - /// Currently restricted to function pointers, references, `core::num::NonZero*`, + /// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`, /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. fn is_repr_nullable_ptr( &self, @@ -692,6 +697,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } match ty.kind { + ty::Adt(def, _) + if def.is_box() && matches!(self.mode, ImproperCTypesMode::Definitions) => + { + FfiSafe + } + ty::Adt(def, substs) => { if def.is_phantom_data() { return FfiPhantom(ty); diff --git a/src/test/ui/lint/lint-ctypes-fn.rs b/src/test/ui/lint/lint-ctypes-fn.rs index 67dd7abcf79ef..aa02e57866328 100644 --- a/src/test/ui/lint/lint-ctypes-fn.rs +++ b/src/test/ui/lint/lint-ctypes-fn.rs @@ -71,7 +71,8 @@ pub extern "C" fn str_type(p: &str) { } //~^ ERROR: uses type `str` pub extern "C" fn box_type(p: Box) { } -//~^ ERROR uses type `std::boxed::Box` + +pub extern "C" fn opt_box_type(p: Option>) { } pub extern "C" fn char_type(p: char) { } //~^ ERROR uses type `char` @@ -106,7 +107,6 @@ pub extern "C" fn fn_type2(p: fn()) { } //~^ ERROR uses type `fn()` pub extern "C" fn fn_contained(p: RustBadRet) { } -//~^ ERROR: uses type `std::boxed::Box` pub extern "C" fn transparent_i128(p: TransparentI128) { } //~^ ERROR: uses type `i128` @@ -115,7 +115,6 @@ pub extern "C" fn transparent_str(p: TransparentStr) { } //~^ ERROR: uses type `str` pub extern "C" fn transparent_fn(p: TransparentBadFn) { } -//~^ ERROR: uses type `std::boxed::Box` pub extern "C" fn good3(fptr: Option) { } diff --git a/src/test/ui/lint/lint-ctypes-fn.stderr b/src/test/ui/lint/lint-ctypes-fn.stderr index 66cf195327890..d0a449514e50e 100644 --- a/src/test/ui/lint/lint-ctypes-fn.stderr +++ b/src/test/ui/lint/lint-ctypes-fn.stderr @@ -21,17 +21,8 @@ LL | pub extern "C" fn str_type(p: &str) { } = help: consider using `*const u8` and a length instead = note: string slices have no C equivalent -error: `extern` fn uses type `std::boxed::Box`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:73:31 - | -LL | pub extern "C" fn box_type(p: Box) { } - | ^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout - error: `extern` fn uses type `char`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:76:32 + --> $DIR/lint-ctypes-fn.rs:77:32 | LL | pub extern "C" fn char_type(p: char) { } | ^^^^ not FFI-safe @@ -40,7 +31,7 @@ LL | pub extern "C" fn char_type(p: char) { } = note: the `char` type has no C equivalent error: `extern` fn uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:79:32 + --> $DIR/lint-ctypes-fn.rs:80:32 | LL | pub extern "C" fn i128_type(p: i128) { } | ^^^^ not FFI-safe @@ -48,7 +39,7 @@ LL | pub extern "C" fn i128_type(p: i128) { } = note: 128-bit integers don't currently have a known stable ABI error: `extern` fn uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:82:32 + --> $DIR/lint-ctypes-fn.rs:83:32 | LL | pub extern "C" fn u128_type(p: u128) { } | ^^^^ not FFI-safe @@ -56,7 +47,7 @@ LL | pub extern "C" fn u128_type(p: u128) { } = note: 128-bit integers don't currently have a known stable ABI error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:85:33 + --> $DIR/lint-ctypes-fn.rs:86:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -65,7 +56,7 @@ LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } = note: tuples have unspecified layout error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:88:34 + --> $DIR/lint-ctypes-fn.rs:89:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -74,7 +65,7 @@ LL | pub extern "C" fn tuple_type2(p: I32Pair) { } = note: tuples have unspecified layout error: `extern` fn uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:91:32 + --> $DIR/lint-ctypes-fn.rs:92:32 | LL | pub extern "C" fn zero_size(p: ZeroSize) { } | ^^^^^^^^ not FFI-safe @@ -88,7 +79,7 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:94:40 + --> $DIR/lint-ctypes-fn.rs:95:40 | LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -101,7 +92,7 @@ LL | pub struct ZeroSizeWithPhantomData(PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `std::marker::PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:97:51 + --> $DIR/lint-ctypes-fn.rs:98:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -109,7 +100,7 @@ LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:102:30 + --> $DIR/lint-ctypes-fn.rs:103:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -118,7 +109,7 @@ LL | pub extern "C" fn fn_type(p: RustFn) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:105:31 + --> $DIR/lint-ctypes-fn.rs:106:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -126,15 +117,6 @@ LL | pub extern "C" fn fn_type2(p: fn()) { } = help: consider using an `extern fn(...) -> ...` function pointer instead = note: this function pointer has Rust-specific calling convention -error: `extern` fn uses type `std::boxed::Box`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:108:35 - | -LL | pub extern "C" fn fn_contained(p: RustBadRet) { } - | ^^^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout - error: `extern` fn uses type `i128`, which is not FFI-safe --> $DIR/lint-ctypes-fn.rs:111:39 | @@ -152,17 +134,8 @@ LL | pub extern "C" fn transparent_str(p: TransparentStr) { } = help: consider using `*const u8` and a length instead = note: string slices have no C equivalent -error: `extern` fn uses type `std::boxed::Box`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:117:37 - | -LL | pub extern "C" fn transparent_fn(p: TransparentBadFn) { } - | ^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout - error: `extern` fn uses type `std::marker::PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:161:43 + --> $DIR/lint-ctypes-fn.rs:160:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -170,7 +143,7 @@ LL | pub extern "C" fn unused_generic2() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `std::vec::Vec`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:174:39 + --> $DIR/lint-ctypes-fn.rs:173:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } | ^^^^^^ not FFI-safe @@ -179,7 +152,7 @@ LL | pub extern "C" fn used_generic4(x: Vec) { } = note: this struct has unspecified layout error: `extern` fn uses type `std::vec::Vec`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:177:41 + --> $DIR/lint-ctypes-fn.rs:176:41 | LL | pub extern "C" fn used_generic5() -> Vec { | ^^^^^^ not FFI-safe @@ -187,5 +160,5 @@ LL | pub extern "C" fn used_generic5() -> Vec { = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout -error: aborting due to 20 previous errors +error: aborting due to 17 previous errors diff --git a/src/test/ui/lint/lint-ctypes.rs b/src/test/ui/lint/lint-ctypes.rs index a439a1f339aea..bdf95350c7045 100644 --- a/src/test/ui/lint/lint-ctypes.rs +++ b/src/test/ui/lint/lint-ctypes.rs @@ -48,6 +48,8 @@ extern { pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]` pub fn str_type(p: &str); //~ ERROR: uses type `str` pub fn box_type(p: Box); //~ ERROR uses type `std::boxed::Box` + pub fn opt_box_type(p: Option>); + //~^ ERROR uses type `std::option::Option>` pub fn char_type(p: char); //~ ERROR uses type `char` pub fn i128_type(p: i128); //~ ERROR uses type `i128` pub fn u128_type(p: u128); //~ ERROR uses type `u128` diff --git a/src/test/ui/lint/lint-ctypes.stderr b/src/test/ui/lint/lint-ctypes.stderr index 9821f858d9caf..13b9adca3f9f5 100644 --- a/src/test/ui/lint/lint-ctypes.stderr +++ b/src/test/ui/lint/lint-ctypes.stderr @@ -58,8 +58,17 @@ LL | pub fn box_type(p: Box); = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout +error: `extern` block uses type `std::option::Option>`, which is not FFI-safe + --> $DIR/lint-ctypes.rs:51:28 + | +LL | pub fn opt_box_type(p: Option>); + | ^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum + = note: enum has no representation hint + error: `extern` block uses type `char`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:51:25 + --> $DIR/lint-ctypes.rs:53:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -68,7 +77,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:52:25 + --> $DIR/lint-ctypes.rs:54:25 | LL | pub fn i128_type(p: i128); | ^^^^ not FFI-safe @@ -76,7 +85,7 @@ LL | pub fn i128_type(p: i128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:53:25 + --> $DIR/lint-ctypes.rs:55:25 | LL | pub fn u128_type(p: u128); | ^^^^ not FFI-safe @@ -84,7 +93,7 @@ LL | pub fn u128_type(p: u128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `dyn std::clone::Clone`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:54:26 + --> $DIR/lint-ctypes.rs:56:26 | LL | pub fn trait_type(p: &dyn Clone); | ^^^^^^^^^^ not FFI-safe @@ -92,7 +101,7 @@ LL | pub fn trait_type(p: &dyn Clone); = note: trait objects have no C equivalent error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:55:26 + --> $DIR/lint-ctypes.rs:57:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -101,7 +110,7 @@ LL | pub fn tuple_type(p: (i32, i32)); = note: tuples have unspecified layout error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:56:27 + --> $DIR/lint-ctypes.rs:58:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -110,7 +119,7 @@ LL | pub fn tuple_type2(p: I32Pair); = note: tuples have unspecified layout error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:57:25 + --> $DIR/lint-ctypes.rs:59:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -124,7 +133,7 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:58:33 + --> $DIR/lint-ctypes.rs:60:33 | LL | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -137,7 +146,7 @@ LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `std::marker::PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:61:12 + --> $DIR/lint-ctypes.rs:63:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -145,7 +154,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:62:23 + --> $DIR/lint-ctypes.rs:64:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -154,7 +163,7 @@ LL | pub fn fn_type(p: RustFn); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:63:24 + --> $DIR/lint-ctypes.rs:65:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -163,7 +172,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `std::boxed::Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:64:28 + --> $DIR/lint-ctypes.rs:66:28 | LL | pub fn fn_contained(p: RustBadRet); | ^^^^^^^^^^ not FFI-safe @@ -172,7 +181,7 @@ LL | pub fn fn_contained(p: RustBadRet); = note: this struct has unspecified layout error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:65:32 + --> $DIR/lint-ctypes.rs:67:32 | LL | pub fn transparent_i128(p: TransparentI128); | ^^^^^^^^^^^^^^^ not FFI-safe @@ -180,7 +189,7 @@ LL | pub fn transparent_i128(p: TransparentI128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:66:31 + --> $DIR/lint-ctypes.rs:68:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -189,7 +198,7 @@ LL | pub fn transparent_str(p: TransparentStr); = note: string slices have no C equivalent error: `extern` block uses type `std::boxed::Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:67:30 + --> $DIR/lint-ctypes.rs:69:30 | LL | pub fn transparent_fn(p: TransparentBadFn); | ^^^^^^^^^^^^^^^^ not FFI-safe @@ -198,7 +207,7 @@ LL | pub fn transparent_fn(p: TransparentBadFn); = note: this struct has unspecified layout error: `extern` block uses type `[u8; 8]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:68:27 + --> $DIR/lint-ctypes.rs:70:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -207,7 +216,7 @@ LL | pub fn raw_array(arr: [u8; 8]); = note: passing raw arrays by value is not FFI-safe error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:70:34 + --> $DIR/lint-ctypes.rs:72:34 | LL | pub static static_u128_type: u128; | ^^^^ not FFI-safe @@ -215,12 +224,12 @@ LL | pub static static_u128_type: u128; = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:71:40 + --> $DIR/lint-ctypes.rs:73:40 | LL | pub static static_u128_array_type: [u128; 16]; | ^^^^^^^^^^ not FFI-safe | = note: 128-bit integers don't currently have a known stable ABI -error: aborting due to 23 previous errors +error: aborting due to 24 previous errors From 4127ed1732f8fe55363df1b65b03b2e58d25fc7f Mon Sep 17 00:00:00 2001 From: aticu <15schnic@gmail.com> Date: Fri, 17 Jul 2020 19:47:25 +0200 Subject: [PATCH 24/31] Fix `Safety` docs for `from_raw_parts_mut` --- src/libcore/slice/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 309a4ddb00657..20b2c3d3c965a 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -6095,7 +6095,7 @@ pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `data` must be [valid] for writes for `len * mem::size_of::()` many bytes, +/// * `data` must be [valid] for boths reads and writes for `len * mem::size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// /// * The entire memory range of this slice must be contained within a single allocated object! From 4adb13c3a4328046688f04d4a15c8c3321ca6bfb Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Fri, 17 Jul 2020 22:10:37 +0200 Subject: [PATCH 25/31] rustbuild: drop tool::should_install Always install when the build succeeds Fixes #74431 Signed-off-by: Marc-Antoine Perennou --- src/bootstrap/install.rs | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 7026b25d1b984..b4a817738ff3f 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -159,11 +159,6 @@ macro_rules! install { config.extended && config.tools.as_ref() .map_or(true, |t| t.contains($path)) } - - #[allow(dead_code)] - fn should_install(builder: &Builder<'_>) -> bool { - builder.config.tools.as_ref().map_or(false, |t| t.contains($path)) - } } impl Step for $name { @@ -210,8 +205,7 @@ install!((self, builder, _config), install_cargo(builder, self.compiler.stage, self.target); }; Rls, "rls", Self::should_build(_config), only_hosts: true, { - if builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }).is_some() || - Self::should_install(builder) { + if builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }).is_some() { install_rls(builder, self.compiler.stage, self.target); } else { builder.info( @@ -221,27 +215,14 @@ install!((self, builder, _config), }; RustAnalyzer, "rust-analyzer", Self::should_build(_config), only_hosts: true, { builder.ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target }); - if Self::should_install(builder) { - install_rust_analyzer(builder, self.compiler.stage, self.target); - } else { - builder.info( - &format!("skipping Install rust-analyzer stage{} ({})", self.compiler.stage, self.target), - ); - } + install_rust_analyzer(builder, self.compiler.stage, self.target); }; Clippy, "clippy", Self::should_build(_config), only_hosts: true, { builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target }); - if Self::should_install(builder) { - install_clippy(builder, self.compiler.stage, self.target); - } else { - builder.info( - &format!("skipping Install clippy stage{} ({})", self.compiler.stage, self.target), - ); - } + install_clippy(builder, self.compiler.stage, self.target); }; Miri, "miri", Self::should_build(_config), only_hosts: true, { - if builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }).is_some() || - Self::should_install(builder) { + if builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }).is_some() { install_miri(builder, self.compiler.stage, self.target); } else { builder.info( @@ -253,7 +234,7 @@ install!((self, builder, _config), if builder.ensure(dist::Rustfmt { compiler: self.compiler, target: self.target - }).is_some() || Self::should_install(builder) { + }).is_some() { install_rustfmt(builder, self.compiler.stage, self.target); } else { builder.info( From 91314e2d279374bb9eec79754b3480d359d7a412 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Fri, 17 Jul 2020 12:04:04 -0700 Subject: [PATCH 26/31] Use intra-doc links in BTreeSet docs --- src/liballoc/collections/btree/set.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/liballoc/collections/btree/set.rs b/src/liballoc/collections/btree/set.rs index d8959966fe5ad..530cb0c91b8e3 100644 --- a/src/liballoc/collections/btree/set.rs +++ b/src/liballoc/collections/btree/set.rs @@ -22,7 +22,6 @@ use super::Recover; /// to any other item, as determined by the [`Ord`] trait, changes while it is in the set. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. /// -/// [`BTreeMap`]: struct.BTreeMap.html /// [`Ord`]: ../../std/cmp/trait.Ord.html /// [`Cell`]: ../../std/cell/struct.Cell.html /// [`RefCell`]: ../../std/cell/struct.RefCell.html @@ -78,8 +77,7 @@ impl Clone for BTreeSet { /// This `struct` is created by the [`iter`] method on [`BTreeSet`]. /// See its documentation for more. /// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`iter`]: struct.BTreeSet.html#method.iter +/// [`iter`]: BTreeSet::iter #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, T: 'a> { iter: Keys<'a, T, ()>, @@ -97,8 +95,7 @@ impl fmt::Debug for Iter<'_, T> { /// This `struct` is created by the [`into_iter`] method on [`BTreeSet`] /// (provided by the `IntoIterator` trait). See its documentation for more. /// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`into_iter`]: struct.BTreeSet.html#method.into_iter +/// [`into_iter`]: BTreeSet#method.into_iter #[stable(feature = "rust1", since = "1.0.0")] #[derive(Debug)] pub struct IntoIter { @@ -110,8 +107,7 @@ pub struct IntoIter { /// This `struct` is created by the [`range`] method on [`BTreeSet`]. /// See its documentation for more. /// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`range`]: struct.BTreeSet.html#method.range +/// [`range`]: BTreeSet::range #[derive(Debug)] #[stable(feature = "btree_range", since = "1.17.0")] pub struct Range<'a, T: 'a> { @@ -194,8 +190,7 @@ where /// This `struct` is created by the [`difference`] method on [`BTreeSet`]. /// See its documentation for more. /// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`difference`]: struct.BTreeSet.html#method.difference +/// [`difference`]: BTreeSet::difference #[stable(feature = "rust1", since = "1.0.0")] pub struct Difference<'a, T: 'a> { inner: DifferenceInner<'a, T>, @@ -227,8 +222,7 @@ impl fmt::Debug for Difference<'_, T> { /// This `struct` is created by the [`symmetric_difference`] method on /// [`BTreeSet`]. See its documentation for more. /// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`symmetric_difference`]: struct.BTreeSet.html#method.symmetric_difference +/// [`symmetric_difference`]: BTreeSet::symmetric_difference #[stable(feature = "rust1", since = "1.0.0")] pub struct SymmetricDifference<'a, T: 'a>(MergeIterInner>); @@ -244,8 +238,7 @@ impl fmt::Debug for SymmetricDifference<'_, T> { /// This `struct` is created by the [`intersection`] method on [`BTreeSet`]. /// See its documentation for more. /// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`intersection`]: struct.BTreeSet.html#method.intersection +/// [`intersection`]: BTreeSet::intersection #[stable(feature = "rust1", since = "1.0.0")] pub struct Intersection<'a, T: 'a> { inner: IntersectionInner<'a, T>, @@ -277,8 +270,7 @@ impl fmt::Debug for Intersection<'_, T> { /// This `struct` is created by the [`union`] method on [`BTreeSet`]. /// See its documentation for more. /// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`union`]: struct.BTreeSet.html#method.union +/// [`union`]: BTreeSet::union #[stable(feature = "rust1", since = "1.0.0")] pub struct Union<'a, T: 'a>(MergeIterInner>); From 748634e151696a15875299e7b8307868080cd94c Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Fri, 17 Jul 2020 12:44:44 -0700 Subject: [PATCH 27/31] Use intra doc links in std::str --- src/libcore/str/mod.rs | 226 +++++++++++++---------------------------- 1 file changed, 73 insertions(+), 153 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 86e8d5c42b7ad..faf58cafbb70b 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -31,9 +31,8 @@ pub mod lossy; /// `FromStr`'s [`from_str`] method is often used implicitly, through /// [`str`]'s [`parse`] method. See [`parse`]'s documentation for examples. /// -/// [`from_str`]: #tymethod.from_str -/// [`str`]: ../../std/primitive.str.html -/// [`parse`]: ../../std/primitive.str.html#method.parse +/// [`from_str`]: FromStr::from_str +/// [`parse`]: str::parse /// /// `FromStr` does not have a lifetime parameter, and so you can only parse types /// that do not contain a lifetime parameter themselves. In other words, you can @@ -143,7 +142,7 @@ impl FromStr for bool { /// An error returned when parsing a `bool` using [`from_str`] fails /// -/// [`from_str`]: ../../std/primitive.bool.html#method.from_str +/// [`from_str`]: FromStr::from_str #[derive(Debug, Clone, PartialEq, Eq)] #[stable(feature = "rust1", since = "1.0.0")] pub struct ParseBoolError { @@ -266,8 +265,7 @@ impl Utf8Error { /// that it is valid UTF-8. `from_utf8()` checks to ensure that the bytes are valid /// UTF-8, and then does the conversion. /// -/// [`&str`]: ../../std/primitive.str.html -/// [`u8`]: ../../std/primitive.u8.html +/// [`&str`]: str /// [byteslice]: ../../std/primitive.slice.html /// /// If you are sure that the byte slice is valid UTF-8, and you don't want to @@ -398,7 +396,7 @@ pub fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { /// it are valid UTF-8. If this constraint is violated, undefined behavior /// results, as the rest of Rust assumes that [`&str`]s are valid UTF-8. /// -/// [`&str`]: ../../std/primitive.str.html +/// [`&str`]: str /// /// # Examples /// @@ -429,9 +427,7 @@ pub unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { /// Converts a slice of bytes to a string slice without checking /// that the string contains valid UTF-8; mutable version. /// -/// See the immutable version, [`from_utf8_unchecked()`][fromutf8], for more information. -/// -/// [fromutf8]: fn.from_utf8_unchecked.html +/// See the immutable version, [`from_utf8_unchecked()`] for more information. /// /// # Examples /// @@ -476,13 +472,11 @@ Section: Iterators /// An iterator over the [`char`]s of a string slice. /// -/// [`char`]: ../../std/primitive.char.html /// /// This struct is created by the [`chars`] method on [`str`]. /// See its documentation for more. /// -/// [`chars`]: ../../std/primitive.str.html#method.chars -/// [`str`]: ../../std/primitive.str.html +/// [`chars`]: str::chars #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Chars<'a> { @@ -676,13 +670,10 @@ impl<'a> Chars<'a> { /// An iterator over the [`char`]s of a string slice, and their positions. /// -/// [`char`]: ../../std/primitive.char.html -/// /// This struct is created by the [`char_indices`] method on [`str`]. /// See its documentation for more. /// -/// [`char_indices`]: ../../std/primitive.str.html#method.char_indices -/// [`str`]: ../../std/primitive.str.html +/// [`char_indices`]: str::char_indices #[derive(Clone, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct CharIndices<'a> { @@ -756,8 +747,7 @@ impl<'a> CharIndices<'a> { /// This struct is created by the [`bytes`] method on [`str`]. /// See its documentation for more. /// -/// [`bytes`]: ../../std/primitive.str.html#method.bytes -/// [`str`]: ../../std/primitive.str.html +/// [`bytes`]: str::bytes #[stable(feature = "rust1", since = "1.0.0")] #[derive(Clone, Debug)] pub struct Bytes<'a>(Copied>); @@ -1249,12 +1239,12 @@ generate_pattern_iterators! { forward: /// Created with the method [`split`]. /// - /// [`split`]: ../../std/primitive.str.html#method.split + /// [`split`]: str::split struct Split; reverse: /// Created with the method [`rsplit`]. /// - /// [`rsplit`]: ../../std/primitive.str.html#method.rsplit + /// [`rsplit`]: str::rsplit struct RSplit; stability: #[stable(feature = "rust1", since = "1.0.0")] @@ -1267,12 +1257,12 @@ generate_pattern_iterators! { forward: /// Created with the method [`split_terminator`]. /// - /// [`split_terminator`]: ../../std/primitive.str.html#method.split_terminator + /// [`split_terminator`]: str::split_terminator struct SplitTerminator; reverse: /// Created with the method [`rsplit_terminator`]. /// - /// [`rsplit_terminator`]: ../../std/primitive.str.html#method.rsplit_terminator + /// [`rsplit_terminator`]: str::rsplit_terminator struct RSplitTerminator; stability: #[stable(feature = "rust1", since = "1.0.0")] @@ -1343,12 +1333,12 @@ generate_pattern_iterators! { forward: /// Created with the method [`splitn`]. /// - /// [`splitn`]: ../../std/primitive.str.html#method.splitn + /// [`splitn`]: str::splitn struct SplitN; reverse: /// Created with the method [`rsplitn`]. /// - /// [`rsplitn`]: ../../std/primitive.str.html#method.rsplitn + /// [`rsplitn`]: str::rsplitn struct RSplitN; stability: #[stable(feature = "rust1", since = "1.0.0")] @@ -1398,12 +1388,12 @@ generate_pattern_iterators! { forward: /// Created with the method [`match_indices`]. /// - /// [`match_indices`]: ../../std/primitive.str.html#method.match_indices + /// [`match_indices`]: str::match_indices struct MatchIndices; reverse: /// Created with the method [`rmatch_indices`]. /// - /// [`rmatch_indices`]: ../../std/primitive.str.html#method.rmatch_indices + /// [`rmatch_indices`]: str::rmatch_indices struct RMatchIndices; stability: #[stable(feature = "str_match_indices", since = "1.5.0")] @@ -1455,12 +1445,12 @@ generate_pattern_iterators! { forward: /// Created with the method [`matches`]. /// - /// [`matches`]: ../../std/primitive.str.html#method.matches + /// [`matches`]: str::matches struct Matches; reverse: /// Created with the method [`rmatches`]. /// - /// [`rmatches`]: ../../std/primitive.str.html#method.rmatches + /// [`rmatches`]: str::rmatches struct RMatches; stability: #[stable(feature = "str_matches", since = "1.2.0")] @@ -1474,8 +1464,7 @@ generate_pattern_iterators! { /// This struct is created with the [`lines`] method on [`str`]. /// See its documentation for more. /// -/// [`lines`]: ../../std/primitive.str.html#method.lines -/// [`str`]: ../../std/primitive.str.html +/// [`lines`]: str::lines #[stable(feature = "rust1", since = "1.0.0")] #[derive(Clone, Debug)] pub struct Lines<'a>(Map, LinesAnyMap>); @@ -1513,7 +1502,7 @@ impl FusedIterator for Lines<'_> {} /// Created with the method [`lines_any`]. /// -/// [`lines_any`]: ../../std/primitive.str.html#method.lines_any +/// [`lines_any`]: str::lines_any #[stable(feature = "rust1", since = "1.0.0")] #[rustc_deprecated(since = "1.4.0", reason = "use lines()/Lines instead now")] #[derive(Clone, Debug)] @@ -2347,9 +2336,7 @@ impl str { } /// Converts a string slice to a byte slice. To convert the byte slice back - /// into a string slice, use the [`str::from_utf8`] function. - /// - /// [`str::from_utf8`]: ./str/fn.from_utf8.html + /// into a string slice, use the [`from_utf8`] function. /// /// # Examples /// @@ -2429,8 +2416,7 @@ impl str { /// The caller must ensure that the returned pointer is never written to. /// If you need to mutate the contents of the string slice, use [`as_mut_ptr`]. /// - /// [`u8`]: primitive.u8.html - /// [`as_mut_ptr`]: #method.as_mut_ptr + /// [`as_mut_ptr`]: str::as_mut_ptr /// /// # Examples /// @@ -2455,8 +2441,6 @@ impl str { /// /// It is your responsibility to make sure that the string slice only gets /// modified in a way that it remains valid UTF-8. - /// - /// [`u8`]: primitive.u8.html #[stable(feature = "str_as_mut_ptr", since = "1.36.0")] #[inline] pub fn as_mut_ptr(&mut self) -> *mut u8 { @@ -2468,8 +2452,6 @@ impl str { /// This is the non-panicking alternative to indexing the `str`. Returns /// [`None`] whenever equivalent indexing operation would panic. /// - /// [`None`]: option/enum.Option.html#variant.None - /// /// # Examples /// /// ``` @@ -2495,8 +2477,6 @@ impl str { /// This is the non-panicking alternative to indexing the `str`. Returns /// [`None`] whenever equivalent indexing operation would panic. /// - /// [`None`]: option/enum.Option.html#variant.None - /// /// # Examples /// /// ``` @@ -2600,8 +2580,7 @@ impl str { /// This is generally not recommended, use with caution! For a safe /// alternative see [`str`] and [`Index`]. /// - /// [`str`]: primitive.str.html - /// [`Index`]: ops/trait.Index.html + /// [`Index`]: crate::ops::Index /// /// This new slice goes from `begin` to `end`, including `begin` but /// excluding `end`. @@ -2609,7 +2588,7 @@ impl str { /// To get a mutable string slice instead, see the /// [`slice_mut_unchecked`] method. /// - /// [`slice_mut_unchecked`]: #method.slice_mut_unchecked + /// [`slice_mut_unchecked`]: str::slice_mut_unchecked /// /// # Safety /// @@ -2652,8 +2631,7 @@ impl str { /// This is generally not recommended, use with caution! For a safe /// alternative see [`str`] and [`IndexMut`]. /// - /// [`str`]: primitive.str.html - /// [`IndexMut`]: ops/trait.IndexMut.html + /// [`IndexMut`]: crate::ops::IndexMut /// /// This new slice goes from `begin` to `end`, including `begin` but /// excluding `end`. @@ -2661,7 +2639,7 @@ impl str { /// To get an immutable string slice instead, see the /// [`slice_unchecked`] method. /// - /// [`slice_unchecked`]: #method.slice_unchecked + /// [`slice_unchecked`]: str::slice_unchecked /// /// # Safety /// @@ -2692,7 +2670,7 @@ impl str { /// To get mutable string slices instead, see the [`split_at_mut`] /// method. /// - /// [`split_at_mut`]: #method.split_at_mut + /// [`split_at_mut`]: str::split_at_mut /// /// # Panics /// @@ -2733,7 +2711,7 @@ impl str { /// /// To get immutable string slices instead, see the [`split_at`] method. /// - /// [`split_at`]: #method.split_at + /// [`split_at`]: str::split_at /// /// # Panics /// @@ -2913,7 +2891,7 @@ impl str { /// Core Property `White_Space`. If you only want to split on ASCII whitespace /// instead, use [`split_ascii_whitespace`]. /// - /// [`split_ascii_whitespace`]: #method.split_ascii_whitespace + /// [`split_ascii_whitespace`]: str::split_ascii_whitespace /// /// # Examples /// @@ -2954,7 +2932,7 @@ impl str { /// /// To split by Unicode `Whitespace` instead, use [`split_whitespace`]. /// - /// [`split_whitespace`]: #method.split_whitespace + /// [`split_whitespace`]: str::split_whitespace /// /// # Examples /// @@ -3068,8 +3046,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Examples /// @@ -3095,8 +3072,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Examples /// @@ -3121,8 +3097,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Examples /// @@ -3150,9 +3125,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`None`]: option/enum.Option.html#variant.None - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Examples /// @@ -3199,9 +3172,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`None`]: option/enum.Option.html#variant.None - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Examples /// @@ -3247,8 +3218,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Iterator behavior /// @@ -3256,12 +3226,10 @@ impl str { /// allows a reverse search and forward/reverse search yields the same /// elements. This is true for, e.g., [`char`], but not for `&str`. /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// /// If the pattern allows a reverse search but its results might differ /// from a forward search, the [`rsplit`] method can be used. /// - /// [`rsplit`]: #method.rsplit + /// [`rsplit`]: str::rsplit /// /// # Examples /// @@ -3348,7 +3316,7 @@ impl str { /// /// Use [`split_whitespace`] for this behavior. /// - /// [`split_whitespace`]: #method.split_whitespace + /// [`split_whitespace`]: str::split_whitespace #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { @@ -3369,8 +3337,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Examples /// @@ -3409,8 +3376,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Iterator behavior /// @@ -3418,11 +3384,9 @@ impl str { /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse /// search yields the same elements. /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// /// For iterating from the front, the [`split`] method can be used. /// - /// [`split`]: #method.split + /// [`split`]: str::split /// /// # Examples /// @@ -3463,13 +3427,12 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// Equivalent to [`split`], except that the trailing substring /// is skipped if empty. /// - /// [`split`]: #method.split + /// [`split`]: str::split /// /// This method can be used for string data that is _terminated_, /// rather than _separated_ by a pattern. @@ -3480,12 +3443,10 @@ impl str { /// allows a reverse search and forward/reverse search yields the same /// elements. This is true for, e.g., [`char`], but not for `&str`. /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// /// If the pattern allows a reverse search but its results might differ /// from a forward search, the [`rsplit_terminator`] method can be used. /// - /// [`rsplit_terminator`]: #method.rsplit_terminator + /// [`rsplit_terminator`]: str::rsplit_terminator /// /// # Examples /// @@ -3510,13 +3471,12 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// Equivalent to [`split`], except that the trailing substring is /// skipped if empty. /// - /// [`split`]: #method.split + /// [`split`]: str::split /// /// This method can be used for string data that is _terminated_, /// rather than _separated_ by a pattern. @@ -3530,7 +3490,7 @@ impl str { /// For iterating from the front, the [`split_terminator`] method can be /// used. /// - /// [`split_terminator`]: #method.split_terminator + /// [`split_terminator`]: str::split_terminator /// /// # Examples /// @@ -3559,8 +3519,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Iterator behavior /// @@ -3570,7 +3529,7 @@ impl str { /// If the pattern allows a reverse search, the [`rsplitn`] method can be /// used. /// - /// [`rsplitn`]: #method.rsplitn + /// [`rsplitn`]: str::rsplitn /// /// # Examples /// @@ -3612,8 +3571,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Iterator behavior /// @@ -3622,7 +3580,7 @@ impl str { /// /// For splitting from the front, the [`splitn`] method can be used. /// - /// [`splitn`]: #method.splitn + /// [`splitn`]: str::splitn /// /// # Examples /// @@ -3660,8 +3618,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Iterator behavior /// @@ -3669,12 +3626,10 @@ impl str { /// allows a reverse search and forward/reverse search yields the same /// elements. This is true for, e.g., [`char`], but not for `&str`. /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// /// If the pattern allows a reverse search but its results might differ /// from a forward search, the [`rmatches`] method can be used. /// - /// [`rmatches`]: #method.rmatches + /// [`rmatches`]: str::matches /// /// # Examples /// @@ -3699,8 +3654,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Iterator behavior /// @@ -3708,11 +3662,9 @@ impl str { /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse /// search yields the same elements. /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// /// For iterating from the front, the [`matches`] method can be used. /// - /// [`matches`]: #method.matches + /// [`matches`]: str::matches /// /// # Examples /// @@ -3743,8 +3695,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Iterator behavior /// @@ -3752,12 +3703,10 @@ impl str { /// allows a reverse search and forward/reverse search yields the same /// elements. This is true for, e.g., [`char`], but not for `&str`. /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// /// If the pattern allows a reverse search but its results might differ /// from a forward search, the [`rmatch_indices`] method can be used. /// - /// [`rmatch_indices`]: #method.rmatch_indices + /// [`rmatch_indices`]: str::match_indices /// /// # Examples /// @@ -3788,8 +3737,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Iterator behavior /// @@ -3797,11 +3745,9 @@ impl str { /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse /// search yields the same elements. /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// /// For iterating from the front, the [`match_indices`] method can be used. /// - /// [`match_indices`]: #method.match_indices + /// [`match_indices`]: str::match_indices /// /// # Examples /// @@ -4009,8 +3955,7 @@ impl str { /// The [pattern] can be a [`char`], a slice of [`char`]s, or a function /// or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Examples /// @@ -4057,8 +4002,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Text directionality /// @@ -4102,8 +4046,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Examples /// @@ -4130,8 +4073,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Examples /// @@ -4157,8 +4099,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Text directionality /// @@ -4206,8 +4147,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Text directionality /// @@ -4243,8 +4183,7 @@ impl str { /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. /// - /// [`char`]: primitive.char.html - /// [pattern]: str/pattern/index.html + /// [pattern]: self::pattern /// /// # Text directionality /// @@ -4292,15 +4231,14 @@ impl str { /// you're trying to parse into. /// /// `parse` can parse any type that implements the [`FromStr`] trait. - /// - /// [`FromStr`]: str/trait.FromStr.html + /// /// # Errors /// /// Will return [`Err`] if it's not possible to parse this string slice into /// the desired type. /// - /// [`Err`]: str/trait.FromStr.html#associatedtype.Err + /// [`Err`]: FromStr::Err /// /// # Examples /// @@ -4428,8 +4366,6 @@ impl str { /// Note: only extended grapheme codepoints that begin the string will be /// escaped. /// - /// [`char::escape_debug`]: ../std/primitive.char.html#method.escape_debug - /// /// # Examples /// /// As an iterator: @@ -4474,8 +4410,6 @@ impl str { /// Return an iterator that escapes each char in `self` with [`char::escape_default`]. /// - /// [`char::escape_default`]: ../std/primitive.char.html#method.escape_default - /// /// # Examples /// /// As an iterator: @@ -4512,8 +4446,6 @@ impl str { /// Return an iterator that escapes each char in `self` with [`char::escape_unicode`]. /// - /// [`char::escape_unicode`]: ../std/primitive.char.html#method.escape_unicode - /// /// # Examples /// /// As an iterator: @@ -4596,8 +4528,7 @@ impl Default for &mut str { /// This struct is created by the [`split_whitespace`] method on [`str`]. /// See its documentation for more. /// -/// [`split_whitespace`]: ../../std/primitive.str.html#method.split_whitespace -/// [`str`]: ../../std/primitive.str.html +/// [`split_whitespace`]: str::split_whitespace #[stable(feature = "split_whitespace", since = "1.1.0")] #[derive(Clone, Debug)] pub struct SplitWhitespace<'a> { @@ -4610,8 +4541,7 @@ pub struct SplitWhitespace<'a> { /// This struct is created by the [`split_ascii_whitespace`] method on [`str`]. /// See its documentation for more. /// -/// [`split_ascii_whitespace`]: ../../std/primitive.str.html#method.split_ascii_whitespace -/// [`str`]: ../../std/primitive.str.html +/// [`split_ascii_whitespace`]: str::split_ascii_whitespace #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] #[derive(Clone, Debug)] pub struct SplitAsciiWhitespace<'a> { @@ -4626,8 +4556,7 @@ pub struct SplitAsciiWhitespace<'a> { /// This struct is created by the [`split_inclusive`] method on [`str`]. /// See its documentation for more. /// -/// [`split_inclusive`]: ../../std/primitive.str.html#method.split_inclusive -/// [`str`]: ../../std/primitive.str.html +/// [`split_inclusive`]: str::split_inclusive #[unstable(feature = "split_inclusive", issue = "72360")] pub struct SplitInclusive<'a, P: Pattern<'a>>(SplitInternal<'a, P>); @@ -4761,13 +4690,10 @@ impl<'a, P: Pattern<'a>> FusedIterator for SplitInclusive<'a, P> {} /// An iterator of [`u16`] over the string encoded as UTF-16. /// -/// [`u16`]: ../../std/primitive.u16.html -/// /// This struct is created by the [`encode_utf16`] method on [`str`]. /// See its documentation for more. /// -/// [`encode_utf16`]: ../../std/primitive.str.html#method.encode_utf16 -/// [`str`]: ../../std/primitive.str.html +/// [`encode_utf16`]: str::encode_utf16 #[derive(Clone)] #[stable(feature = "encode_utf16", since = "1.8.0")] pub struct EncodeUtf16<'a> { @@ -4818,8 +4744,6 @@ impl<'a> Iterator for EncodeUtf16<'a> { impl FusedIterator for EncodeUtf16<'_> {} /// The return type of [`str::escape_debug`]. -/// -/// [`str::escape_debug`]: ../../std/primitive.str.html#method.escape_debug #[stable(feature = "str_escape", since = "1.34.0")] #[derive(Clone, Debug)] pub struct EscapeDebug<'a> { @@ -4830,8 +4754,6 @@ pub struct EscapeDebug<'a> { } /// The return type of [`str::escape_default`]. -/// -/// [`str::escape_default`]: ../../std/primitive.str.html#method.escape_default #[stable(feature = "str_escape", since = "1.34.0")] #[derive(Clone, Debug)] pub struct EscapeDefault<'a> { @@ -4839,8 +4761,6 @@ pub struct EscapeDefault<'a> { } /// The return type of [`str::escape_unicode`]. -/// -/// [`str::escape_unicode`]: ../../std/primitive.str.html#method.escape_unicode #[stable(feature = "str_escape", since = "1.34.0")] #[derive(Clone, Debug)] pub struct EscapeUnicode<'a> { From 4b6a0278fe2d1c8d74a6bfe5b14c3022ca3b3c46 Mon Sep 17 00:00:00 2001 From: Federico Ponzi Date: Sat, 18 Jul 2020 00:01:27 +0200 Subject: [PATCH 28/31] fixes #67108 by using the external crate --- Cargo.lock | 7 ++++++ src/librustc_codegen_ssa/Cargo.toml | 1 + src/librustc_codegen_ssa/back/rpath.rs | 33 ++------------------------ 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28ff6b3b1ebf2..3ee7d07900700 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2142,6 +2142,12 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "pathdiff" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877630b3de15c0b64cc52f659345724fbf6bdad9bd9566699fc53688f3c34a34" + [[package]] name = "percent-encoding" version = "1.0.1" @@ -3318,6 +3324,7 @@ dependencies = [ "log", "memmap", "num_cpus", + "pathdiff", "rustc_apfloat", "rustc_ast", "rustc_attr", diff --git a/src/librustc_codegen_ssa/Cargo.toml b/src/librustc_codegen_ssa/Cargo.toml index eeb6b4aabcf29..e100e0095c92a 100644 --- a/src/librustc_codegen_ssa/Cargo.toml +++ b/src/librustc_codegen_ssa/Cargo.toml @@ -18,6 +18,7 @@ log = "0.4.5" libc = "0.2.50" jobserver = "0.1.11" tempfile = "3.1" +pathdiff = "0.2.0" rustc_serialize = { path = "../librustc_serialize" } rustc_ast = { path = "../librustc_ast" } diff --git a/src/librustc_codegen_ssa/back/rpath.rs b/src/librustc_codegen_ssa/back/rpath.rs index c02e4f279b1fb..e1d649c6ed3de 100644 --- a/src/librustc_codegen_ssa/back/rpath.rs +++ b/src/librustc_codegen_ssa/back/rpath.rs @@ -2,6 +2,7 @@ use rustc_data_structures::fx::FxHashSet; use std::env; use std::fs; use std::path::{Path, PathBuf}; +use pathdiff::diff_paths; use rustc_hir::def_id::CrateNum; use rustc_middle::middle::cstore::LibSource; @@ -109,37 +110,7 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> Str // In particular, this handles the case on unix where both paths are // absolute but with only the root as the common directory. fn path_relative_from(path: &Path, base: &Path) -> Option { - use std::path::Component; - - if path.is_absolute() != base.is_absolute() { - path.is_absolute().then(|| PathBuf::from(path)) - } else { - let mut ita = path.components(); - let mut itb = base.components(); - let mut comps: Vec> = vec![]; - loop { - match (ita.next(), itb.next()) { - (None, None) => break, - (Some(a), None) => { - comps.push(a); - comps.extend(ita.by_ref()); - break; - } - (None, _) => comps.push(Component::ParentDir), - (Some(a), Some(b)) if comps.is_empty() && a == b => (), - (Some(a), Some(b)) if b == Component::CurDir => comps.push(a), - (Some(_), Some(b)) if b == Component::ParentDir => return None, - (Some(a), Some(_)) => { - comps.push(Component::ParentDir); - comps.extend(itb.map(|_| Component::ParentDir)); - comps.push(a); - comps.extend(ita.by_ref()); - break; - } - } - } - Some(comps.iter().map(|c| c.as_os_str()).collect()) - } + diff_paths(path, base) } fn get_install_prefix_rpath(config: &mut RPathConfig<'_>) -> String { From 5702ce8962b37affa29d9acafeefdf379fdd1dcd Mon Sep 17 00:00:00 2001 From: Federico Ponzi Date: Sat, 18 Jul 2020 01:00:17 +0200 Subject: [PATCH 29/31] Allows pathdiff package --- src/tools/tidy/src/deps.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index b7d3d428cd283..559267a494f29 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -123,6 +123,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "opaque-debug", "parking_lot", "parking_lot_core", + "pathdiff", "pkg-config", "polonius-engine", "ppv-lite86", From fe639057086fa7bef1e964bf3a211517b04bc328 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 18 Jul 2020 10:09:47 +1000 Subject: [PATCH 30/31] link once_cell feature to #74465 --- src/libcore/lazy.rs | 42 +++++++++++++++++----------------- src/libcore/lib.rs | 2 +- src/libstd/lazy.rs | 56 ++++++++++++++++++++++----------------------- src/libstd/lib.rs | 2 +- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/libcore/lazy.rs b/src/libcore/lazy.rs index e9af66ff64264..5cf7217ef11e8 100644 --- a/src/libcore/lazy.rs +++ b/src/libcore/lazy.rs @@ -26,20 +26,20 @@ use crate::ops::Deref; /// assert_eq!(value, "Hello, World!"); /// assert!(cell.get().is_some()); /// ``` -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] pub struct OnceCell { // Invariant: written to at most once. inner: UnsafeCell>, } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl Default for OnceCell { fn default() -> Self { Self::new() } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl fmt::Debug for OnceCell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.get() { @@ -49,7 +49,7 @@ impl fmt::Debug for OnceCell { } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl Clone for OnceCell { fn clone(&self) -> OnceCell { let res = OnceCell::new(); @@ -63,17 +63,17 @@ impl Clone for OnceCell { } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl PartialEq for OnceCell { fn eq(&self, other: &Self) -> bool { self.get() == other.get() } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl Eq for OnceCell {} -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl From for OnceCell { fn from(value: T) -> Self { OnceCell { inner: UnsafeCell::new(Some(value)) } @@ -82,7 +82,7 @@ impl From for OnceCell { impl OnceCell { /// Creates a new empty cell. - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub const fn new() -> OnceCell { OnceCell { inner: UnsafeCell::new(None) } } @@ -90,7 +90,7 @@ impl OnceCell { /// Gets the reference to the underlying value. /// /// Returns `None` if the cell is empty. - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn get(&self) -> Option<&T> { // Safety: Safe due to `inner`'s invariant unsafe { &*self.inner.get() }.as_ref() @@ -99,7 +99,7 @@ impl OnceCell { /// Gets the mutable reference to the underlying value. /// /// Returns `None` if the cell is empty. - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn get_mut(&mut self) -> Option<&mut T> { // Safety: Safe because we have unique access unsafe { &mut *self.inner.get() }.as_mut() @@ -127,7 +127,7 @@ impl OnceCell { /// /// assert!(cell.get().is_some()); /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn set(&self, value: T) -> Result<(), T> { // Safety: Safe because we cannot have overlapping mutable borrows let slot = unsafe { &*self.inner.get() }; @@ -168,7 +168,7 @@ impl OnceCell { /// let value = cell.get_or_init(|| unreachable!()); /// assert_eq!(value, &92); /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn get_or_init(&self, f: F) -> &T where F: FnOnce() -> T, @@ -206,7 +206,7 @@ impl OnceCell { /// assert_eq!(value, Ok(&92)); /// assert_eq!(cell.get(), Some(&92)) /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn get_or_try_init(&self, f: F) -> Result<&T, E> where F: FnOnce() -> Result, @@ -241,7 +241,7 @@ impl OnceCell { /// cell.set("hello".to_string()).unwrap(); /// assert_eq!(cell.into_inner(), Some("hello".to_string())); /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn into_inner(self) -> Option { // Because `into_inner` takes `self` by value, the compiler statically verifies // that it is not currently borrowed. So it is safe to move out `Option`. @@ -269,7 +269,7 @@ impl OnceCell { /// assert_eq!(cell.take(), Some("hello".to_string())); /// assert_eq!(cell.get(), None); /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn take(&mut self) -> Option { mem::take(self).into_inner() } @@ -298,13 +298,13 @@ impl OnceCell { /// // 92 /// // 92 /// ``` -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] pub struct Lazy T> { cell: OnceCell, init: Cell>, } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl fmt::Debug for Lazy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() @@ -329,7 +329,7 @@ impl Lazy { /// assert_eq!(&*lazy, "HELLO, WORLD!"); /// # } /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub const fn new(init: F) -> Lazy { Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) } } @@ -353,7 +353,7 @@ impl T> Lazy { /// assert_eq!(Lazy::force(&lazy), &92); /// assert_eq!(&*lazy, &92); /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn force(this: &Lazy) -> &T { this.cell.get_or_init(|| match this.init.take() { Some(f) => f(), @@ -362,7 +362,7 @@ impl T> Lazy { } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl T> Deref for Lazy { type Target = T; fn deref(&self) -> &T { @@ -370,7 +370,7 @@ impl T> Deref for Lazy { } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl Default for Lazy { /// Creates a new lazy value using `Default` as the initializing function. fn default() -> Lazy { diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 1e6a27cff0ce1..7bd530c5fb10a 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -239,7 +239,7 @@ pub mod char; pub mod ffi; #[cfg(not(test))] // See #65860 pub mod iter; -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] pub mod lazy; pub mod option; pub mod panic; diff --git a/src/libstd/lazy.rs b/src/libstd/lazy.rs index 094eff17f8916..86e1cfae582e8 100644 --- a/src/libstd/lazy.rs +++ b/src/libstd/lazy.rs @@ -10,7 +10,7 @@ use crate::{ }; #[doc(inline)] -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] pub use core::lazy::*; /// A synchronization primitive which can be written to only once. @@ -38,7 +38,7 @@ pub use core::lazy::*; /// assert!(value.is_some()); /// assert_eq!(value.unwrap().as_str(), "Hello, World!"); /// ``` -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] pub struct SyncOnceCell { once: Once, // Whether or not the value is initialized is tracked by `state_and_queue`. @@ -50,24 +50,24 @@ pub struct SyncOnceCell { // scoped thread B, which fills the cell, which is // then destroyed by A. That is, destructor observes // a sent value. -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] unsafe impl Sync for SyncOnceCell {} -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] unsafe impl Send for SyncOnceCell {} -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl RefUnwindSafe for SyncOnceCell {} -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl UnwindSafe for SyncOnceCell {} -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl Default for SyncOnceCell { fn default() -> SyncOnceCell { SyncOnceCell::new() } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl fmt::Debug for SyncOnceCell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.get() { @@ -77,7 +77,7 @@ impl fmt::Debug for SyncOnceCell { } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl Clone for SyncOnceCell { fn clone(&self) -> SyncOnceCell { let cell = Self::new(); @@ -91,7 +91,7 @@ impl Clone for SyncOnceCell { } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl From for SyncOnceCell { fn from(value: T) -> Self { let cell = Self::new(); @@ -102,19 +102,19 @@ impl From for SyncOnceCell { } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl PartialEq for SyncOnceCell { fn eq(&self, other: &SyncOnceCell) -> bool { self.get() == other.get() } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl Eq for SyncOnceCell {} impl SyncOnceCell { /// Creates a new empty cell. - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub const fn new() -> SyncOnceCell { SyncOnceCell { once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()) } } @@ -123,7 +123,7 @@ impl SyncOnceCell { /// /// Returns `None` if the cell is empty, or being initialized. This /// method never blocks. - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn get(&self) -> Option<&T> { if self.is_initialized() { // Safe b/c checked is_initialized @@ -136,7 +136,7 @@ impl SyncOnceCell { /// Gets the mutable reference to the underlying value. /// /// Returns `None` if the cell is empty. This method never blocks. - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn get_mut(&mut self) -> Option<&mut T> { if self.is_initialized() { // Safe b/c checked is_initialized and we have a unique access @@ -170,7 +170,7 @@ impl SyncOnceCell { /// assert_eq!(CELL.get(), Some(&92)); /// } /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn set(&self, value: T) -> Result<(), T> { let mut value = Some(value); self.get_or_init(|| value.take().unwrap()); @@ -209,7 +209,7 @@ impl SyncOnceCell { /// let value = cell.get_or_init(|| unreachable!()); /// assert_eq!(value, &92); /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn get_or_init(&self, f: F) -> &T where F: FnOnce() -> T, @@ -248,7 +248,7 @@ impl SyncOnceCell { /// assert_eq!(value, Ok(&92)); /// assert_eq!(cell.get(), Some(&92)) /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn get_or_try_init(&self, f: F) -> Result<&T, E> where F: FnOnce() -> Result, @@ -286,7 +286,7 @@ impl SyncOnceCell { /// cell.set("hello".to_string()).unwrap(); /// assert_eq!(cell.into_inner(), Some("hello".to_string())); /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn into_inner(mut self) -> Option { // Safety: Safe because we immediately free `self` without dropping let inner = unsafe { self.take_inner() }; @@ -318,7 +318,7 @@ impl SyncOnceCell { /// assert_eq!(cell.take(), Some("hello".to_string())); /// assert_eq!(cell.get(), None); /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn take(&mut self) -> Option { mem::take(self).into_inner() } @@ -428,13 +428,13 @@ impl Drop for SyncOnceCell { /// // Some("Hoyten") /// } /// ``` -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] pub struct SyncLazy T> { cell: SyncOnceCell, init: Cell>, } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl fmt::Debug for SyncLazy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() @@ -446,17 +446,17 @@ impl fmt::Debug for SyncLazy { // we do create a `&mut Option` in `force`, but this is // properly synchronized, so it only happens once // so it also does not contribute to this impl. -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] unsafe impl Sync for SyncLazy where SyncOnceCell: Sync {} // auto-derived `Send` impl is OK. -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl RefUnwindSafe for SyncLazy where SyncOnceCell: RefUnwindSafe {} impl SyncLazy { /// Creates a new lazy value with the given initializing /// function. - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub const fn new(f: F) -> SyncLazy { SyncLazy { cell: SyncOnceCell::new(), init: Cell::new(Some(f)) } } @@ -479,7 +479,7 @@ impl T> SyncLazy { /// assert_eq!(SyncLazy::force(&lazy), &92); /// assert_eq!(&*lazy, &92); /// ``` - #[unstable(feature = "once_cell", issue = "68198")] + #[unstable(feature = "once_cell", issue = "74465")] pub fn force(this: &SyncLazy) -> &T { this.cell.get_or_init(|| match this.init.take() { Some(f) => f(), @@ -488,7 +488,7 @@ impl T> SyncLazy { } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl T> Deref for SyncLazy { type Target = T; fn deref(&self) -> &T { @@ -496,7 +496,7 @@ impl T> Deref for SyncLazy { } } -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] impl Default for SyncLazy { /// Creates a new lazy value using `Default` as the initializing function. fn default() -> SyncLazy { diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 8178719ccc40a..cf9cc4ebc7bd2 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -478,7 +478,7 @@ pub mod process; pub mod sync; pub mod time; -#[unstable(feature = "once_cell", issue = "68198")] +#[unstable(feature = "once_cell", issue = "74465")] pub mod lazy; #[stable(feature = "futures_api", since = "1.36.0")] From fa885e847b91bfaa6f17fb02b93ee77d2d8ae992 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Fri, 17 Jul 2020 17:39:53 -0700 Subject: [PATCH 31/31] Run fmt --- src/librustc_codegen_ssa/back/rpath.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_codegen_ssa/back/rpath.rs b/src/librustc_codegen_ssa/back/rpath.rs index e1d649c6ed3de..005d2efdd3b26 100644 --- a/src/librustc_codegen_ssa/back/rpath.rs +++ b/src/librustc_codegen_ssa/back/rpath.rs @@ -1,8 +1,8 @@ +use pathdiff::diff_paths; use rustc_data_structures::fx::FxHashSet; use std::env; use std::fs; use std::path::{Path, PathBuf}; -use pathdiff::diff_paths; use rustc_hir::def_id::CrateNum; use rustc_middle::middle::cstore::LibSource;