From e01216cf802f4a00900ecd323f1754c67a966f48 Mon Sep 17 00:00:00 2001 From: Cameron Bytheway Date: Tue, 26 Nov 2024 11:47:54 -0700 Subject: [PATCH] feat(bolero-generator): add support for calling generators from anywhere (#258) --- lib/bolero-engine/src/lib.rs | 3 + lib/bolero-generator/Cargo.toml | 6 +- lib/bolero-generator/src/any.rs | 116 +++++++++++++ lib/bolero-generator/src/any/default.rs | 162 ++++++++++++++++++ lib/bolero-generator/src/any/kani.rs | 82 +++++++++ ...olero_generator__any__tests__any_test.snap | 10 ++ ..._generator__any__tests__fill_any_test.snap | 46 +++++ ...lero_generator__any__tests__pick_test.snap | 9 + ...o_generator__any__tests__shuffle_test.snap | 36 ++++ lib/bolero-generator/src/any/tests.rs | 46 +++++ lib/bolero-generator/src/driver.rs | 106 +----------- lib/bolero-generator/src/driver/options.rs | 105 ++++++++++++ lib/bolero-generator/src/kani.rs | 145 ++++++++++++++++ lib/bolero-generator/src/lib.rs | 19 +- lib/bolero-generator/src/prelude.rs | 16 ++ lib/bolero-generator/src/trace.rs | 2 + lib/bolero-kani/src/lib.rs | 130 +------------- 17 files changed, 793 insertions(+), 246 deletions(-) create mode 100644 lib/bolero-generator/src/any.rs create mode 100644 lib/bolero-generator/src/any/default.rs create mode 100644 lib/bolero-generator/src/any/kani.rs create mode 100644 lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__any_test.snap create mode 100644 lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__fill_any_test.snap create mode 100644 lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__pick_test.snap create mode 100644 lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__shuffle_test.snap create mode 100644 lib/bolero-generator/src/any/tests.rs create mode 100644 lib/bolero-generator/src/driver/options.rs create mode 100644 lib/bolero-generator/src/kani.rs create mode 100644 lib/bolero-generator/src/prelude.rs diff --git a/lib/bolero-engine/src/lib.rs b/lib/bolero-engine/src/lib.rs index b87c37b..f2d5cb7 100644 --- a/lib/bolero-engine/src/lib.rs +++ b/lib/bolero-engine/src/lib.rs @@ -4,6 +4,9 @@ pub use bolero_generator::{ TypeGenerator, ValueGenerator, }; +#[cfg(kani)] +pub use bolero_generator::kani; + pub type Seed = u128; #[cfg(not(kani))] diff --git a/lib/bolero-generator/Cargo.toml b/lib/bolero-generator/Cargo.toml index 1739fac..90ad242 100644 --- a/lib/bolero-generator/Cargo.toml +++ b/lib/bolero-generator/Cargo.toml @@ -12,7 +12,8 @@ readme = "README.md" rust-version = "1.66.0" [features] -default = ["either", "std"] +default = ["any", "either", "std"] +any = ["getrandom", "rand_xoshiro", "std"] std = ["alloc", "either/use_std"] alloc = ["rand_core/alloc"] @@ -20,9 +21,12 @@ alloc = ["rand_core/alloc"] arbitrary = { version = "1.0", optional = true } bolero-generator-derive = { version = "0.11", path = "../bolero-generator-derive" } either = { version = "1.5", default-features = false, optional = true } +getrandom = { version = "0.2", optional = true } rand_core = { version = "0.6", default-features = false } +rand_xoshiro = { version = "0.6", optional = true } [dev-dependencies] +insta = "1" rand = "0.8" [lints.rust.unexpected_cfgs] diff --git a/lib/bolero-generator/src/any.rs b/lib/bolero-generator/src/any.rs new file mode 100644 index 0000000..71d6e36 --- /dev/null +++ b/lib/bolero-generator/src/any.rs @@ -0,0 +1,116 @@ +use crate::{gen, TypeGenerator, ValueGenerator}; + +#[cfg(not(kani))] +mod default; + +#[cfg(any(kani, test))] +#[cfg_attr(not(kani), allow(dead_code, unused_imports))] +mod kani; + +#[cfg(test)] +mod tests; + +pub mod scope { + #[cfg(not(kani))] + pub use super::default::*; + #[cfg(kani)] + pub use super::kani::*; +} + +pub use scope::{assume, fill_bytes, Error}; + +pub trait Any: ValueGenerator { + fn any(&self) -> Self::Output; +} + +impl Any for G { + #[track_caller] + fn any(&self) -> Self::Output { + scope::any(self) + } +} + +#[inline] +pub fn any() -> T { + gen().any() +} + +pub trait AnySliceExt { + fn pick(&self) -> &T; +} + +impl AnySliceExt for [T] { + #[inline] + fn pick(&self) -> &T { + let index = (0..self.len()).any(); + &self[index] + } +} + +pub trait AnySliceMutExt { + fn shuffle(&mut self); + fn fill_any(&mut self) + where + T: TypeGenerator; +} + +impl AnySliceMutExt for [T] { + #[inline] + fn shuffle(&mut self) { + let max_dst = self.len().saturating_sub(1); + for src in 0..max_dst { + let dst = (src..=max_dst).any(); + self.swap(src, dst); + } + } + + #[inline] + fn fill_any(&mut self) + where + T: TypeGenerator, + { + for value in self { + *value = any(); + } + } +} + +#[cfg(feature = "alloc")] +impl AnySliceMutExt for alloc::collections::VecDeque { + #[inline] + fn shuffle(&mut self) { + let max_dst = self.len().saturating_sub(1); + for src in 0..max_dst { + let dst = (src..=max_dst).any(); + self.swap(src, dst); + } + } + + #[inline] + fn fill_any(&mut self) + where + T: TypeGenerator, + { + for value in self { + *value = any(); + } + } +} + +#[inline] +pub fn fill(values: &mut [T]) +where + T: TypeGenerator, +{ + values.fill_any() +} + +#[inline] +pub fn shuffle(items: &mut [T]) { + items.shuffle() +} + +#[inline] +pub fn pick(items: &[T]) -> &T { + items.pick() +} diff --git a/lib/bolero-generator/src/any/default.rs b/lib/bolero-generator/src/any/default.rs new file mode 100644 index 0000000..0702a49 --- /dev/null +++ b/lib/bolero-generator/src/any/default.rs @@ -0,0 +1,162 @@ +use crate::driver::object::{self, DynDriver, Object}; +use core::fmt; +use std::cell::RefCell; + +pub trait Scope: 'static + DynDriver + core::any::Any { + fn borrowed(&mut self) -> object::Borrowed; +} + +impl Scope for T +where + T: 'static + DynDriver + core::any::Any, +{ + fn borrowed(&mut self) -> object::Borrowed { + object::Borrowed(self) + } +} + +type Type = Box; + +thread_local! { + static SCOPE: RefCell = RefCell::new(Box::new(Object(default()))); +} + +fn default() -> impl crate::Driver { + use rand_core::SeedableRng; + use rand_xoshiro::Xoshiro128PlusPlus; + + let mut seed = [42; 16]; + // make a best effort to get random seeds + let _ = getrandom::getrandom(&mut seed); + let rng = Xoshiro128PlusPlus::from_seed(seed); + crate::driver::Rng::new(rng, &Default::default()) +} + +fn set(value: Type) -> Type { + SCOPE.with(|r| core::mem::replace(&mut *r.borrow_mut(), value)) +} + +// protect against panics in the `with` function +struct Prev(Option); + +impl Prev { + fn reset(mut self) -> Type { + set(self.0.take().unwrap()) + } +} + +impl Drop for Prev { + fn drop(&mut self) { + if let Some(prev) = self.0.take() { + let _ = set(prev); + } + } +} + +pub fn with(driver: Box, f: F) -> (Box, R) +where + D: Scope, + F: FnOnce() -> R, +{ + let prev = Prev(Some(set(driver))); + let res = f(); + let driver = prev.reset(); + let driver = if driver.type_id() == core::any::TypeId::of::() { + unsafe { + let raw = Box::into_raw(driver); + Box::from_raw(raw as *mut D) + } + } else { + panic!( + "invalid scope state; expected {}", + core::any::type_name::() + ) + }; + (driver, res) +} + +fn borrow_with R, R>(f: F) -> R { + SCOPE.with(|r| { + let mut driver = r.borrow_mut(); + let mut driver = driver.borrowed(); + f(&mut driver) + }) +} + +#[track_caller] +pub fn any(g: &G) -> G::Output { + borrow_with(|driver| { + g.generate(driver).unwrap_or_else(|| { + std::panic::panic_any(Error { + location: core::panic::Location::caller(), + generator: core::any::type_name::(), + output: core::any::type_name::(), + }) + }) + }) +} + +#[track_caller] +pub fn assume(condition: bool, message: &'static str) { + if !condition { + std::panic::panic_any(Error { + location: core::panic::Location::caller(), + generator: "", + output: message, + }); + } +} + +#[track_caller] +pub fn fill_bytes(bytes: &mut [u8]) { + borrow_with(|driver| { + let len = bytes.len(); + let mut hint = || (len, Some(len)); + driver + .0 + .gen_from_bytes(&mut hint, &mut |src: &[u8]| { + if src.len() == len { + bytes.copy_from_slice(src); + Some(len) + } else { + None + } + }) + .unwrap_or_else(|| { + std::panic::panic_any(Error { + location: core::panic::Location::caller(), + generator: "", + output: "could not generate enough bytes", + }); + }) + }) +} + +#[derive(Clone)] +pub struct Error { + location: &'static core::panic::Location<'static>, + generator: &'static str, + output: &'static str, +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Error") + .field("location", &self.location) + .field("generator", &self.generator) + .field("output", &self.output) + .finish() + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Could not generate value of type {} at {}", + self.output, self.location, + ) + } +} + +impl std::error::Error for Error {} diff --git a/lib/bolero-generator/src/any/kani.rs b/lib/bolero-generator/src/any/kani.rs new file mode 100644 index 0000000..ccddf23 --- /dev/null +++ b/lib/bolero-generator/src/any/kani.rs @@ -0,0 +1,82 @@ +use std::cell::RefCell; + +type Type = crate::kani::Driver; + +pub use core::convert::Infallible as Error; + +/// Kani doesn't support thread_locals so use a static global instead +static mut CURRENT: Type = Type { + depth: 0, + max_depth: crate::driver::Options::DEFAULT_MAX_DEPTH, +}; + +fn get() -> Type { + unsafe { + let depth = CURRENT.depth; + let max_depth = CURRENT.max_depth; + + Type { depth, max_depth } + } +} + +fn set(value: Type) -> Type { + let prev = get(); + + unsafe { + CURRENT.depth = value.depth; + CURRENT.max_depth = value.max_depth; + } + + prev +} + +pub fn with(driver: Type, f: F) -> (Type, R) +where + F: FnOnce() -> R, +{ + let prev = set(driver); + let res = f(); + let driver = set(prev); + (driver, res) +} + +fn borrow_with R, R>(f: F) -> R { + let mut driver = unsafe { + let depth = CURRENT.depth; + let max_depth = CURRENT.max_depth; + + Type { depth, max_depth } + }; + let result = f(&mut driver); + set(driver); + result +} + +pub fn any(g: &G) -> G::Output { + borrow_with(|driver| { + let v = g.generate(driver); + assume(v.is_some(), "generator should return at least one value"); + v.unwrap() + }) +} + +pub fn fill_bytes(bytes: &mut [u8]) { + for dst in bytes { + #[cfg(kani)] + let src = ::kani::any(); + + #[cfg(not(kani))] + let src = 0; + + *dst = src; + } +} + +#[inline] +pub fn assume(condition: bool, message: &'static str) { + #[cfg(kani)] + ::kani::assume(condition); + + let _ = condition; + let _ = message; +} diff --git a/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__any_test.snap b/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__any_test.snap new file mode 100644 index 0000000..046d3e8 --- /dev/null +++ b/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__any_test.snap @@ -0,0 +1,10 @@ +--- +source: bolero-generator/src/any/tests.rs +expression: out +--- +[ + 0, + 1, + 2, + 3, +] diff --git a/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__fill_any_test.snap b/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__fill_any_test.snap new file mode 100644 index 0000000..5d78910 --- /dev/null +++ b/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__fill_any_test.snap @@ -0,0 +1,46 @@ +--- +source: bolero-generator/src/any/tests.rs +expression: out +--- +[ + [ + false, + false, + false, + ], + [ + false, + false, + true, + ], + [ + false, + true, + false, + ], + [ + false, + true, + true, + ], + [ + true, + false, + false, + ], + [ + true, + false, + true, + ], + [ + true, + true, + false, + ], + [ + true, + true, + true, + ], +] diff --git a/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__pick_test.snap b/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__pick_test.snap new file mode 100644 index 0000000..cc600ec --- /dev/null +++ b/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__pick_test.snap @@ -0,0 +1,9 @@ +--- +source: bolero-generator/src/any/tests.rs +expression: out +--- +[ + 1, + 2, + 3, +] diff --git a/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__shuffle_test.snap b/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__shuffle_test.snap new file mode 100644 index 0000000..baefe8a --- /dev/null +++ b/lib/bolero-generator/src/any/snapshots/bolero_generator__any__tests__shuffle_test.snap @@ -0,0 +1,36 @@ +--- +source: bolero-generator/src/any/tests.rs +expression: out +--- +[ + [ + 0, + 1, + 2, + ], + [ + 0, + 2, + 1, + ], + [ + 1, + 0, + 2, + ], + [ + 1, + 2, + 0, + ], + [ + 2, + 1, + 0, + ], + [ + 2, + 0, + 1, + ], +] diff --git a/lib/bolero-generator/src/any/tests.rs b/lib/bolero-generator/src/any/tests.rs new file mode 100644 index 0000000..6f3ced3 --- /dev/null +++ b/lib/bolero-generator/src/any/tests.rs @@ -0,0 +1,46 @@ +use super::*; + +fn exhaustive(name: &str, f: impl Fn() -> T) { + let driver = crate::driver::exhaustive::Driver::default(); + let driver = crate::driver::object::Object(driver); + let mut driver = Box::new(driver); + + let mut out = vec![]; + + while driver.step().is_continue() { + driver = scope::with(driver, || { + out.push(f()); + }) + .0; + } + + insta::assert_debug_snapshot!(name, out); +} + +#[test] +fn any_test() { + exhaustive("any_test", || (0..4).any()) +} + +#[test] +fn shuffle_test() { + exhaustive("shuffle_test", || { + let mut v = [0, 1, 2]; + v.shuffle(); + v + }) +} + +#[test] +fn fill_any_test() { + exhaustive("fill_any_test", || { + let mut v = [false, false, false]; + v.fill_any(); + v + }) +} + +#[test] +fn pick_test() { + exhaustive("pick_test", || [1, 2, 3].pick()) +} diff --git a/lib/bolero-generator/src/driver.rs b/lib/bolero-generator/src/driver.rs index caad6fa..d82b0ab 100644 --- a/lib/bolero-generator/src/driver.rs +++ b/lib/bolero-generator/src/driver.rs @@ -14,9 +14,11 @@ pub mod cache; #[cfg(feature = "alloc")] pub mod exhaustive; pub mod object; +mod options; mod rng; pub use bytes::ByteSliceDriver; +pub use options::Options; pub use rng::Rng; macro_rules! gen_method { @@ -176,107 +178,3 @@ pub enum DriverMode { /// This is useful for engines that want to maximize the amount of time spent executing tests. Forced, } - -#[derive(Clone, Debug, Default)] -pub struct Options { - shrink_time: Option, - max_depth: Option, - max_len: Option, - exhaustive: bool, -} - -impl Options { - pub const DEFAULT_MAX_DEPTH: usize = 5; - pub const DEFAULT_MAX_LEN: usize = 4096; - pub const DEFAULT_SHRINK_TIME: core::time::Duration = core::time::Duration::from_secs(1); - - pub fn with_shrink_time(mut self, shrink_time: core::time::Duration) -> Self { - self.shrink_time = Some(shrink_time); - self - } - - pub fn with_max_depth(mut self, max_depth: usize) -> Self { - self.max_depth = Some(max_depth); - self - } - - pub fn with_max_len(mut self, max_len: usize) -> Self { - self.max_len = Some(max_len); - self - } - - pub fn with_exhaustive(mut self, exhaustive: bool) -> Self { - self.exhaustive = exhaustive; - self - } - - pub fn set_exhaustive(&mut self, exhaustive: bool) -> &mut Self { - self.exhaustive = exhaustive; - self - } - - pub fn set_shrink_time(&mut self, shrink_time: core::time::Duration) -> &mut Self { - self.shrink_time = Some(shrink_time); - self - } - - pub fn set_max_depth(&mut self, max_depth: usize) -> &mut Self { - self.max_depth = Some(max_depth); - self - } - - pub fn set_max_len(&mut self, max_len: usize) -> &mut Self { - self.max_len = Some(max_len); - self - } - - #[inline] - pub fn exhaustive(&self) -> bool { - self.exhaustive - } - - #[inline] - pub fn max_depth(&self) -> Option { - self.max_depth - } - - #[inline] - pub fn max_len(&self) -> Option { - self.max_len - } - - #[inline] - pub fn shrink_time(&self) -> Option { - self.shrink_time - } - - #[inline] - pub fn max_depth_or_default(&self) -> usize { - self.max_depth.unwrap_or(Self::DEFAULT_MAX_DEPTH) - } - - #[inline] - pub fn max_len_or_default(&self) -> usize { - self.max_len.unwrap_or(Self::DEFAULT_MAX_LEN) - } - - #[inline] - pub fn shrink_time_or_default(&self) -> core::time::Duration { - self.shrink_time.unwrap_or(Self::DEFAULT_SHRINK_TIME) - } - - #[inline] - pub fn merge_from(&mut self, other: &Self) { - macro_rules! merge { - ($name:ident) => { - if let Some($name) = other.$name { - self.$name = Some($name); - } - }; - } - - merge!(max_depth); - merge!(max_len); - merge!(shrink_time); - } -} diff --git a/lib/bolero-generator/src/driver/options.rs b/lib/bolero-generator/src/driver/options.rs new file mode 100644 index 0000000..01c0c34 --- /dev/null +++ b/lib/bolero-generator/src/driver/options.rs @@ -0,0 +1,105 @@ +use core::time::Duration; + +#[derive(Clone, Debug, Default)] +pub struct Options { + shrink_time: Option, + max_depth: Option, + max_len: Option, + exhaustive: bool, +} + +impl Options { + pub const DEFAULT_MAX_DEPTH: usize = 5; + pub const DEFAULT_MAX_LEN: usize = 4096; + pub const DEFAULT_SHRINK_TIME: Duration = Duration::from_secs(1); + + pub fn with_shrink_time(mut self, shrink_time: Duration) -> Self { + self.shrink_time = Some(shrink_time); + self + } + + pub fn with_max_depth(mut self, max_depth: usize) -> Self { + self.max_depth = Some(max_depth); + self + } + + pub fn with_max_len(mut self, max_len: usize) -> Self { + self.max_len = Some(max_len); + self + } + + pub fn with_exhaustive(mut self, exhaustive: bool) -> Self { + self.exhaustive = exhaustive; + self + } + + pub fn set_exhaustive(&mut self, exhaustive: bool) -> &mut Self { + self.exhaustive = exhaustive; + self + } + + pub fn set_shrink_time(&mut self, shrink_time: Duration) -> &mut Self { + self.shrink_time = Some(shrink_time); + self + } + + pub fn set_max_depth(&mut self, max_depth: usize) -> &mut Self { + self.max_depth = Some(max_depth); + self + } + + pub fn set_max_len(&mut self, max_len: usize) -> &mut Self { + self.max_len = Some(max_len); + self + } + + #[inline] + pub fn exhaustive(&self) -> bool { + self.exhaustive + } + + #[inline] + pub fn max_depth(&self) -> Option { + self.max_depth + } + + #[inline] + pub fn max_len(&self) -> Option { + self.max_len + } + + #[inline] + pub fn shrink_time(&self) -> Option { + self.shrink_time + } + + #[inline] + pub fn max_depth_or_default(&self) -> usize { + self.max_depth.unwrap_or(Self::DEFAULT_MAX_DEPTH) + } + + #[inline] + pub fn max_len_or_default(&self) -> usize { + self.max_len.unwrap_or(Self::DEFAULT_MAX_LEN) + } + + #[inline] + pub fn shrink_time_or_default(&self) -> Duration { + self.shrink_time.unwrap_or(Self::DEFAULT_SHRINK_TIME) + } + + #[inline] + pub fn merge_from(&mut self, other: &Self) { + macro_rules! merge { + ($name:ident) => { + if let Some($name) = other.$name { + self.$name = Some($name); + } + }; + } + + merge!(max_depth); + merge!(max_len); + merge!(shrink_time); + } +} diff --git a/lib/bolero-generator/src/kani.rs b/lib/bolero-generator/src/kani.rs new file mode 100644 index 0000000..3a65103 --- /dev/null +++ b/lib/bolero-generator/src/kani.rs @@ -0,0 +1,145 @@ +use crate::TypeGenerator; +use core::ops::{Bound, RangeBounds}; + +pub struct Driver { + pub(crate) depth: usize, + pub(crate) max_depth: usize, +} + +impl Default for Driver { + #[inline] + fn default() -> Self { + Self::new(&crate::driver::Options::default()) + } +} + +impl Driver { + #[inline] + pub fn new(options: &crate::driver::Options) -> Self { + Self { + depth: 0, + max_depth: options.max_depth_or_default(), + } + } +} + +macro_rules! gen { + ($name:ident, $ty:ident) => { + #[inline(always)] + fn $name(&mut self, min: Bound<&$ty>, max: Bound<&$ty>) -> Option<$ty> { + let value: $ty = shim::any(); + shim::assume((min, max).contains(&value)); + Some(value) + } + }; +} + +impl crate::Driver for Driver { + gen!(gen_u8, u8); + + gen!(gen_i8, i8); + + gen!(gen_u16, u16); + + gen!(gen_i16, i16); + + gen!(gen_u32, u32); + + gen!(gen_i32, i32); + + gen!(gen_u64, u64); + + gen!(gen_i64, i64); + + gen!(gen_u128, u128); + + gen!(gen_i128, i128); + + gen!(gen_usize, usize); + + gen!(gen_isize, isize); + + gen!(gen_f32, f32); + + gen!(gen_f64, f64); + + #[inline(always)] + fn gen(&mut self) -> Option { + let value = T::generate(self); + shim::assume(value.is_some()); + value + } + + #[inline(always)] + fn gen_char(&mut self, min: Bound<&char>, max: Bound<&char>) -> Option { + let value = shim::any(); + shim::assume((min, max).contains(&value)); + Some(value) + } + + #[inline(always)] + fn gen_bool(&mut self, _probability: Option) -> Option { + Some(shim::any()) + } + + #[inline(always)] + fn gen_from_bytes(&mut self, _hint: Hint, mut gen: Gen) -> Option + where + Hint: FnOnce() -> (usize, Option), + Gen: FnMut(&[u8]) -> Option<(usize, T)>, + { + // TODO make this configurable + const MAX_LEN: usize = 256; + + let bytes = shim::any::<[u8; MAX_LEN]>(); + let len = shim::any::(); + shim::assume(len <= MAX_LEN); + + let value = gen(&bytes[..len]).map(|v| v.1); + shim::assume(value.is_some()); + value + } + + #[inline(always)] + fn gen_variant(&mut self, variants: usize, base_case: usize) -> Option { + if self.depth == self.max_depth { + return Some(base_case); + } + + let selected: usize = shim::any(); + shim::assume(selected < variants); + Some(selected) + } + + #[inline(always)] + fn depth(&self) -> usize { + self.depth + } + + #[inline(always)] + fn set_depth(&mut self, depth: usize) { + self.depth = depth; + } + + #[inline(always)] + fn max_depth(&self) -> usize { + self.max_depth + } +} + +/// Make a shim for when kani isn't available +#[cfg(not(kani))] +#[allow(dead_code)] +mod shim { + pub fn any() -> T { + todo!() + } + + pub fn assume(cond: bool) { + // no-op + let _ = cond; + } +} + +#[cfg(kani)] +use ::kani as shim; diff --git a/lib/bolero-generator/src/lib.rs b/lib/bolero-generator/src/lib.rs index 206f3cc..81bf4b5 100644 --- a/lib/bolero-generator/src/lib.rs +++ b/lib/bolero-generator/src/lib.rs @@ -31,6 +31,8 @@ pub use bolero_generator_derive::*; #[cfg(feature = "arbitrary")] pub mod arbitrary; +#[cfg(feature = "any")] +pub mod any; pub mod array; pub mod atomic; pub mod bool; @@ -38,8 +40,11 @@ pub mod bounded; pub mod char; pub mod combinator; pub mod driver; +#[cfg(any(test, kani))] +pub mod kani; pub mod num; pub mod one_of; +pub mod prelude; pub mod range; pub mod result; pub mod time; @@ -269,17 +274,3 @@ impl ValueGenerator for Constant { pub fn constant(value: T) -> Constant { Constant { value } } - -pub mod prelude { - pub use crate::{ - constant, gen, gen_with, - one_of::{one_of, one_value_of, OneOfExt, OneValueOfExt}, - TypeGenerator, TypeGeneratorWithParams, ValueGenerator, - }; - - #[allow(deprecated)] - pub use crate::driver::DriverMode; - - #[cfg(feature = "arbitrary")] - pub use crate::gen_arbitrary; -} diff --git a/lib/bolero-generator/src/prelude.rs b/lib/bolero-generator/src/prelude.rs new file mode 100644 index 0000000..b95810a --- /dev/null +++ b/lib/bolero-generator/src/prelude.rs @@ -0,0 +1,16 @@ +pub use crate::{ + constant, gen, gen_with, + one_of::{one_of, one_value_of, OneOfExt, OneValueOfExt}, + TypeGenerator, TypeGeneratorWithParams, ValueGenerator, +}; + +#[cfg(feature = "any")] +pub use crate::any::{ + any, assume, fill, fill_bytes, pick, shuffle, Any, AnySliceExt, AnySliceMutExt, +}; + +#[allow(deprecated)] +pub use crate::driver::DriverMode; + +#[cfg(feature = "arbitrary")] +pub use crate::gen_arbitrary; diff --git a/lib/bolero-generator/src/trace.rs b/lib/bolero-generator/src/trace.rs index 9fe479f..a795f74 100644 --- a/lib/bolero-generator/src/trace.rs +++ b/lib/bolero-generator/src/trace.rs @@ -1,3 +1,5 @@ +#![cfg_attr(kani, allow(dead_code))] + use super::ValueGenerator; use core::{any::type_name, ops::Bound}; diff --git a/lib/bolero-kani/src/lib.rs b/lib/bolero-kani/src/lib.rs index 9a2ff2f..45d7607 100644 --- a/lib/bolero-kani/src/lib.rs +++ b/lib/bolero-kani/src/lib.rs @@ -2,29 +2,13 @@ //! //! This crate should not be used directly. Instead, use `bolero`. -#![allow(unused_unsafe)] // nondet needs to be unsafe but it's currently not - -#[cfg(not(kani))] -#[allow(dead_code)] -mod kani { - pub fn any() -> T { - todo!() - } - - pub fn assume(cond: bool) { - // no-op - let _ = cond; - } -} - #[doc(hidden)] -#[cfg(any(test, all(feature = "lib", kani)))] +#[cfg(all(feature = "lib", kani))] pub mod lib { #[allow(unused_imports)] use super::*; - use bolero_engine::{driver, input, Driver, Engine, TargetLocation, Test, TypeGenerator}; - use core::ops::{Bound, RangeBounds}; + use bolero_engine::{driver, input, kani::Driver as KaniDriver, Engine, TargetLocation, Test}; #[derive(Debug, Default)] pub struct KaniEngine {} @@ -81,118 +65,10 @@ pub mod lib { } fn with_driver Output>(&mut self, f: &mut F) -> Output { - let max_depth = self.options.max_depth_or_default(); - let mut driver = KaniDriver { - max_depth, - depth: 0, - }; + let mut driver = KaniDriver::new(&self.options); f(&mut driver) } } - - struct KaniDriver { - depth: usize, - max_depth: usize, - } - - macro_rules! gen { - ($name:ident, $ty:ident) => { - fn $name(&mut self, min: Bound<&$ty>, max: Bound<&$ty>) -> Option<$ty> { - let value: $ty = kani::any(); - kani::assume((min, max).contains(&value)); - Some(value) - } - }; - } - - impl Driver for KaniDriver { - gen!(gen_u8, u8); - - gen!(gen_i8, i8); - - gen!(gen_u16, u16); - - gen!(gen_i16, i16); - - gen!(gen_u32, u32); - - gen!(gen_i32, i32); - - gen!(gen_u64, u64); - - gen!(gen_i64, i64); - - gen!(gen_u128, u128); - - gen!(gen_i128, i128); - - gen!(gen_usize, usize); - - gen!(gen_isize, isize); - - gen!(gen_f32, f32); - - gen!(gen_f64, f64); - - fn gen(&mut self) -> Option { - let value = T::generate(self); - kani::assume(value.is_some()); - value - } - - fn gen_char(&mut self, min: Bound<&char>, max: Bound<&char>) -> Option { - let value = kani::any(); - kani::assume((min, max).contains(&value)); - Some(value) - } - - fn gen_bool(&mut self, _probability: Option) -> Option { - Some(kani::any()) - } - - fn gen_from_bytes(&mut self, _hint: Hint, mut gen: Gen) -> Option - where - Hint: FnOnce() -> (usize, Option), - Gen: FnMut(&[u8]) -> Option<(usize, T)>, - { - // TODO make this configurable - const MAX_LEN: usize = 256; - - let bytes = kani::any::<[u8; MAX_LEN]>(); - let len = kani::any::(); - kani::assume(len <= MAX_LEN); - - let value = gen(&bytes[..len]).map(|v| v.1); - kani::assume(value.is_some()); - value - } - - #[inline] - fn gen_variant(&mut self, variants: usize, base_case: usize) -> Option { - if self.depth == self.max_depth { - return Some(base_case); - } - - let selected: usize = kani::any(); - kani::assume(selected < variants); - Some(selected) - } - - #[inline] - fn depth(&self) -> usize { - self.depth - } - - #[inline] - fn set_depth(&mut self, depth: usize) { - self.depth = depth; - } - - #[inline] - fn max_depth(&self) -> usize { - self.max_depth - } - } } #[doc(hidden)]