diff --git a/crates/formality-core/src/binder/fuzz.rs b/crates/formality-core/src/binder/fuzz.rs index c4f66f90..6f17c723 100644 --- a/crates/formality-core/src/binder/fuzz.rs +++ b/crates/formality-core/src/binder/fuzz.rs @@ -1,7 +1,28 @@ +use bolero::{TypeGenerator, ValueGenerator}; + use crate::{fold::CoreFold, fuzz::PushGuard, language::Language, variable::CoreBoundVar}; use super::{fresh_bound_var, CoreBinder}; +#[derive(Debug)] +pub struct KindVec { + pub vec: Vec, +} + +impl bolero::TypeGenerator for KindVec +where + L::Kind: TypeGenerator, +{ + fn generate(driver: &mut D) -> Option { + let num_kinds: usize = L::fuzz_binder_range().get().generate(driver)?; + Some(KindVec { + vec: (0..num_kinds) + .map(|_| driver.gen()) + .collect::>()?, + }) + } +} + /// Brings new variables into scope for fuzzing. /// Don't invoke directly, instead call `L::open_fuzz_binder`. pub(crate) fn open_fuzz_binder_impl(kinds: &[L::Kind]) -> PushKindGuard @@ -52,8 +73,8 @@ where { /// Generate a binder with some fresh data inside. fn generate(driver: &mut D) -> Option { - let kinds: Vec = driver.gen()?; - let guard = L::open_fuzz_binder(&kinds); + let kinds: KindVec = driver.gen()?; + let guard = L::open_fuzz_binder(&kinds.vec); let bound_term: T = driver.gen()?; Some(guard.into_binder(bound_term)) } diff --git a/crates/formality-core/src/language.rs b/crates/formality-core/src/language.rs index 9217104e..cc216d9d 100644 --- a/crates/formality-core/src/language.rs +++ b/crates/formality-core/src/language.rs @@ -51,6 +51,9 @@ pub trait Language: 'static + Copy + Ord + Hash + Debug + Default { /// Access the data for bound variables in scope. /// Not normally used directly, instead invoke `push_fuzz_variables`. fn fuzz_free_variables() -> &'static FuzzSingleton>>; + + /// Range of parameters to use when generating a `Binder`. + fn fuzz_binder_range() -> &'static FuzzSingleton>; } /// For consistency with types like `CoreVariable`, we write `CoreKind` instead of `Kind`. diff --git a/crates/formality-core/src/lib.rs b/crates/formality-core/src/lib.rs index 686ce0dc..0963d126 100644 --- a/crates/formality-core/src/lib.rs +++ b/crates/formality-core/src/lib.rs @@ -118,11 +118,17 @@ macro_rules! declare_language { use super::super::*; impl $crate::language::Language for super::FormalityLang { const NAME: &'static str = $name; + type Kind = $kind; + type Parameter = $param; + const BINDING_OPEN: char = $binding_open; + const BINDING_CLOSE: char = $binding_close; + const KEYWORDS: &'static [&'static str] = &[$($kw),*]; + fn fuzz_free_variables() -> &'static $crate::fuzz::FuzzSingleton< Vec<$crate::variable::CoreBoundVar> > { @@ -131,6 +137,13 @@ macro_rules! declare_language { > = $crate::fuzz::FuzzSingleton::new(); &FUZZ_POOL } + + fn fuzz_binder_range() -> &'static $crate::fuzz::FuzzSingleton< + std::ops::Range + > { + static FUZZ_POOL: $crate::fuzz::FuzzSingleton> = $crate::fuzz::FuzzSingleton::new(); + &FUZZ_POOL + } } } diff --git a/crates/formality-rust/src/fuzz.rs b/crates/formality-rust/src/fuzz.rs index 48e256de..361a5d6e 100644 --- a/crates/formality-rust/src/fuzz.rs +++ b/crates/formality-rust/src/fuzz.rs @@ -4,18 +4,59 @@ //! //! [f]: https://rust-lang.github.io/a-mir-formality/formality_core/fuzzing.html -use bolero::Driver; -use formality_core::{language::Language, Map, Upcast}; +use bolero::{Driver, ValueGenerator}; +use formality_core::{binder::fuzz::KindVec, language::Language, Map, Upcast}; use formality_types::{ fuzz::FuzzCx, - grammar::{AdtId, CrateId, ParameterKind, TraitId}, + grammar::{AdtId, CrateId, TraitId}, + rust::FormalityLang, }; use crate::grammar::{AdtBoundData, Crate, CrateItem, Enum, Program, Struct, StructBoundData}; +#[derive(Debug)] +pub struct FuzzProgram { + pub num_adts: std::ops::Range, + pub num_traits: std::ops::Range, + pub num_generic_parameters: std::ops::Range, +} + +impl FuzzProgram { + pub fn num_adts(self, r: std::ops::Range) -> Self { + Self { + num_adts: r, + ..self + } + } + + pub fn num_traits(self, r: std::ops::Range) -> Self { + Self { + num_traits: r, + ..self + } + } + + pub fn num_generic_parameters(self, r: std::ops::Range) -> Self { + Self { + num_generic_parameters: r, + ..self + } + } +} + +impl Default for FuzzProgram { + fn default() -> Self { + Self { + num_adts: (0..10), + num_traits: (0..10), + num_generic_parameters: (0..3), + } + } +} + /// Set of items that we wll declare in our program. /// We begin by fuzzing an instance of this type. -#[derive(bolero::TypeGenerator)] +#[derive(bolero::TypeGenerator, Debug)] struct FuzzItems { adts: Vec, traits: Vec, @@ -23,16 +64,16 @@ struct FuzzItems { /// An ADT that will be declared along with its parameter kinds. /// Its name uses a `String` so that the fuzzer can generate arbitrary names. -#[derive(bolero::TypeGenerator)] +#[derive(bolero::TypeGenerator, Debug)] struct FuzzAdt { - arity: Vec, + arity: KindVec, } /// A trait that will be declared along with its parameter kinds. /// Its name uses a `String` so that the fuzzer can generate arbitrary names. -#[derive(bolero::TypeGenerator)] +#[derive(bolero::TypeGenerator, Debug)] struct FuzzTrait { - arity: Vec, + arity: KindVec, } /// The bound data for an ADT definition (fuzzer can choose between struct/enum). @@ -42,10 +83,17 @@ enum FuzzAdtBoundData { Enum(AdtBoundData), } -impl bolero::TypeGenerator for Program { - fn generate(driver: &mut D) -> Option { +impl bolero::ValueGenerator for FuzzProgram { + type Output = Program; + + fn generate(&self, driver: &mut D) -> Option { + let _guard = FormalityLang::fuzz_binder_range().set(self.num_generic_parameters.clone()); + // First we determine what kind of items there will be. - let items: FuzzItems = driver.gen()?; + let items = FuzzItems { + adts: self.generate_fuzz_adts(driver)?, + traits: self.generate_fuzz_trait(driver)?, + }; // Then bring those items into scope for what follows. let _guard = FuzzCx { @@ -53,13 +101,13 @@ impl bolero::TypeGenerator for Program { .adts .into_iter() .zip(0..) - .map(|(f, idx)| (AdtId::new(&format!("Adt{}", idx)), f.arity)) + .map(|(f, idx)| (AdtId::new(&format!("Adt{}", idx)), f.arity.vec)) .collect(), trait_kinds: items .traits .into_iter() .zip(0..) - .map(|(f, idx)| (TraitId::new(&format!("Trait{}", idx)), f.arity)) + .map(|(f, idx)| (TraitId::new(&format!("Trait{}", idx)), f.arity.vec)) .collect(), associated_items: Map::default(), } @@ -105,3 +153,15 @@ impl bolero::TypeGenerator for Program { }) } } + +impl FuzzProgram { + fn generate_fuzz_adts(&self, driver: &mut impl Driver) -> Option> { + let num_adts = self.num_adts.generate(driver)?; + (0..num_adts).map(|_| driver.gen()).collect() + } + + fn generate_fuzz_trait(&self, driver: &mut impl Driver) -> Option> { + let num_traits = self.num_traits.generate(driver)?; + (0..num_traits).map(|_| driver.gen()).collect() + } +} diff --git a/src/lib.rs b/src/lib.rs index 29b50732..723af222 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,12 +10,13 @@ extern crate stable_mir; use std::{path::PathBuf, sync::Arc}; +use bolero::ValueGenerator; use clap::Parser; use formality_check::check_all_crates; use formality_core::Set; use formality_prove::{test_util::TestAssertion, Constraints}; -use formality_rust::grammar::Program; -use formality_types::rust::try_term; +use formality_rust::{fuzz::FuzzProgram, grammar::Program}; +use formality_types::{fuzz::FuzzCx, rust::try_term}; #[cfg(test)] mod test; @@ -77,7 +78,7 @@ fn generate_fuzzed_program(args: &Args) { use bolero::check; check!() - .with_type::() + .with_generator(FuzzProgram::default().num_generic_parameters(0..1)) .with_iterations(args.generate_fuzzed_programs) .for_each(|p| eprintln!("{p:?}")); }