// SPDX-License-Identifier: CC0-1.0
// SPDX-FileCopyrightText: Authored by Tsukasa OI <floss_ssdeep@irq.a4lg.com> in 2023


/// "Optionally" unsafe block.
///
/// When this crate is built with the `unsafe` feature, this macro is
/// expanded to an `unsafe` block.
///
/// Inside this block, you may place statements that may change the behavior
/// depending on the feature `unstable`.  For instance, you may place
/// [`invariant!`] inside this block.
///
/// ```ignore
/// # // Because this is an internal macro, we must ignore on the doctest
/// # // because each Rust doctest's scope is external to this crate.
/// // INTERNAL USE (INSIDE THIS CRATE) ONLY
/// // let index: usize = ... (but proven to be inside the array).
/// # let index = 3usize;
/// let array = [0, 1, 2, 3];
/// optionally_unsafe! {
///     invariant!(index < array.len());
/// }
/// // Bound checking may be optimized out.
/// let result = array[index];
/// ```
///
/// An advanced usage can be found on
/// the [`crate::generate::Generator::update`] implementation
/// (the `generator_update_template!` macro).
#[doc(alias = "optionally_unsafe")]
macro_rules! optionally_unsafe_impl {
    {$($tokens: tt)*} => {
        cfg_if::cfg_if! {
            if #[cfg(feature = "unsafe")] {
                unsafe { $($tokens)* }
            }
            else {
                { $($tokens)* }
            }
        }
    };
}


/// Declare an invariant for optimization.
///
/// When feature `unsafe` is disabled, it only places [`debug_assert!`].
/// If both `unsafe` and `nightly` are enabled, [`core::intrinsics::assume`] is
/// used (which requires `core_intrinsics` Rust unstable feature).
/// If only `unsafe` is enabled, [`core::hint::unreachable_unchecked`] is used.
///
/// If `unsafe` and `nightly` are enabled, enable unstable `core_intrinsics`
/// feature.
///
/// The difference is, since `unsafe` (without `nightly`) implementation uses
/// plain `if` statement, non-intuitive expression may appear in the code (on
/// the other hand, [`core::intrinsics::assume`] guarantees that it does not
/// emit any code).
///
/// Use this macro along with [`optionally_unsafe!`].
#[doc(alias = "invariant")]
macro_rules! invariant_impl {
    ($expr: expr) => {
        cfg_if::cfg_if! {
            if #[cfg(all(feature = "unsafe", feature = "nightly"))] {
                core::intrinsics::assume($expr);
            }
            else if #[cfg(feature = "unsafe")] {
                if !($expr) {
                    core::hint::unreachable_unchecked();
                }
            }
            else {
                debug_assert!($expr);
            }
        }
    };
}


pub(crate) use optionally_unsafe_impl as optionally_unsafe;
pub(crate) use invariant_impl as invariant;
