git diff d3df8512 cfade738
diff --git a/src/libcore/lazy.rs b/src/libcore/lazy.rs | |
new file mode 100644 | |
index 00000000000..5cf7217ef11 | |
--- /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 = "74465")] | |
+pub struct OnceCell<T> { | |
+ // Invariant: written to at most once. | |
+ inner: UnsafeCell<Option<T>>, | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T> Default for OnceCell<T> { | |
+ fn default() -> Self { | |
+ Self::new() | |
+ } | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: fmt::Debug> fmt::Debug for OnceCell<T> { | |
+ 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 = "74465")] | |
+impl<T: Clone> Clone for OnceCell<T> { | |
+ fn clone(&self) -> OnceCell<T> { | |
+ let res = OnceCell::new(); | |
+ if let Some(value) = self.get() { | |
+ match res.set(value.clone()) { | |
+ Ok(()) => (), | |
+ Err(_) => unreachable!(), | |
+ } | |
+ } | |
+ res | |
+ } | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: PartialEq> PartialEq for OnceCell<T> { | |
+ fn eq(&self, other: &Self) -> bool { | |
+ self.get() == other.get() | |
+ } | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: Eq> Eq for OnceCell<T> {} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T> From<T> for OnceCell<T> { | |
+ fn from(value: T) -> Self { | |
+ OnceCell { inner: UnsafeCell::new(Some(value)) } | |
+ } | |
+} | |
+ | |
+impl<T> OnceCell<T> { | |
+ /// Creates a new empty cell. | |
+ #[unstable(feature = "once_cell", issue = "74465")] | |
+ pub const fn new() -> OnceCell<T> { | |
+ OnceCell { inner: UnsafeCell::new(None) } | |
+ } | |
+ | |
+ /// Gets the reference to the underlying value. | |
+ /// | |
+ /// Returns `None` if the cell is empty. | |
+ #[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() | |
+ } | |
+ | |
+ /// Gets the mutable reference to the underlying value. | |
+ /// | |
+ /// Returns `None` if the cell is empty. | |
+ #[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() | |
+ } | |
+ | |
+ /// 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 = "74465")] | |
+ 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 = "74465")] | |
+ pub fn get_or_init<F>(&self, f: F) -> &T | |
+ where | |
+ F: FnOnce() -> T, | |
+ { | |
+ match self.get_or_try_init(|| Ok::<T, !>(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<i32, ()> { | |
+ /// Ok(92) | |
+ /// }); | |
+ /// assert_eq!(value, Ok(&92)); | |
+ /// assert_eq!(cell.get(), Some(&92)) | |
+ /// ``` | |
+ #[unstable(feature = "once_cell", issue = "74465")] | |
+ pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E> | |
+ where | |
+ F: FnOnce() -> Result<T, E>, | |
+ { | |
+ 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<String> = 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 = "74465")] | |
+ pub fn into_inner(self) -> Option<T> { | |
+ // 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<T>`. | |
+ 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<String> = 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 = "74465")] | |
+ pub fn take(&mut self) -> Option<T> { | |
+ 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<i32> = Lazy::new(|| { | |
+/// println!("initializing"); | |
+/// 92 | |
+/// }); | |
+/// println!("ready"); | |
+/// println!("{}", *lazy); | |
+/// println!("{}", *lazy); | |
+/// | |
+/// // Prints: | |
+/// // ready | |
+/// // initializing | |
+/// // 92 | |
+/// // 92 | |
+/// ``` | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+pub struct Lazy<T, F = fn() -> T> { | |
+ cell: OnceCell<T>, | |
+ init: Cell<Option<F>>, | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: fmt::Debug, F> fmt::Debug for Lazy<T, F> { | |
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
+ f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() | |
+ } | |
+} | |
+ | |
+impl<T, F> Lazy<T, F> { | |
+ /// 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 = "74465")] | |
+ pub const fn new(init: F) -> Lazy<T, F> { | |
+ Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) } | |
+ } | |
+} | |
+ | |
+impl<T, F: FnOnce() -> T> Lazy<T, F> { | |
+ /// 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 = "74465")] | |
+ pub fn force(this: &Lazy<T, F>) -> &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 = "74465")] | |
+impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> { | |
+ type Target = T; | |
+ fn deref(&self) -> &T { | |
+ Lazy::force(self) | |
+ } | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: Default> Default for Lazy<T> { | |
+ /// Creates a new lazy value using `Default` as the initializing function. | |
+ fn default() -> Lazy<T> { | |
+ Lazy::new(T::default) | |
+ } | |
+} | |
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs | |
index 2f2206a117c..1621cf79751 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 = "74465")] | |
+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 00000000000..1c0bddb9aef | |
--- /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<T>` --+ | |
+ let _ = x.set(27); // <-- temporary (unique) borrow of inner `Option<T>` | | |
+ println!("{}", at_x); // <------- up until here ---------------------------+ | |
+} | |
+ | |
+#[test] | |
+#[should_panic(expected = "reentrant init")] | |
+fn reentrant_init() { | |
+ let x: OnceCell<Box<i32>> = OnceCell::new(); | |
+ let dangling_ref: Cell<Option<&i32>> = 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 090ce471745..47ed6db6c67 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/librustc_middle/lib.rs b/src/librustc_middle/lib.rs | |
index b7dccb8d8ce..a68301385b7 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 82daae7d921..8ae9269a6bf 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,23 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { | |
abi, | |
align, | |
size, | |
- }) | |
+ }; | |
+ | |
+ 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/libstd/lazy.rs b/src/libstd/lazy.rs | |
new file mode 100644 | |
index 00000000000..86e1cfae582 | |
--- /dev/null | |
+++ b/src/libstd/lazy.rs | |
@@ -0,0 +1,844 @@ | |
+//! Lazy values and one-time initialization of static data. | |
+ | |
+use crate::{ | |
+ cell::{Cell, UnsafeCell}, | |
+ fmt, | |
+ mem::{self, MaybeUninit}, | |
+ ops::{Deref, Drop}, | |
+ panic::{RefUnwindSafe, UnwindSafe}, | |
+ sync::Once, | |
+}; | |
+ | |
+#[doc(inline)] | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+pub use core::lazy::*; | |
+ | |
+/// A synchronization primitive which can be written to only once. | |
+/// | |
+/// This type is a thread-safe `OnceCell`. | |
+/// | |
+/// # Examples | |
+/// | |
+/// ``` | |
+/// #![feature(once_cell)] | |
+/// | |
+/// use std::lazy::SyncOnceCell; | |
+/// | |
+/// static CELL: SyncOnceCell<String> = SyncOnceCell::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!"); | |
+/// ``` | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+pub struct SyncOnceCell<T> { | |
+ once: Once, | |
+ // Whether or not the value is initialized is tracked by `state_and_queue`. | |
+ value: UnsafeCell<MaybeUninit<T>>, | |
+} | |
+ | |
+// Why do we need `T: Send`? | |
+// 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 = "74465")] | |
+unsafe impl<T: Sync + Send> Sync for SyncOnceCell<T> {} | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+unsafe impl<T: Send> Send for SyncOnceCell<T> {} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SyncOnceCell<T> {} | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: UnwindSafe> UnwindSafe for SyncOnceCell<T> {} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T> Default for SyncOnceCell<T> { | |
+ fn default() -> SyncOnceCell<T> { | |
+ SyncOnceCell::new() | |
+ } | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: fmt::Debug> fmt::Debug for SyncOnceCell<T> { | |
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
+ match self.get() { | |
+ Some(v) => f.debug_tuple("Once").field(v).finish(), | |
+ None => f.write_str("Once(Uninit)"), | |
+ } | |
+ } | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: Clone> Clone for SyncOnceCell<T> { | |
+ fn clone(&self) -> SyncOnceCell<T> { | |
+ let cell = Self::new(); | |
+ if let Some(value) = self.get() { | |
+ match cell.set(value.clone()) { | |
+ Ok(()) => (), | |
+ Err(_) => unreachable!(), | |
+ } | |
+ } | |
+ cell | |
+ } | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T> From<T> for SyncOnceCell<T> { | |
+ fn from(value: T) -> Self { | |
+ let cell = Self::new(); | |
+ match cell.set(value) { | |
+ Ok(()) => cell, | |
+ Err(_) => unreachable!(), | |
+ } | |
+ } | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: PartialEq> PartialEq for SyncOnceCell<T> { | |
+ fn eq(&self, other: &SyncOnceCell<T>) -> bool { | |
+ self.get() == other.get() | |
+ } | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: Eq> Eq for SyncOnceCell<T> {} | |
+ | |
+impl<T> SyncOnceCell<T> { | |
+ /// Creates a new empty cell. | |
+ #[unstable(feature = "once_cell", issue = "74465")] | |
+ pub const fn new() -> SyncOnceCell<T> { | |
+ SyncOnceCell { once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()) } | |
+ } | |
+ | |
+ /// Gets the reference to the underlying value. | |
+ /// | |
+ /// Returns `None` if the cell is empty, or being initialized. This | |
+ /// method never blocks. | |
+ #[unstable(feature = "once_cell", issue = "74465")] | |
+ pub fn get(&self) -> Option<&T> { | |
+ if self.is_initialized() { | |
+ // Safe b/c checked is_initialized | |
+ Some(unsafe { self.get_unchecked() }) | |
+ } else { | |
+ None | |
+ } | |
+ } | |
+ | |
+ /// Gets the mutable reference to the underlying value. | |
+ /// | |
+ /// Returns `None` if the cell is empty. This method never blocks. | |
+ #[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 | |
+ Some(unsafe { self.get_unchecked_mut() }) | |
+ } else { | |
+ None | |
+ } | |
+ } | |
+ | |
+ /// Sets the contents of this cell to `value`. | |
+ /// | |
+ /// Returns `Ok(())` if the cell's value was updated. | |
+ /// | |
+ /// # Examples | |
+ /// | |
+ /// ``` | |
+ /// #![feature(once_cell)] | |
+ /// | |
+ /// use std::lazy::SyncOnceCell; | |
+ /// | |
+ /// static CELL: SyncOnceCell<i32> = 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)); | |
+ /// } | |
+ /// ``` | |
+ #[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()); | |
+ 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. | |
+ /// | |
+ /// # Examples | |
+ /// | |
+ /// ``` | |
+ /// #![feature(once_cell)] | |
+ /// | |
+ /// 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); | |
+ /// ``` | |
+ #[unstable(feature = "once_cell", issue = "74465")] | |
+ pub fn get_or_init<F>(&self, f: F) -> &T | |
+ where | |
+ F: FnOnce() -> T, | |
+ { | |
+ match self.get_or_try_init(|| Ok::<T, !>(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. | |
+ /// | |
+ /// # Examples | |
+ /// | |
+ /// ``` | |
+ /// #![feature(once_cell)] | |
+ /// | |
+ /// 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<i32, ()> { | |
+ /// Ok(92) | |
+ /// }); | |
+ /// assert_eq!(value, Ok(&92)); | |
+ /// assert_eq!(cell.get(), Some(&92)) | |
+ /// ``` | |
+ #[unstable(feature = "once_cell", issue = "74465")] | |
+ pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E> | |
+ where | |
+ F: FnOnce() -> Result<T, E>, | |
+ { | |
+ // Fast path check | |
+ // 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); | |
+ } | |
+ self.initialize(f)?; | |
+ | |
+ debug_assert!(self.is_initialized()); | |
+ | |
+ // Safety: The inner value has been initialized | |
+ Ok(unsafe { self.get_unchecked() }) | |
+ } | |
+ | |
+ /// Consumes the `SyncOnceCell`, returning the wrapped value. Returns | |
+ /// `None` if the cell was empty. | |
+ /// | |
+ /// # Examples | |
+ /// | |
+ /// ``` | |
+ /// #![feature(once_cell)] | |
+ /// | |
+ /// use std::lazy::SyncOnceCell; | |
+ /// | |
+ /// let cell: SyncOnceCell<String> = 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())); | |
+ /// ``` | |
+ #[unstable(feature = "once_cell", issue = "74465")] | |
+ pub fn into_inner(mut self) -> Option<T> { | |
+ // 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<String> = 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 = "74465")] | |
+ pub fn take(&mut self) -> Option<T> { | |
+ 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<T> { | |
+ // 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 | |
+ } | |
+ } | |
+ | |
+ #[inline] | |
+ fn is_initialized(&self) -> bool { | |
+ self.once.is_completed() | |
+ } | |
+ | |
+ #[cold] | |
+ fn initialize<F, E>(&self, f: F) -> Result<(), E> | |
+ where | |
+ F: FnOnce() -> Result<T, E>, | |
+ { | |
+ let mut res: Result<(), E> = Ok(()); | |
+ let slot = &self.value; | |
+ | |
+ // 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) }; | |
+ } | |
+ Err(e) => { | |
+ res = Err(e); | |
+ | |
+ // Treat the underlying `Once` as poisoned since we | |
+ // failed to initialize our value. Calls | |
+ p.poison(); | |
+ } | |
+ } | |
+ }); | |
+ 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<T> Drop for SyncOnceCell<T> { | |
+ fn drop(&mut self) { | |
+ // Safety: The cell is being dropped, so it can't be accessed again | |
+ unsafe { self.take_inner() }; | |
+ } | |
+} | |
+ | |
+/// A value which is initialized on the first access. | |
+/// | |
+/// This type is a thread-safe `Lazy`, and can be used in statics. | |
+/// | |
+/// # Examples | |
+/// | |
+/// ``` | |
+/// #![feature(once_cell)] | |
+/// | |
+/// use std::collections::HashMap; | |
+/// | |
+/// use std::lazy::SyncLazy; | |
+/// | |
+/// static HASHMAP: SyncLazy<HashMap<i32, String>> = SyncLazy::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") | |
+/// } | |
+/// ``` | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+pub struct SyncLazy<T, F = fn() -> T> { | |
+ cell: SyncOnceCell<T>, | |
+ init: Cell<Option<F>>, | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: fmt::Debug, F> fmt::Debug for SyncLazy<T, F> { | |
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
+ f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() | |
+ } | |
+} | |
+ | |
+// We never create a `&F` from a `&SyncLazy<T, F>` so it is fine | |
+// to not impl `Sync` for `F` | |
+// we do create a `&mut Option<F>` 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 = "74465")] | |
+unsafe impl<T, F: Send> Sync for SyncLazy<T, F> where SyncOnceCell<T>: Sync {} | |
+// auto-derived `Send` impl is OK. | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T, F: RefUnwindSafe> RefUnwindSafe for SyncLazy<T, F> where SyncOnceCell<T>: RefUnwindSafe {} | |
+ | |
+impl<T, F> SyncLazy<T, F> { | |
+ /// Creates a new lazy value with the given initializing | |
+ /// function. | |
+ #[unstable(feature = "once_cell", issue = "74465")] | |
+ pub const fn new(f: F) -> SyncLazy<T, F> { | |
+ SyncLazy { cell: SyncOnceCell::new(), init: Cell::new(Some(f)) } | |
+ } | |
+} | |
+ | |
+impl<T, F: FnOnce() -> T> SyncLazy<T, F> { | |
+ /// Forces the evaluation of this lazy value and | |
+ /// returns a reference to result. This is equivalent | |
+ /// to the `Deref` impl, but is explicit. | |
+ /// | |
+ /// # Examples | |
+ /// | |
+ /// ``` | |
+ /// #![feature(once_cell)] | |
+ /// | |
+ /// use std::lazy::SyncLazy; | |
+ /// | |
+ /// let lazy = SyncLazy::new(|| 92); | |
+ /// | |
+ /// assert_eq!(SyncLazy::force(&lazy), &92); | |
+ /// assert_eq!(&*lazy, &92); | |
+ /// ``` | |
+ #[unstable(feature = "once_cell", issue = "74465")] | |
+ pub fn force(this: &SyncLazy<T, F>) -> &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 = "74465")] | |
+impl<T, F: FnOnce() -> T> Deref for SyncLazy<T, F> { | |
+ type Target = T; | |
+ fn deref(&self) -> &T { | |
+ SyncLazy::force(self) | |
+ } | |
+} | |
+ | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+impl<T: Default> Default for SyncLazy<T> { | |
+ /// Creates a new lazy value using `Default` as the initializing function. | |
+ fn default() -> SyncLazy<T> { | |
+ 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<Mutex<Foo>> = <_>::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<String> = 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<R: Send + 'static>(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<R: Send + 'static>(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<i32> = 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::<String>::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<String> = 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.is_initialized()); | |
+ 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::<String>::new() == SyncOnceCell::new()); | |
+ assert!(SyncOnceCell::<String>::new() != SyncOnceCell::from("value".to_owned())); | |
+ } | |
+ | |
+ #[test] | |
+ fn into_inner() { | |
+ let cell: SyncOnceCell<String> = 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<i32> = 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<Mutex<Foo>> = <_>::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<Vec<i32>> = 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<i32> { | |
+ static XS: SyncOnceCell<Vec<i32>> = 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<String> = 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<T: Send + Sync>() {} | |
+ assert_traits::<SyncOnceCell<String>>(); | |
+ assert_traits::<SyncLazy<String>>(); | |
+ } | |
+ | |
+ #[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<i32> = eval_once! { | |
+ || -> Vec<i32> { | |
+ 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<String> = 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 54fc35b20f7..be8d9f7a7c9 100644 | |
--- a/src/libstd/lib.rs | |
+++ b/src/libstd/lib.rs | |
@@ -287,6 +287,7 @@ | |
#![feature(linkage)] | |
#![feature(llvm_asm)] | |
#![feature(log_syntax)] | |
+#![feature(maybe_uninit_extra)] | |
#![feature(maybe_uninit_ref)] | |
#![feature(maybe_uninit_slice)] | |
#![feature(min_specialization)] | |
@@ -294,6 +295,7 @@ | |
#![feature(negative_impls)] | |
#![feature(never_type)] | |
#![feature(nll)] | |
+#![feature(once_cell)] | |
#![feature(optin_builtin_traits)] | |
#![feature(or_patterns)] | |
#![feature(panic_info_message)] | |
@@ -477,6 +479,9 @@ pub mod process; | |
pub mod sync; | |
pub mod time; | |
+#[unstable(feature = "once_cell", issue = "74465")] | |
+pub mod lazy; | |
+ | |
#[stable(feature = "futures_api", since = "1.36.0")] | |
pub mod task { | |
//! Types and Traits for working with asynchronous tasks. | |
diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs | |
index 7dc822db3d0..64260990824 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<usize>, | |
} | |
/// 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,12 @@ 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 +559,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")))] | |
diff --git a/src/test/ui/print_type_sizes/niche-filling.stdout b/src/test/ui/print_type_sizes/niche-filling.stdout | |
index 301edc0d086..1894cd218ee 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<Union1<std::num::NonZeroU32>>`: 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 6a3f3c98f12..73a11a5e743 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,23 @@ enum Option2<A, B> { | |
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<Option<CanBeNicheFilledButShouldnt>>` to fit into 8 bytes. | |
+pub enum CanBeNicheFilledButShouldnt { | |
+ A(NonZeroU8, u32), | |
+ B | |
+} | |
+pub enum AlwaysTaggedBecauseItHasNoNiche { | |
+ A(u8, u32), | |
+ B | |
+} | |
+ | |
pub fn main() { | |
assert_eq!(size_of::<u8>(), 1 as usize); | |
assert_eq!(size_of::<u32>(), 4 as usize); | |
@@ -145,4 +163,11 @@ pub fn main() { | |
assert_eq!(size_of::<Option<Option<(&(), bool)>>>(), size_of::<(bool, &())>()); | |
assert_eq!(size_of::<Option<Option2<bool, &()>>>(), size_of::<(bool, &())>()); | |
assert_eq!(size_of::<Option<Option2<&(), bool>>>(), size_of::<(bool, &())>()); | |
+ | |
+ assert_eq!(size_of::<CanBeNicheFilledButShouldnt>(), 8); | |
+ assert_eq!(size_of::<Option<CanBeNicheFilledButShouldnt>>(), 8); | |
+ assert_eq!(size_of::<Option<Option<CanBeNicheFilledButShouldnt>>>(), 8); | |
+ assert_eq!(size_of::<AlwaysTaggedBecauseItHasNoNiche>(), 8); | |
+ assert_eq!(size_of::<Option<AlwaysTaggedBecauseItHasNoNiche>>(), 8); | |
+ assert_eq!(size_of::<Option<Option<AlwaysTaggedBecauseItHasNoNiche>>>(), 8); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment