-
-
Save Mark-Simulacrum/6893dff239d116947bca647f4f8128c7 to your computer and use it in GitHub Desktop.
git diff d3df8512 cfade738
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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