From 7bf129139e1d2a0bd1fb983ab65cefe2da164bbd Mon Sep 17 00:00:00 2001 From: TimLuq Date: Fri, 24 Sep 2021 04:20:39 +0200 Subject: [PATCH 1/4] feat: servo/rust-smallvec#263 added feature `const_new` --- Cargo.toml | 1 + src/lib.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/tests.rs | 10 ++++++++++ 3 files changed, 60 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index b19b93e..ff39340 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ documentation = "https://docs.rs/smallvec/" [features] const_generics = [] +const_new = [] write = [] union = [] specialization = [] diff --git a/src/lib.rs b/src/lib.rs index 2e22a18..8cbc180 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,11 +67,22 @@ //! [Rustonomicon](https://doc.rust-lang.org/1.42.0/nomicon/dropck.html#an-escape-hatch). //! //! Tracking issue: [rust-lang/rust#34761](https://github.com/rust-lang/rust/issues/34761) +//! +//! ### `const_new` +//! +//! **This feature is unstable and requires a nightly build of the Rust toolchain.** +//! +//! This feature exposes the function [`SmallVec::new_const`] which is a `const fn` so the `SmallVec` may be used from a const context. +//! For details, see the +//! [Rust Reference](https://doc.rust-lang.org/reference/const_eval.html#const-functions). +//! +//! Tracking issue: [rust-lang/rust#57563](https://github.com/rust-lang/rust/issues/57563) #![no_std] #![cfg_attr(feature = "specialization", allow(incomplete_features))] #![cfg_attr(feature = "specialization", feature(specialization))] #![cfg_attr(feature = "may_dangle", feature(dropck_eyepatch))] +#![cfg_attr(feature = "const_new", feature(const_fn_trait_bound))] #![deny(missing_docs)] #[doc(hidden)] @@ -353,6 +364,16 @@ union SmallVecData { heap: (*mut A::Item, usize), } +#[cfg(all(feature = "union", feature = "const_new"))] +impl SmallVecData { + #[inline] + const fn from_const(inline: MaybeUninit) -> SmallVecData { + SmallVecData { + inline: core::mem::ManuallyDrop::new(inline), + } + } +} + #[cfg(feature = "union")] impl SmallVecData { #[inline] @@ -393,6 +414,14 @@ enum SmallVecData { Heap((*mut A::Item, usize)), } +#[cfg(all(not(feature = "union"), feature = "const_new"))] +impl SmallVecData { + #[inline] + const fn from_const(inline: MaybeUninit) -> SmallVecData { + SmallVecData::Inline(inline) + } +} + #[cfg(not(feature = "union"))] impl SmallVecData { #[inline] @@ -1329,6 +1358,26 @@ impl SmallVec { } } +#[cfg(feature = "const_new")] +impl SmallVec { + /// Construct an empty vector. + /// + /// # Safety + /// No size validation is attempted for this function. + /// Invalid custom implementations of [`Array`] normally panics during [`new`]. + /// `new_const` will still initialize which may cause undefined behavior (such as segmentation errors) when used with invalid implementations. + /// + /// [`Array`]: crate::Array + /// [`new`]: crate::SmallVec::new + #[inline] + pub const unsafe fn new_const() -> SmallVec { + SmallVec { + capacity: 0, + data: SmallVecData::from_const(MaybeUninit::uninit()), + } + } +} + impl SmallVec where A::Item: Copy, diff --git a/src/tests.rs b/src/tests.rs index f30dfa2..a7c3fa6 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -903,6 +903,16 @@ fn const_generics() { let _v = SmallVec::<[i32; 987]>::default(); } +#[cfg(feature = "const_new")] +#[test] +fn const_new() { + let _v = const_new_inner(); +} +#[cfg(feature = "const_new")] +const fn const_new_inner() -> SmallVec<[i32; 4]> { + unsafe { SmallVec::<[i32; 4]>::new_const() } +} + #[test] fn empty_macro() { let _v: SmallVec<[u8; 1]> = smallvec![]; From 11e34ee26556547130d8d354fe1e0ae1ce136d76 Mon Sep 17 00:00:00 2001 From: TimLuq Date: Sun, 26 Sep 2021 00:15:09 +0200 Subject: [PATCH 2/4] feat: `const_new` feature allows const init of items + docs --- Cargo.toml | 6 +++- src/lib.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++---- src/tests.rs | 21 +++++++++++++- 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ff39340..93f0aa3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/smallvec/" [features] const_generics = [] -const_new = [] +const_new = ["const_generics"] write = [] union = [] specialization = [] @@ -24,3 +24,7 @@ serde = { version = "1", optional = true, default-features = false } [dev_dependencies] bincode = "1.0.1" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/src/lib.rs b/src/lib.rs index 8cbc180..1295896 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,13 +72,14 @@ //! //! **This feature is unstable and requires a nightly build of the Rust toolchain.** //! -//! This feature exposes the function [`SmallVec::new_const`] which is a `const fn` so the `SmallVec` may be used from a const context. +//! This feature exposes the functions [`SmallVec::new_const`] and [`SmallVec::from_const`] which enables the `SmallVec` to be initialized from a const context. //! For details, see the //! [Rust Reference](https://doc.rust-lang.org/reference/const_eval.html#const-functions). //! //! Tracking issue: [rust-lang/rust#57563](https://github.com/rust-lang/rust/issues/57563) #![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(feature = "specialization", allow(incomplete_features))] #![cfg_attr(feature = "specialization", feature(specialization))] #![cfg_attr(feature = "may_dangle", feature(dropck_eyepatch))] @@ -181,6 +182,53 @@ macro_rules! smallvec { }); } + +/// Creates an inline [`SmallVec`] containing the arguments. This macro is enabled by the feature `const_new`. +/// +/// `smallvec_inline!` allows `SmallVec`s to be defined with the same syntax as array expressions in `const` contexts. +/// The inline storage `A` will always be an array of the size specified by the arguments. +/// There are two forms of this macro: +/// +/// - Create a [`SmallVec`] containing a given list of elements: +/// +/// ``` +/// # #[macro_use] extern crate smallvec; +/// # use smallvec::SmallVec; +/// # fn main() { +/// const V: SmallVec<[i32; 3]> = smallvec_inline![1, 2, 3]; +/// assert_eq!(V[0], 1); +/// assert_eq!(V[1], 2); +/// assert_eq!(V[2], 3); +/// # } +/// ``` +/// +/// - Create a [`SmallVec`] from a given element and size: +/// +/// ``` +/// # #[macro_use] extern crate smallvec; +/// # use smallvec::SmallVec; +/// # fn main() { +/// const V: SmallVec<[i32; 3]> = smallvec_inline![1; 3]; +/// assert_eq!(V, SmallVec::from_buf([1, 1, 1])); +/// # } +/// ``` +/// +/// Note that the behavior mimics that of array expressions, in contrast to [`smallvec`]. +#[cfg(feature = "const_new")] +#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] +#[macro_export] +macro_rules! smallvec_inline { + // count helper: transform any expression into 1 + (@one $x:expr) => (1usize); + ($elem:expr; $n:expr) => ({ + $crate::SmallVec::<[_; $n]>::from_const([$elem; $n]) + }); + ($($x:expr),+ $(,)?) => ({ + const N: usize = 0usize $(+ $crate::smallvec_inline!(@one $x))*; + $crate::SmallVec::<[_; N]>::from_const([$($x,)*]) + }); +} + /// `panic!()` in debug builds, optimization hint in release. #[cfg(not(feature = "union"))] macro_rules! debug_unreachable { @@ -366,6 +414,7 @@ union SmallVecData { #[cfg(all(feature = "union", feature = "const_new"))] impl SmallVecData { + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline] const fn from_const(inline: MaybeUninit) -> SmallVecData { SmallVecData { @@ -416,6 +465,7 @@ enum SmallVecData { #[cfg(all(not(feature = "union"), feature = "const_new"))] impl SmallVecData { + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline] const fn from_const(inline: MaybeUninit) -> SmallVecData { SmallVecData::Inline(inline) @@ -1360,7 +1410,7 @@ impl SmallVec { #[cfg(feature = "const_new")] impl SmallVec { - /// Construct an empty vector. + /// Construct an empty vector. This is currently gated behind the feature `const_new`. /// /// # Safety /// No size validation is attempted for this function. @@ -1369,6 +1419,7 @@ impl SmallVec { /// /// [`Array`]: crate::Array /// [`new`]: crate::SmallVec::new + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline] pub const unsafe fn new_const() -> SmallVec { SmallVec { @@ -1536,6 +1587,7 @@ impl BorrowMut<[A::Item]> for SmallVec { } #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(feature = "write")))] impl> io::Write for SmallVec { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { @@ -1556,6 +1608,7 @@ impl> io::Write for SmallVec { } #[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl Serialize for SmallVec where A::Item: Serialize, @@ -1570,6 +1623,7 @@ where } #[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl<'de, A: Array> Deserialize<'de> for SmallVec where A::Item: Deserialize<'de>, @@ -1999,7 +2053,22 @@ impl<'a> Drop for SetLenOnDrop<'a> { } } -#[cfg(feature = "const_generics")] +#[cfg(feature = "const_new")] +impl SmallVec<[T; N]> { + /// The array passed as an argument is moved to be an inline version of `SmallVec`. + /// This is a `const` version of [`SmallVec::from_buf`] that is enabled by the feature `const_new`, with the limitation that it only works for arrays. + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] + #[inline] + pub const fn from_const(items: [T; N]) -> SmallVec<[T; N]> { + SmallVec { + capacity: N, + data: SmallVecData::from_const(MaybeUninit::new(items)), + } + } +} + +#[cfg(all(feature = "const_generics", not(doc)))] +#[cfg_attr(docsrs, doc(cfg(feature = "const_generics")))] unsafe impl Array for [T; N] { type Item = T; fn size() -> usize { @@ -2007,7 +2076,7 @@ unsafe impl Array for [T; N] { } } -#[cfg(not(feature = "const_generics"))] +#[cfg(any(not(feature = "const_generics"), doc))] macro_rules! impl_array( ($($size:expr),+) => { $( @@ -2019,7 +2088,7 @@ macro_rules! impl_array( } ); -#[cfg(not(feature = "const_generics"))] +#[cfg(any(not(feature = "const_generics"), doc))] impl_array!( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 36, 0x40, 0x60, 0x80, 0x100, 0x200, 0x400, 0x600, 0x800, 0x1000, diff --git a/src/tests.rs b/src/tests.rs index a7c3fa6..1c2c2e6 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -906,12 +906,31 @@ fn const_generics() { #[cfg(feature = "const_new")] #[test] fn const_new() { - let _v = const_new_inner(); + let v = const_new_inner(); + assert_eq!(v.capacity(), 4); + assert_eq!(v.len(), 0); + let v = const_new_inline_sized(); + assert_eq!(v.capacity(), 4); + assert_eq!(v.len(), 4); + assert_eq!(v[0], 1); + let v = const_new_inline_args(); + assert_eq!(v.capacity(), 2); + assert_eq!(v.len(), 2); + assert_eq!(v[0], 1); + assert_eq!(v[1], 4); } #[cfg(feature = "const_new")] const fn const_new_inner() -> SmallVec<[i32; 4]> { unsafe { SmallVec::<[i32; 4]>::new_const() } } +#[cfg(feature = "const_new")] +const fn const_new_inline_sized() -> SmallVec<[i32; 4]> { + crate::smallvec_inline![1; 4] +} +#[cfg(feature = "const_new")] +const fn const_new_inline_args() -> SmallVec<[i32; 2]> { + crate::smallvec_inline![1, 4] +} #[test] fn empty_macro() { From 8973eabe39e9907b37132506593242dd943b5069 Mon Sep 17 00:00:00 2001 From: TimLuq Date: Sun, 26 Sep 2021 12:18:21 +0200 Subject: [PATCH 3/4] limit feature `const_new` to arrays --- src/lib.rs | 63 ++++++++++++++++++++++------------------------------ src/tests.rs | 2 +- 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1295896..95538f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,14 @@ //! When this feature is enabled, `SmallVec` works with any arrays of any size, not just a fixed //! list of sizes. //! +//! ### `const_new` +//! +//! **This feature requires Rust 1.51.** +//! +//! This feature exposes the functions [`SmallVec::new_const`], [`SmallVec::from_const`], and [`smallvec_inline`] which enables the `SmallVec` to be initialized from a const context. +//! For details, see the +//! [Rust Reference](https://doc.rust-lang.org/reference/const_eval.html#const-functions). +//! //! ### `specialization` //! //! **This feature is unstable and requires a nightly build of the Rust toolchain.** @@ -67,23 +75,12 @@ //! [Rustonomicon](https://doc.rust-lang.org/1.42.0/nomicon/dropck.html#an-escape-hatch). //! //! Tracking issue: [rust-lang/rust#34761](https://github.com/rust-lang/rust/issues/34761) -//! -//! ### `const_new` -//! -//! **This feature is unstable and requires a nightly build of the Rust toolchain.** -//! -//! This feature exposes the functions [`SmallVec::new_const`] and [`SmallVec::from_const`] which enables the `SmallVec` to be initialized from a const context. -//! For details, see the -//! [Rust Reference](https://doc.rust-lang.org/reference/const_eval.html#const-functions). -//! -//! Tracking issue: [rust-lang/rust#57563](https://github.com/rust-lang/rust/issues/57563) #![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(feature = "specialization", allow(incomplete_features))] #![cfg_attr(feature = "specialization", feature(specialization))] #![cfg_attr(feature = "may_dangle", feature(dropck_eyepatch))] -#![cfg_attr(feature = "const_new", feature(const_fn_trait_bound))] #![deny(missing_docs)] #[doc(hidden)] @@ -413,10 +410,10 @@ union SmallVecData { } #[cfg(all(feature = "union", feature = "const_new"))] -impl SmallVecData { +impl SmallVecData<[T; N]> { #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline] - const fn from_const(inline: MaybeUninit) -> SmallVecData { + const fn from_const(inline: MaybeUninit<[T; N]>) -> Self { SmallVecData { inline: core::mem::ManuallyDrop::new(inline), } @@ -464,10 +461,10 @@ enum SmallVecData { } #[cfg(all(not(feature = "union"), feature = "const_new"))] -impl SmallVecData { +impl SmallVecData<[T; N]> { #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline] - const fn from_const(inline: MaybeUninit) -> SmallVecData { + const fn from_const(inline: MaybeUninit<[T; N]>) -> Self { SmallVecData::Inline(inline) } } @@ -1408,27 +1405,6 @@ impl SmallVec { } } -#[cfg(feature = "const_new")] -impl SmallVec { - /// Construct an empty vector. This is currently gated behind the feature `const_new`. - /// - /// # Safety - /// No size validation is attempted for this function. - /// Invalid custom implementations of [`Array`] normally panics during [`new`]. - /// `new_const` will still initialize which may cause undefined behavior (such as segmentation errors) when used with invalid implementations. - /// - /// [`Array`]: crate::Array - /// [`new`]: crate::SmallVec::new - #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] - #[inline] - pub const unsafe fn new_const() -> SmallVec { - SmallVec { - capacity: 0, - data: SmallVecData::from_const(MaybeUninit::uninit()), - } - } -} - impl SmallVec where A::Item: Copy, @@ -2055,11 +2031,24 @@ impl<'a> Drop for SetLenOnDrop<'a> { #[cfg(feature = "const_new")] impl SmallVec<[T; N]> { + /// Construct an empty vector. + /// + /// This is a `const` version of [`SmallVec::new`] that is enabled by the feature `const_new`, with the limitation that it only works for arrays. + #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] + #[inline] + pub const fn new_const() -> Self { + SmallVec { + capacity: 0, + data: SmallVecData::from_const(MaybeUninit::uninit()), + } + } + /// The array passed as an argument is moved to be an inline version of `SmallVec`. + /// /// This is a `const` version of [`SmallVec::from_buf`] that is enabled by the feature `const_new`, with the limitation that it only works for arrays. #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline] - pub const fn from_const(items: [T; N]) -> SmallVec<[T; N]> { + pub const fn from_const(items: [T; N]) -> Self { SmallVec { capacity: N, data: SmallVecData::from_const(MaybeUninit::new(items)), diff --git a/src/tests.rs b/src/tests.rs index 1c2c2e6..bc4f3f6 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -921,7 +921,7 @@ fn const_new() { } #[cfg(feature = "const_new")] const fn const_new_inner() -> SmallVec<[i32; 4]> { - unsafe { SmallVec::<[i32; 4]>::new_const() } + SmallVec::<[i32; 4]>::new_const() } #[cfg(feature = "const_new")] const fn const_new_inline_sized() -> SmallVec<[i32; 4]> { From 8320871d602b4d14bb0056143a8a476661cbdd5a Mon Sep 17 00:00:00 2001 From: TimLuq Date: Sun, 26 Sep 2021 12:19:10 +0200 Subject: [PATCH 4/4] rustfmt --- src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 95538f8..ba9cd78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,7 +179,6 @@ macro_rules! smallvec { }); } - /// Creates an inline [`SmallVec`] containing the arguments. This macro is enabled by the feature `const_new`. /// /// `smallvec_inline!` allows `SmallVec`s to be defined with the same syntax as array expressions in `const` contexts. @@ -2032,7 +2031,7 @@ impl<'a> Drop for SetLenOnDrop<'a> { #[cfg(feature = "const_new")] impl SmallVec<[T; N]> { /// Construct an empty vector. - /// + /// /// This is a `const` version of [`SmallVec::new`] that is enabled by the feature `const_new`, with the limitation that it only works for arrays. #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline] @@ -2044,7 +2043,7 @@ impl SmallVec<[T; N]> { } /// The array passed as an argument is moved to be an inline version of `SmallVec`. - /// + /// /// This is a `const` version of [`SmallVec::from_buf`] that is enabled by the feature `const_new`, with the limitation that it only works for arrays. #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))] #[inline]