diff --git a/qadapt-macro/src/lib.rs b/qadapt-macro/src/lib.rs index fbcc611..56b218a 100644 --- a/qadapt-macro/src/lib.rs +++ b/qadapt-macro/src/lib.rs @@ -20,6 +20,31 @@ type G = proc_macro::Group; type I = proc_macro::Ident; type P = proc_macro::Punct; +fn release_guard(fn_name: &str) -> TokenStream { + let rel = I::new("release", Span::call_site()); + let not_rel: Vec = vec![ + I::new("not", Span::call_site()).into(), + G::new(Delimiter::Parenthesis, TokenTree::Ident(rel).into()).into() + ]; + let cfg_not_rel: Vec = vec![ + I::new("cfg", Span::call_site()).into(), + G::new(Delimiter::Parenthesis, TS::from_iter(not_rel.into_iter())).into() + ]; + let guarded: Vec = vec![ + P::new('#', Spacing::Alone).into(), + G::new(Delimiter::Bracket, TS::from_iter(cfg_not_rel.into_iter())).into(), + P::new(':', Spacing::Joint).into(), + P::new(':', Spacing::Alone).into(), + I::new("qadapt", Span::call_site()).into(), + P::new(':', Spacing::Joint).into(), + P::new(':', Spacing::Alone).into(), + I::new(fn_name, Span::call_site()).into(), + G::new(Delimiter::Parenthesis, TokenStream::new()).into(), + ]; + + TS::from_iter(guarded.into_iter()) +} + fn protected_body(fn_name: &str, args: G) -> TokenTree { let mut args_filtered = Vec::new(); let mut waiting_for_comma = false; @@ -40,30 +65,16 @@ fn protected_body(fn_name: &str, args: G) -> TokenTree { } } let args_group = G::new(Delimiter::Parenthesis, TS::from_iter(args_filtered)); - + let tt: Vec = vec![ - P::new(':', Spacing::Joint).into(), - P::new(':', Spacing::Alone).into(), - I::new("qadapt", Span::call_site()).into(), - P::new(':', Spacing::Joint).into(), - P::new(':', Spacing::Alone).into(), - I::new("enter_protected", Span::call_site()).into(), - G::new(Delimiter::Parenthesis, TokenStream::new()).into(), - P::new(';', Spacing::Alone).into(), + G::new(Delimiter::Brace, release_guard("enter_protected")).into(), I::new("let", Span::call_site()).into(), I::new("__ret__", Span::call_site()).into(), P::new('=', Spacing::Alone).into(), I::new(fn_name, Span::call_site()).into(), args_group.into(), P::new(';', Spacing::Alone).into(), - P::new(':', Spacing::Joint).into(), - P::new(':', Spacing::Alone).into(), - I::new("qadapt", Span::call_site()).into(), - P::new(':', Spacing::Joint).into(), - P::new(':', Spacing::Alone).into(), - I::new("exit_protected", Span::call_site()).into(), - G::new(Delimiter::Parenthesis, TokenStream::new()).into(), - P::new(';', Spacing::Alone).into(), + G::new(Delimiter::Brace, release_guard("exit_protected")).into(), I::new("__ret__", Span::call_site()).into(), ]; diff --git a/src/lib.rs b/src/lib.rs index 1f1fe2f..9410835 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,12 +3,8 @@ //! This allocator is a helper for writing high-performance code that is allocation/drop free; //! for functions annotated with `#[allocate_panic]`, QADAPT will detect when allocations/drops //! happen during their execution (and execution of any functions they call) and throw a -//! thread panic if this occurs. -//! -//! Because QADAPT panics on allocation and is rather slow (for an allocator) it is **strongly** -//! recommended that QADAPT (the allocator) be used only in code tests. Functions annotated with -//! `#[allocate_panic]` will have no side effects if the QADAPT allocator is not being used, -//! so the attribute is safe to leave everywhere. +//! thread panic if this occurs. QADAPT-related code is stripped out during release builds, +//! so no worries about random allocations crashing in production. //! //! Currently this crate is Nightly-only, but will work once `const fn` is in Stable. //! diff --git a/tests/allocations.rs b/tests/allocations.rs index f5d3061..1547832 100644 --- a/tests/allocations.rs +++ b/tests/allocations.rs @@ -3,6 +3,7 @@ extern crate qadapt; use qadapt::enter_protected; use qadapt::exit_protected; +use qadapt::protection_level; use qadapt::QADAPT; #[global_allocator] @@ -59,6 +60,11 @@ fn test_vec_push() { let mut v = Vec::new(); enter_protected(); v.push(0); + // We don't make it here in debug mode, but in release mode, + // pushing one element doesn't trigger an allocation. Instead, + // we use a box to force it onto the heap + assert_eq!(protection_level(), 1); + let _b = Box::new(v); } #[test] @@ -89,7 +95,12 @@ fn test_vec_new() { #[should_panic] fn test_vec_with_one() { enter_protected(); - let _v: Vec = Vec::with_capacity(1); + let v: Vec = Vec::with_capacity(1); + // We don't make it here in debug mode, but in release mode, + // pushing one element doesn't trigger an allocation. Instead, + // we use a box to force it onto the heap + assert_eq!(protection_level(), 1); + let _b = Box::new(v); } #[test] diff --git a/tests/macros.rs b/tests/macros.rs index 3cd214a..cd5d252 100644 --- a/tests/macros.rs +++ b/tests/macros.rs @@ -7,64 +7,58 @@ use qadapt::QADAPT; #[global_allocator] static Q: QADAPT = QADAPT; -#[allocate_panic] -fn allocates() { - assert_eq!(::qadapt::protection_level(), 1); - // Without boxing, release profile can actually optimize out the allocation - let mut v = Box::new(Vec::new()); - v.push(1); -} - #[allocate_panic] fn no_allocate() { + #[cfg(not(release))] + { + let _v = 0; + } assert_eq!(::qadapt::protection_level(), 1); let _v: Vec<()> = Vec::with_capacity(0); } -#[allocate_panic] -fn no_allocate_ret() -> bool { - return true; -} - -#[allocate_panic] -fn no_allocate_implicit_ret() -> bool { - true -} - -#[allocate_panic] -fn no_allocate_arg(b: bool) -> bool { - b -} - -#[allocate_panic] -fn no_allocate_args(_b: bool, _u: usize, i: i64) -> i64 { - i -} - -#[allocate_panic] -fn return_result(r: Result) -> Result, ()> { - Ok(r) -} - #[test] fn macro_no_allocate() { no_allocate(); } +#[allocate_panic] +fn allocates() { + assert_eq!(::qadapt::protection_level(), 1); + // Without boxing, release profile can actually optimize out the allocation + let mut v = Box::new(Vec::new()); + v.push(1); +} + #[test] #[should_panic] fn macro_allocates() { allocates(); } +#[allocate_panic] +fn no_allocate_ret() -> bool { + return true; +} + #[test] fn macro_return() { assert!(no_allocate_ret()); } +#[allocate_panic] +fn no_allocate_implicit_ret() -> bool { + true +} + #[test] fn macro_implicit_return() { - assert!(no_allocate_ret()); + assert!(no_allocate_implicit_ret()); +} + +#[allocate_panic] +fn no_allocate_arg(b: bool) -> bool { + b } #[test] @@ -73,13 +67,23 @@ fn macro_allocate_arg() { no_allocate_arg(false); } +#[allocate_panic] +fn no_allocate_args(_b: bool, _u: usize, i: i64) -> i64 { + i +} + #[test] fn macro_allocate_args() { no_allocate_args(true, 0, -1); no_allocate_args(false, 4, -90); } +#[allocate_panic] +fn return_result(r: Result) -> Result, ()> { + Ok(r) +} + #[test] fn macro_return_result() { return_result(Ok(16)).unwrap().unwrap(); -} \ No newline at end of file +}