diff --git a/Cargo.toml b/Cargo.toml index b3a4e142..3a424a83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,10 @@ name = "heapless" repository = "https://github.com/japaric/heapless" version = "0.3.6" +[features] +default = ["const-fn"] +const-fn = [] + [dev-dependencies] scoped_threadpool = "0.1.8" diff --git a/ci/script.sh b/ci/script.sh index 936d31b3..eafda3fd 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -5,14 +5,18 @@ main() { if [ $TARGET = x86_64-unknown-linux-gnu ]; then cargo test --target $TARGET + cargo test --target $TARGET --no-default-features cargo test --target $TARGET --release + cargo test --target $TARGET --release --no-default-features export RUSTFLAGS="-Z sanitizer=thread" export RUST_TEST_THREADS=1 export TSAN_OPTIONS="suppressions=$(pwd)/blacklist.txt" cargo test --test tsan --target $TARGET + cargo test --test tsan --target $TARGET --no-default-features cargo test --test tsan --target $TARGET --release + cargo test --test tsan --target $TARGET --release --no-default-features fi } diff --git a/src/__core.rs b/src/__core.rs index 671255f4..763d0500 100644 --- a/src/__core.rs +++ b/src/__core.rs @@ -12,9 +12,11 @@ pub mod mem { impl ManuallyDrop { #[inline] - pub const fn new(value: T) -> ManuallyDrop { - ManuallyDrop { value: value } - } + const_fn!( + pub const fn new(value: T) -> ManuallyDrop { + ManuallyDrop { value: value } + } + ); } impl Deref for ManuallyDrop { @@ -33,13 +35,42 @@ pub mod mem { } } - pub const unsafe fn uninitialized() -> T { - #[allow(unions_with_drop_fields)] - union U { - none: (), - some: T, - } + const_fn!( + pub const unsafe fn uninitialized() -> T { + #[allow(unions_with_drop_fields)] + union U { + none: (), + some: T, + } + + U { none: () }.some + } + ); +} + +#[cfg(feature = "const-fn")] // Remove this if there are more tests +#[cfg(test)] +mod test { + use __core; + use __core::mem::ManuallyDrop; + use core; + + #[cfg(feature = "const-fn")] + #[test] + fn static_uninitzialized() { + static mut I: i32 = unsafe { __core::mem::uninitialized() }; + // Initialize before drop + unsafe { core::ptr::write(&mut I as *mut i32, 42) }; + unsafe{ assert_eq!(I, 42) }; + } + + #[cfg(feature = "const-fn")] + #[test] + fn static_new_manually_drop() { + static mut M: ManuallyDrop = ManuallyDrop::new(42); + unsafe { assert_eq!(*M, 42); } + // Drop before deinitialization + unsafe { core::ptr::drop_in_place(&mut M as &mut i32 as *mut i32) }; + } - U { none: () }.some - } } diff --git a/src/binary_heap.rs b/src/binary_heap.rs index d535485e..f09c67f2 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -107,21 +107,24 @@ where K: Kind, { /* Constructors */ - /// Creates an empty BinaryHeap as a $K-heap. - /// - /// ``` - /// use heapless::binary_heap::{BinaryHeap, Max}; - /// use heapless::consts::*; - /// - /// let mut heap: BinaryHeap<_, U8, Max> = BinaryHeap::new(); - /// heap.push(4).unwrap(); - /// ``` - pub const fn new() -> Self { - BinaryHeap { - _kind: PhantomData, - data: Vec::new(), + + const_fn!( + /// Creates an empty BinaryHeap as a $K-heap. + /// + /// ``` + /// use heapless::binary_heap::{BinaryHeap, Max}; + /// use heapless::consts::*; + /// + /// let mut heap: BinaryHeap<_, U8, Max> = BinaryHeap::new(); + /// heap.push(4).unwrap(); + /// ``` + pub const fn new() -> Self { + BinaryHeap { + _kind: PhantomData, + data: Vec::new(), + } } - } + ); /* Public API */ /// Returns the capacity of the binary heap. @@ -430,6 +433,12 @@ mod tests { use binary_heap::{self, BinaryHeap, Min}; use consts::*; + #[cfg(feature = "const-fn")] + #[test] + fn static_new() { + static mut _B: BinaryHeap = BinaryHeap::new(); + } + #[test] fn min() { let mut heap = BinaryHeap::<_, U16, Min>::new(); diff --git a/src/const_fn.rs b/src/const_fn.rs new file mode 100644 index 00000000..e4286c64 --- /dev/null +++ b/src/const_fn.rs @@ -0,0 +1,35 @@ +// Make functions `const` if the `const-fn` feature is active. +// The meta attributes are in place to keep doc comments with the functions. +// The function definition incl. annotations and doc comments must be enclodes +// by the marco invocation. +macro_rules! const_fn { + ($(#[$attr:meta])* pub const unsafe fn $($f:tt)*) => ( + + $(#[$attr])* + #[cfg(feature = "const-fn")] + pub const unsafe fn $($f)* + + $(#[$attr])* + #[cfg(not(feature = "const-fn"))] + pub unsafe fn $($f)* + ); + ($(#[$attr:meta])* pub const fn $($f:tt)*) => ( + + $(#[$attr])* + #[cfg(feature = "const-fn")] + pub const fn $($f)* + + $(#[$attr])* + #[cfg(not(feature = "const-fn"))] + pub fn $($f)* + ); + ($(#[$attr:meta])* const fn $($f:tt)*) => ( + $(#[$attr])* + #[cfg(feature = "const-fn")] + const fn $($f)* + + $(#[$attr])* + #[cfg(not(feature = "const-fn"))] + fn $($f)* + ); +} diff --git a/src/lib.rs b/src/lib.rs index ce2b1475..dac98e3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,15 @@ //! assert_eq!(xs.pop(), Some(42)); //! //! // in a `static` variable -//! static mut XS: Vec = Vec::new(); +//! // static mut XS: Vec = Vec::new(); // requires feature `const-fn` +//! +//! // work around +//! static mut XS: Option> = None; +//! unsafe { XS = Some(Vec::new()) }; +//! let xs = unsafe { XS.as_mut().unwrap() }; +//! +//! xs.push(42); +//! assert_eq!(xs.pop(), Some(42)); //! //! // in the heap (though kind of pointless because no reallocation) //! let mut ys: Box> = Box::new(Vec::new()); @@ -47,11 +55,34 @@ //! queue //! - [`String`](struct.String.html) //! - [`Vec`](struct.Vec.html) +//! +//! +//! In order to target the Rust stable toolchain, there are some feature gates. +//! The features need to be enabled in `Cargo.toml` in order to use them. +//! Once the underlaying features in Rust are stable, +//! these feature gates might be activated by default. +//! +//! Example of `Cargo.toml`: +//! +//! ```text +//! ... +//! [dependencies] +//! heapless = { version = "0.4.0", features = ["const-fn"] } +//! ... +//! +//! ``` +//! +//! Currently the following features are availbale and not active by default: +//! +//! - `"const-fn"` -- Enable the nightly `const_fn` feature and make most `new` methods `const`. +//! This way they can be used to initialize static memory at compile time. +//! + #![allow(warnings)] #![deny(missing_docs)] #![deny(warnings)] -#![feature(const_fn)] +#![cfg_attr(feature = "const-fn", feature(const_fn))] #![feature(core_intrinsics)] #![feature(untagged_unions)] #![no_std] @@ -61,6 +92,9 @@ extern crate hash32; #[cfg(test)] extern crate std; +#[macro_use] +mod const_fn; + pub use binary_heap::BinaryHeap; pub use generic_array::typenum::consts; pub use generic_array::ArrayLength; diff --git a/src/linear_map.rs b/src/linear_map.rs index f4fc06ea..ed0ad2b1 100644 --- a/src/linear_map.rs +++ b/src/linear_map.rs @@ -21,19 +21,22 @@ where N: ArrayLength<(K, V)>, K: Eq, { - /// Creates an empty `LinearMap` - /// - /// # Examples - /// - /// ``` - /// use heapless::LinearMap; - /// use heapless::consts::*; - /// - /// let mut map: LinearMap<&str, isize, U8> = LinearMap::new(); - /// ``` - pub const fn new() -> Self { - LinearMap { buffer: Vec::new() } - } + + const_fn!( + /// Creates an empty `LinearMap` + /// + /// # Examples + /// + /// ``` + /// use heapless::LinearMap; + /// use heapless::consts::*; + /// + /// let mut map: LinearMap<&str, isize, U8> = LinearMap::new(); + /// ``` + pub const fn new() -> Self { + LinearMap { buffer: Vec::new() } + } + ); /// Returns the number of elements that the map can hold /// @@ -439,3 +442,18 @@ where self.iter.next().map(|&mut (ref k, ref mut v)| (k, v)) } } + + +#[cfg(feature = "const-fn")] // Remove this if there are more tests +#[cfg(test)] +mod test { + use consts::*; + use LinearMap; + + #[cfg(feature = "const-fn")] + #[test] + fn static_new() { + static mut _L: LinearMap= LinearMap::new(); + } + +} diff --git a/src/ring_buffer/mod.rs b/src/ring_buffer/mod.rs index f582e4e4..0f316d39 100644 --- a/src/ring_buffer/mod.rs +++ b/src/ring_buffer/mod.rs @@ -61,11 +61,13 @@ impl Atomic where U: Uxx, { - const fn new(v: U) -> Atomic { - Atomic { - v: UnsafeCell::new(v), + const_fn!( + const fn new(v: U) -> Atomic { + Atomic { + v: UnsafeCell::new(v), + } } - } + ); fn get_mut(&mut self) -> &mut U { unsafe { &mut *self.v.get() } @@ -116,13 +118,16 @@ where /// use heapless::RingBuffer; /// use heapless::consts::*; /// -/// static mut RB: RingBuffer = RingBuffer::new(); +/// // static mut RB: RingBuffer = RingBuffer::new(); // requires feature `const-fn` +/// +/// static mut RB: Option> = None; /// /// enum Event { A, B } /// /// fn main() { +/// unsafe { RB = Some(RingBuffer::new()) }; /// // NOTE(unsafe) beware of aliasing the `consumer` end point -/// let mut consumer = unsafe { RB.split().1 }; +/// let mut consumer = unsafe { RB.as_mut().unwrap().split().1 }; /// /// loop { /// // `dequeue` is a lockless operation @@ -138,7 +143,7 @@ where /// // this is a different execution context that can preempt `main` /// fn interrupt_handler() { /// // NOTE(unsafe) beware of aliasing the `producer` end point -/// let mut producer = unsafe { RB.split().0 }; +/// let mut producer = unsafe { RB.as_mut().unwrap().split().0 }; /// # let condition = true; /// /// // .. @@ -264,14 +269,17 @@ macro_rules! impl_ { N: Add + Unsigned, Sum: ArrayLength, { - /// Creates an empty ring buffer with a fixed capacity of `N` - pub const fn $uxx() -> Self { - RingBuffer { - buffer: ManuallyDrop::new(unsafe { mem::uninitialized() }), - head: Atomic::new(0), - tail: Atomic::new(0), + + const_fn!( + /// Creates an empty ring buffer with a fixed capacity of `N` + pub const fn $uxx() -> Self { + RingBuffer { + buffer: ManuallyDrop::new(unsafe { mem::uninitialized() }), + head: Atomic::new(0), + tail: Atomic::new(0), + } } - } + ); /// Returns the item in the front of the queue, or `None` if the queue is empty pub fn dequeue(&mut self) -> Option { @@ -349,10 +357,13 @@ where N: Add + Unsigned, Sum: ArrayLength, { - /// Alias for [`RingBuffer::usize`](struct.RingBuffer.html#method.usize) - pub const fn new() -> Self { - RingBuffer::usize() - } + + const_fn!( + /// Alias for [`RingBuffer::usize`](struct.RingBuffer.html#method.usize) + pub const fn new() -> Self { + RingBuffer::usize() + } + ); } impl_!(u8); @@ -434,6 +445,12 @@ mod tests { use consts::*; use RingBuffer; + #[cfg(feature = "const-fn")] + #[test] + fn static_new() { + static mut _R: RingBuffer = RingBuffer::new(); + } + #[test] fn drop() { struct Droppable; diff --git a/src/ring_buffer/spsc.rs b/src/ring_buffer/spsc.rs index d0d99423..a4f1e16f 100644 --- a/src/ring_buffer/spsc.rs +++ b/src/ring_buffer/spsc.rs @@ -207,9 +207,9 @@ mod tests { #[test] fn sanity() { - static mut RB: RingBuffer = RingBuffer::new(); + let mut rb: RingBuffer = RingBuffer::new(); - let (mut p, mut c) = unsafe { RB.split() }; + let (mut p, mut c) = rb.split(); assert_eq!(c.dequeue(), None); diff --git a/src/string.rs b/src/string.rs index ec171bc2..d6f93462 100644 --- a/src/string.rs +++ b/src/string.rs @@ -17,22 +17,25 @@ impl String where N: ArrayLength, { - /// Constructs a new, empty `String` with a fixed capacity of `N` - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use heapless::String; - /// use heapless::consts::*; - /// - /// let mut s: String = String::new(); - /// ``` + #[inline] - pub const fn new() -> Self { - String { vec: Vec::new() } - } + const_fn!( + /// Constructs a new, empty `String` with a fixed capacity of `N` + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use heapless::String; + /// use heapless::consts::*; + /// + /// let mut s: String = String::new(); + /// ``` + pub const fn new() -> Self { + String { vec: Vec::new() } + } + ); /// Converts a vector of bytes into a `String`. /// @@ -521,6 +524,12 @@ mod tests { use consts::*; use {String, Vec}; + #[cfg(feature = "const-fn")] + #[test] + fn static_new() { + static mut _S: String = String::new(); + } + #[test] fn debug() { extern crate std; diff --git a/src/vec.rs b/src/vec.rs index dbff46cc..e7db938d 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -48,13 +48,15 @@ where N: ArrayLength, { /* Constructors */ - /// Constructs a new, empty vector with a fixed capacity of `N` - pub const fn new() -> Self { - Vec { - buffer: ManuallyDrop::new(unsafe { mem::uninitialized() }), - len: 0, + const_fn!( + /// Constructs a new, empty vector with a fixed capacity of `N` + pub const fn new() -> Self { + Vec { + buffer: ManuallyDrop::new(unsafe { mem::uninitialized() }), + len: 0, + } } - } + ); /* Public API */ /// Returns the maximum number of elements the vector can hold @@ -512,6 +514,12 @@ mod tests { use consts::*; use Vec; + #[cfg(feature = "const-fn")] + #[test] + fn static_new() { + static mut _V: Vec = Vec::new(); + } + macro_rules! droppable { () => ( struct Droppable; diff --git a/tests/tsan.rs b/tests/tsan.rs index 59092f13..c6dc2743 100644 --- a/tests/tsan.rs +++ b/tests/tsan.rs @@ -13,9 +13,10 @@ use scoped_threadpool::Pool; #[test] fn once() { - static mut RB: RingBuffer = RingBuffer::new(); + static mut RB: Option> = None; + unsafe{ RB = Some(RingBuffer::new()) }; - let rb = unsafe { &mut RB }; + let rb = unsafe { RB.as_mut().unwrap() }; rb.enqueue(0).unwrap(); @@ -34,9 +35,10 @@ fn once() { #[test] fn twice() { - static mut RB: RingBuffer = RingBuffer::new(); + static mut RB: Option> = None; + unsafe{ RB = Some(RingBuffer::new()) }; - let rb = unsafe { &mut RB }; + let rb = unsafe { RB.as_mut().unwrap() }; rb.enqueue(0).unwrap(); rb.enqueue(1).unwrap();