Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added feature const_new which enables SmallVec::new_const() #265

Merged
merged 4 commits into from
Sep 28, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: const_new feature allows const init of items + docs
TimLuq committed Sep 25, 2021
commit 11e34ee26556547130d8d354fe1e0ae1ce136d76
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"]
79 changes: 74 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<A: Array> {

#[cfg(all(feature = "union", feature = "const_new"))]
impl<A: Array> SmallVecData<A> {
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
#[inline]
const fn from_const(inline: MaybeUninit<A>) -> SmallVecData<A> {
SmallVecData {
@@ -416,6 +465,7 @@ enum SmallVecData<A: Array> {

#[cfg(all(not(feature = "union"), feature = "const_new"))]
impl<A: Array> SmallVecData<A> {
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
#[inline]
const fn from_const(inline: MaybeUninit<A>) -> SmallVecData<A> {
SmallVecData::Inline(inline)
@@ -1360,7 +1410,7 @@ impl<A: Array> SmallVec<A> {

#[cfg(feature = "const_new")]
impl<A: Array> SmallVec<A> {
/// 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<A: Array> SmallVec<A> {
///
/// [`Array`]: crate::Array
/// [`new`]: crate::SmallVec::new
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
#[inline]
pub const unsafe fn new_const() -> SmallVec<A> {
SmallVec {
@@ -1536,6 +1587,7 @@ impl<A: Array> BorrowMut<[A::Item]> for SmallVec<A> {
}

#[cfg(feature = "write")]
#[cfg_attr(docsrs, doc(cfg(feature = "write")))]
impl<A: Array<Item = u8>> io::Write for SmallVec<A> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
@@ -1556,6 +1608,7 @@ impl<A: Array<Item = u8>> io::Write for SmallVec<A> {
}

#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<A: Array> Serialize for SmallVec<A>
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<A>
where
A::Item: Deserialize<'de>,
@@ -1999,15 +2053,30 @@ impl<'a> Drop for SetLenOnDrop<'a> {
}
}

#[cfg(feature = "const_generics")]
#[cfg(feature = "const_new")]
impl<T, const N: usize> 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<T, const N: usize> Array for [T; N] {
type Item = T;
fn size() -> usize {
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,
21 changes: 20 additions & 1 deletion src/tests.rs
Original file line number Diff line number Diff line change
@@ -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() {