#[rustfmt::skip]
pub use
::safer_ffi::{
    dyn_traits::DynDrop,
    layout::ReprC,
    prelude::{char_p, Out},
}
;

/// Marker trait that has to be implemented for stuff to be usable in types
/// such as `repr_c::Vec`.
#[diagnostic::on_unimplemented(
    note = "\
    This is a marker trait used to distinguish normal, well-defined, non-FFI-opaque types, from \
            the latter, the `extern \"C\" {{ type`s.\n\nIf you are sure this is the case for this \
            pointee type, then you can simply silence this error by manually writing the \
            appropriate:\n\n    impl NonOpaque for Foo {{}}\n",
    label = "Missing `impl NonOpaque for … {{}}`"
)]
pub trait NonOpaque: Sized {}

/// Same as `c_slice`, but for coming with a `NonOpaque` obligation / safeguard.
pub mod c_slice {
    use super::*;

    guarded_type_alias! {
        pub
        type Box<T> where { T : NonOpaque } = ::safer_ffi::prelude::c_slice::Box<T>;
    }

    guarded_type_alias! {
        pub
        type Ref<'lt, T> where { T : 'lt + NonOpaque } = ::safer_ffi::prelude::c_slice::Ref<'lt, T>;
    }
}

impl NonOpaque for char_p::Box {}
impl NonOpaque for char_p::Ref<'_> {}
impl<T: NonOpaque> NonOpaque for ::safer_ffi::prelude::c_slice::Box<T> {}
impl<T: NonOpaque> NonOpaque for ::safer_ffi::prelude::c_slice::Ref<'_, T> {}
impl NonOpaque for i64 {}
impl NonOpaque for u64 {}
impl NonOpaque for usize {}
impl NonOpaque for u8 {}
impl NonOpaque for bool {}
/// Pointer indirection guarantees concreteness.
impl<T> NonOpaque for repr_c::Box<T> where T: FfiDropBox {}
impl<T> NonOpaque for repr_c::Arc<T> where T: FfiDropArc {}

/// Marker trait to express types for which `Option<Self>` has the same layout as `Self`.
/// (thanks to discriminant elision).
pub trait HasNiche {}
impl<T: HasNiche> NonOpaque for Option<T> {}

impl HasNiche for char_p::Box {}
impl HasNiche for char_p::Ref<'_> {}
impl<T: NonOpaque> HasNiche for ::safer_ffi::prelude::c_slice::Box<T> {}
impl<T: NonOpaque> HasNiche for ::safer_ffi::prelude::c_slice::Ref<'_, T> {}

#[diagnostic::on_unimplemented(
    message = "Pointee type may be opaque",
    note = "\
        If the pointee is some concrete type, rather than one defined by the FFI and opaque, then \
            consider `impl`ementing `NonOpaque` for it.\n\nOtherwise, you will need to involve a \
            companion `#[drop(…)] fn` declaration in the `unsafe extern \"C\" {{}}` block."
)]
pub trait FfiDropBox: Sized + ReprC {
    /// # Safety
    ///
    /// Must not be called directly, only in `impl Drop for repr_c::Box<…>`.
    unsafe fn drop_boxed(it: &mut repr_c::Box<Self>);
}

/// The drop glue can be written for concrete types.
impl<T: NonOpaque + ReprC> FfiDropBox for T {
    unsafe fn drop_boxed(it: &mut repr_c::Box<T>) {
        unsafe {
            drop(::std::boxed::Box::from_raw(it.0.ptr.as_mut_ptr()));
        }
    }
}

#[diagnostic::on_unimplemented(
    message = "Pointee type may be opaque",
    note = "\
        If the pointee is some concrete type, rather than one defined by the FFI and opaque, then \
            consider `impl`ementing `NonOpaque` for it.\n\nOtherwise, you will need to involve a \
            companion `#[drop(…)] fn` declaration in the `unsafe extern \"C\" {{}}` block."
)]
pub trait FfiDropArc: Sized + ReprC {
    /// # Safety
    ///
    /// Must not be called directly, only in `impl Drop for repr_c::Arc<…>`.
    unsafe fn drop_arced(it: &mut repr_c::Arc<Self>);
}

/// The drop glue can be written for concrete types.
impl<T: NonOpaque + ReprC> FfiDropArc for T {
    unsafe fn drop_arced(it: &mut repr_c::Arc<T>) {
        unsafe {
            drop(::std::sync::Arc::from_raw(it.0.ptr.as_mut_ptr()));
        }
    }
}

/// Similar to [`::safer_ffi::prelude::repr_c`] but for a different definition
/// of `Box`, to catch mistakes with code attempting to treat an
/// opaque/`extern type` as if it were a well-defined and `Sized` type.
///
/// # Opaque types and FFI
///
/// Also known as `extern type`s. There is a proper proposal for them in Rust,
/// but until that, we have had to polyfill them in this crate.
///
/// The gist of it, and key takeaway, is that an opaque/`extern type`:
///
///   - has an unknown size, so it's technically non-`Sized`,
///   - and yet, the FFI usually handles these behind **thin** pointer indirection.
///
/// And, right now, "thin pointer to a non-`Sized` pointee" is a Rust oxymoron,
/// due to how it failed to account for such a case and hinged on a `Sized <=> ThinPointee`
/// architecture.
///
/// We work around that by having such types, when polyfilled by our
/// `extern_type_polyfill!` macro, to be technically `Sized`, as zero-sized
/// (non-`Copy`) entities: worst case scenario, we'd end up reading or writing 0
/// bytes, so no harm done.
///
/// Right?
///
/// Well, the `Box<ExternType>` is a delicate matter: indeed, the *statically-dispatched*
/// `Drop` glue of `Box<T : Sized>` is defined as follows:
///
/// ```rust, ignore
/// unsafe {
///     <*mut T>::drop_in_place(ptr);
///     ::alloc::alloc::dealloc(ptr.cast(), Layout::new::<T>());
/// }
/// ```
///
///  1. it `drop_in_place()`s the pointee. Since these `extern type`s come with no drop glue, this
///     is just a no-op (but which eventually would lead to a leak of FFI resources)
///  2. it deällocates the ptr itself, promising to the allocator that it is a pointer to data of
///     the given layout, mainly, of the "given" size of `0`! This is wrong, and thus, UB!
///
/// The solution, then, relies on defining a custom kind of `c::Box`:
///
///  1. `c::Box<T> ~ ptr::NonNull<T>`, layout-wise,
///  2. `impl<Pointee> Drop for c::Box<Pointee> {` defined as follows:
///       - if/when the `Pointee` is [`NonOpaque`], by deferring to `drop::<rust::Box<Pointee>>`,
///       - otherwise, the `Pointee` has an obligation to provide us with a method to drop a
///         `c::Box<Pointee>`, _via_ the [`FfiDropBox`] trait.
///
///         This then shall work in conjunction without `extern_type_polyfill!`
///         _declarations_ of `extern type`s, since they will then be expected
///         to declare an accompanying `extern fn dittoffi_free_thingy(_: c::Box<Thingy>);`
///         _declaration_.
///         We tell so to the macro through the
///         `#[drop(to impl FfiDropBox for Thingy)]` annotation on such `fn`.
///
/// Once we have done all that, we have successfully defined a `ThinBox<T>`.
///
/// But, for the sake of "ergonomics", we'd like our good ol'
/// `repr_c::Box<dyn Send + FnMut()>` to keep working.
///
/// Hence the extra `Box<T : ?Sized + PointeeFitForCBox>` helper trait:
///
///   - when `T : Sized`, `T : PointeeFitForCBox<CBoxed = ThinBox<T>>;`
///   - when `T = dyn Send + FnMut(…)`, `T : PointeeFitForCBox<CBoxed = BoxDynFnMutN<…>>`.
pub mod repr_c {
    use ::safer_ffi::layout::ReprC;
    pub use ::safer_ffi::prelude::repr_c::*;

    use super::*;

    guarded_type_alias! {
        /// Same as `repr_c::Vec`, but for coming with a `NonOpaque` obligation / safeguard.
        pub
        type Vec<T> where { T : NonOpaque } = ::safer_ffi::prelude::repr_c::Vec<T>;
    }

    /// "Same" as `repr_c::Box`, in the thin case, but for coming with an `impl Drop` obligation,
    /// and an appropriate override of `Drop`.
    #[::safer_ffi::derive_ReprC]
    #[repr(transparent)]
    pub struct ThinBox<T>
    where
        T: FfiDropBox,
    {
        pub(super) ptr: ::safer_ffi::ptr::NonNullOwned<T>,
    }

    impl<T> Drop for ThinBox<T>
    where
        T: FfiDropBox,
    {
        fn drop(&mut self) {
            unsafe {
                T::drop_boxed(::core::mem::transmute(self));
            }
        }
    }

    unsafe impl<T> Send for ThinBox<T>
    where
        T: FfiDropBox,
        ::std::boxed::Box<T>: Send,
    {
    }

    unsafe impl<T> Sync for ThinBox<T>
    where
        T: FfiDropBox,
        ::std::boxed::Box<T>: Sync,
    {
    }

    /// Needed in order to duck-type `::safer_ffi::prelude::repr_c::Box_`.
    pub use self::Box as Box_;

    /// `c::Box<T>` type.
    ///
    /// It is a new `struct` wrapper type unifying:
    /// - `ThinBox<impl FfiDropBox>`,
    /// - `BoxDynFnMutN<…>`,
    /// - `c_slice::Box<T>`,
    ///
    /// under one single umbrella:
    ///
    /// - `c::Box<impl FfiDropBox>`,
    /// - `c::Box<dyn Send + FnMut(…) -> …>`,
    /// - `c::Box<[T]>`.
    #[::safer_ffi::derive_ReprC]
    #[repr(transparent)]
    pub struct Box<T: ?Sized + PointeeFitForCBox>(pub(super) <T as PointeeFitForCBox>::CBoxed);

    #[diagnostic::on_unimplemented(
        message = "`repr_c::Box<dyn …>` requires `FnMut` rather than `Fn`, alongside `Send` only \
                   (no `Sync`)",
        label = "double-check that this be using `FnMut` rather than `Fn`, alongside `Send` only \
                 (no `Sync`)"
    )]
    /// "Type-level function", defining what a `c::Box<Self>` ought to be, layout-wise.
    pub trait PointeeFitForCBox<Diagnostics = ()> {
        type CBoxed: ReprC;
    }

    // For instance:
    impl<T: Sized + NonOpaque + ReprC> PointeeFitForCBox for [T] {
        // `c::Box<[T]>` ought to be:
        type CBoxed = c_slice::Box<T>;
    }

    // `ThinBox` case:
    impl<T: Sized + FfiDropBox> PointeeFitForCBox for T {
        type CBoxed = ThinBox<T>;
    }

    closure_impls! {
        dyn Send + FnMut() -> R,
        dyn Send + FnMut(A) -> R,
        dyn Send + FnMut(A, B) -> R,
        dyn Send + FnMut(A, B, C) -> R,
        dyn Send + FnMut(A, B, C, D) -> R,
    }
    // where
    #[rustfmt::skip]
    macro_rules! closure_impls {(
        $(
            dyn Send + FnMut($($Arg:ident),*) -> $R:ident,
        )*
    ) => (
        $(
            impl<
                $R : ReprC $(, $Arg : ReprC)*
            >
                PointeeFitForCBox
            for
                dyn 'static + Send + FnMut($($Arg),*) -> $R
            {
                type CBoxed = ::safer_ffi::prelude::repr_c::Box<
                    dyn 'static + Send + FnMut($($Arg),*) -> $R,
                >;
            }

            impl<
                T,
                $R : ReprC $(, $Arg : ReprC)*
            >
                From<::std::boxed::Box<T>>
            for
                Box<dyn 'static + Send + FnMut($($Arg),*) -> $R>
            where
                T : 'static + Send + FnMut($($Arg),*) -> $R,
            {
                fn from(f: ::std::boxed::Box<T>) -> Self {
                    Self(f.into())
                }
            }

            impl<$R : ReprC $(, $Arg : ReprC)*>
                Box<dyn 'static + Send + FnMut($($Arg),*) -> $R>
            {
                #[allow(nonstandard_style)]
                pub fn call(&mut self $(, $Arg: $Arg)*) -> $R {
                    self.0.call($($Arg),*)
                }
            }

            repr_c_ptr_dyn_diagnostics::improve_lack_of_PointeeFitForCBox_impl_diagnostics! {
                dyn Send? + Sync? + Fn?($($Arg),*) -> $R,
            }
        )*
    )}
    use closure_impls;

    impl<T> ::core::ops::Deref for Box<T>
    where
        T: FfiDropBox,
    {
        type Target = T;

        fn deref(self: &'_ Self) -> &'_ T {
            unsafe { self.0.ptr.as_ref() }
        }
    }

    impl<T> ::core::ops::DerefMut for Box<T>
    where
        T: FfiDropBox,
    {
        fn deref_mut(self: &'_ mut Self) -> &'_ mut T {
            unsafe { self.0.ptr.as_mut() }
        }
    }

    impl<T> ::core::ops::Deref for Box<[T]>
    where
        T: NonOpaque + ReprC,
    {
        type Target = [T];

        fn deref(self: &'_ Self) -> &'_ [T] {
            &self.0[..]
        }
    }

    impl<T> ::core::ops::DerefMut for Box<[T]>
    where
        T: NonOpaque + ReprC,
    {
        fn deref_mut(self: &'_ mut Self) -> &'_ mut [T] {
            &mut self.0[..]
        }
    }

    impl<T: ::std::fmt::Debug> ::std::fmt::Debug for Box<T>
    where
        T: FfiDropBox,
    {
        fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
            T::fmt(self, f)
        }
    }

    // ---------------------------------------------------------------------- //

    /// "Same" as `repr_c::Arc`, in the thin case, but for coming with an `impl Drop` obligation,
    /// and an appropriate override of `Drop`.
    #[::safer_ffi::derive_ReprC]
    #[repr(transparent)]
    pub struct ThinArc<T>
    where
        T: FfiDropArc,
    {
        pub(super) ptr: ::safer_ffi::ptr::NonNullOwned<T>,
    }

    impl<T> Drop for ThinArc<T>
    where
        T: FfiDropArc,
    {
        fn drop(&mut self) {
            unsafe {
                T::drop_arced(::core::mem::transmute(self));
            }
        }
    }

    impl<T> Clone for ThinArc<T>
    where
        T: FfiDropArc,
    {
        fn clone(&self) -> Self {
            unsafe {
                ::std::sync::Arc::increment_strong_count(self.ptr.as_ptr());
            }
            Self {
                ptr: ::safer_ffi::ptr::NonNullOwned::from(self.ptr.0),
            }
        }
    }

    unsafe impl<T> Send for ThinArc<T>
    where
        T: FfiDropArc,
        ::std::sync::Arc<T>: Send,
    {
    }

    unsafe impl<T> Sync for ThinArc<T>
    where
        T: FfiDropArc,
        ::std::sync::Arc<T>: Sync,
    {
    }

    /// Needed in order to duck-type `::safer_ffi::prelude::repr_c::Arc_`.
    pub use self::Arc as Arc_;

    /// `c::Arc<T>` type.
    ///
    /// It is a new `struct` wrapper type unifying:
    /// - `ThinArc<impl FfiDropArc>`,
    /// - `ArcDynFnMutN<…>`,
    ///
    /// under one single umbrella:
    ///
    /// - `c::Arc<impl FfiDropArc>`,
    /// - `c::Arc<dyn Send + FnMut(…) -> …>`,
    #[::safer_ffi::derive_ReprC]
    #[repr(transparent)]
    pub struct Arc<T: ?Sized + PointeeFitForCArc>(pub(super) <T as PointeeFitForCArc>::CArced);

    #[diagnostic::on_unimplemented(
        message = "`repr_c::Arc<dyn …>` requires `Fn` rather than `FnMut`, alongside `Send + Sync`",
        label = "double-check that this be using `Fn` rather than `FnMut`, alongside `Send + Sync`"
    )]
    /// "Type-level function", defining what a `c::Arc<Self>` ought to be, layout-wise.
    pub trait PointeeFitForCArc<Diagnostics = ()> {
        type CArced: ReprC;
    }

    // `ThinArc` case:
    impl<T: Sized + FfiDropArc> PointeeFitForCArc for T {
        type CArced = ThinArc<T>;
    }

    arc_closure_impls! {
        dyn Send + Sync + Fn() -> R,
        dyn Send + Sync + Fn(A) -> R,
        dyn Send + Sync + Fn(A, B) -> R,
        dyn Send + Sync + Fn(A, B, C) -> R,
        dyn Send + Sync + Fn(A, B, C, D) -> R,
    }
    // where
    #[rustfmt::skip]
    macro_rules! arc_closure_impls {(
        $(
            dyn Send + Sync + Fn($($Arg:ident),*) -> $R:ident,
        )*
    ) => (
        $(
            impl<
                $R : ReprC $(, $Arg : ReprC)*
            >
                PointeeFitForCArc
            for
                dyn 'static + Send + Sync + Fn($($Arg),*) -> $R
            {
                type CArced = ::safer_ffi::prelude::repr_c::Arc<
                    dyn 'static + Send + Sync + Fn($($Arg),*) -> $R,
                >;
            }

            impl<
                T,
                $R : ReprC $(, $Arg : ReprC)*
            >
                From<::std::sync::Arc<T>>
            for
                Arc<dyn 'static + Send + Sync + Fn($($Arg),*) -> $R>
            where
                T : 'static + Send + Sync + Fn($($Arg),*) -> $R,
            {
                fn from(f: ::std::sync::Arc<T>) -> Self {
                    Self(f.into())
                }
            }

            impl<$R : ReprC $(, $Arg : ReprC)*>
                Arc<dyn 'static + Send + Sync + Fn($($Arg),*) -> $R>
            {
                #[allow(nonstandard_style)]
                pub fn call(&self $(, $Arg: $Arg)*) -> $R {
                    self.0.call($($Arg),*)
                }
            }

            repr_c_ptr_dyn_diagnostics::improve_lack_of_PointeeFitForCArc_impl_diagnostics! {
                dyn Send? + Sync? + FnMut?($($Arg),*) -> $R,
            }
        )*
    )}
    use arc_closure_impls;

    /// Diagnostic tricks (can technically be removed if hard to maintain, but for
    /// now they yield nicer DX):
    mod repr_c_ptr_dyn_diagnostics {
        include!("repr_c_ptr_dyn_diagnostics.rs");
    }

    impl<T> ::core::ops::Deref for Arc<T>
    where
        T: FfiDropArc,
    {
        type Target = T;

        fn deref(self: &'_ Self) -> &'_ T {
            unsafe { self.0.ptr.as_ref() }
        }
    }

    impl<T> ::core::ops::DerefMut for Arc<T>
    where
        T: FfiDropArc,
    {
        fn deref_mut(self: &'_ mut Self) -> &'_ mut T {
            unsafe { self.0.ptr.as_mut() }
        }
    }

    impl<T: ::std::fmt::Debug> ::std::fmt::Debug for Arc<T>
    where
        T: FfiDropArc,
    {
        fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
            T::fmt(self, f)
        }
    }

    impl<T> Clone for repr_c::Arc<T>
    where
        T: FfiDropArc,
    {
        fn clone(&self) -> Self {
            Self { 0: self.0.clone() }
        }
    }

    pub use result::*;
    mod result {
        include!(concat!(env!("CARGO_MANIFEST_DIR"), "/", "result.rs"));
    }
}

#[cfg_attr(all(rustfmt), rustfmt::skip)]
macro_rules! extern_type_polyfill {
    (
        $( #[$attrs:meta] )*
        unsafe extern $($abi:literal)? {
            $($contents:tt)*
        }
    ) => (
        $crate::ffi_utils::extern_type_polyfill! {
            @internal
                [$($attrs)*]
                [$($abi)?]
            $($contents)*
        }
    );

    // extern type case;
    (
        @internal $attrs:tt $abi:tt

        $( #[$ty_attrs:meta] )*
        $pub:vis type $TypeName:ident
            $(:
                $SendOrSync:ident
                $(+ $SendOrSync2:ident)*
                $(+)?
            )?
        ;

        $($rest:tt)*
    ) => (
        $( #[$ty_attrs] )*
        #[::safer_ffi::derive_ReprC]
        #[repr(opaque)]
        $pub
        struct $TypeName {
            _private: [u8; 0],
            __not_send_nor_sync: ::core::marker::PhantomData<*const ()>,
        }

        ::paste::paste! {
            $pub type [<Boxed $TypeName>] =
                $crate::ffi_utils::repr_c::Box<$TypeName>
            ;
        }

        $(
            unsafe impl $SendOrSync for $TypeName {}
            $(
                unsafe impl $SendOrSync2 for $TypeName {}
            )*
        )?

        // This impl never holds since `ExternDropFnAnnotation` never does since
        // it requires a sealed `NeverImplemented` trait.
        impl $crate::ffi_utils::NonOpaque for $TypeName
        where
            for<'_never_true> // <- this allows using a clause which is trivially `false`
                              //    / always unsatisfiable.
                              //    Here, this is useful so that if an `FfiDrop` obligation
                              //    appears for `$TypeName`, and the companion `#[drop] fn` declaration
                              //    is missing, Rust will fall back to looking for `NonOpaque`, and
                              //    complain that *that* is not implemented, which was resulting
                              //    in a very confusing or even misleading error message.
                              //
                              //    Now, thanks to this, it will, in turn, complain about the
                              //    missing `ExternDropFnAnnotation`, and we have appropriately
                              //    tweaked the `#[diagnostic::]`s of that `trait` to explain
                              //    the situation and nudge the users towards the proper solution.
                $TypeName : $crate::ffi_utils::diagnostic_hack::ExternDropFnAnnotation
            ,
        {}

        $crate::ffi_utils::extern_type_polyfill! {
            @internal $attrs $abi
            $($rest)*
        }
    );

    // FfiDropBox drop case
    (
        @internal $attrs:tt $abi:tt

        #[drop(to impl FfiDropBox for $T:ty)]
        $pub:vis fn $fname:ident (
            $arg_name:tt : $BoxedTy:ty $(,)?
        );

        $($rest:tt)*
    ) => (
        $crate::ffi_utils::extern_type_polyfill! {
            @internal $attrs $abi

            /* `#[drop]` stripped */
            $pub fn $fname (
                $arg_name : $BoxedTy,
            );

            $($rest)*
        }

        impl $crate::ffi_utils::FfiDropBox for $T {
            unsafe
            fn drop_boxed (it: &'_ mut $BoxedTy)
            {
                unsafe {
                    $fname(<*const $BoxedTy>::read(it))
                }
            }
        }
    );

    // FfiDropArc drop case
    (
        @internal $attrs:tt $abi:tt

        #[drop(to impl FfiDropArc for $T:ty)]
        $pub:vis fn $fname:ident (
            $arg_name:tt : $ArcedTy:ty $(,)?
        );

        $($rest:tt)*
    ) => (
        $crate::ffi_utils::extern_type_polyfill! {
            @internal $attrs $abi

            /* `#[drop]` stripped */
            $pub fn $fname (
                $arg_name : $ArcedTy,
            );

            $($rest)*
        }

        impl $crate::ffi_utils::FfiDropArc for $T {
            unsafe
            fn drop_arced (it: &'_ mut $ArcedTy)
            {
                unsafe {
                    $fname(<*const $ArcedTy>::read(it))
                }
            }
        }
    );

    // Default case
    (
        @internal [$($attrs:tt)*] [$($abi:literal)?]

        $(#[$fn_attrs:meta])*
        $pub:vis
        $(unsafe $(@$if_unsafe:tt)?)?
        fn $fname:ident $(< $($a:lifetime $(: $b:lifetime)?),* >)? (
            $($arg_name:ident : $ArgTy:ty),* $(,)?
        ) $(-> $Ret:ty)?;

        $($rest:tt)*
    ) => (
        $(#[$fn_attrs])*
        #[inline]
        $pub
        $($($if_unsafe)?
            unsafe
        )?
        fn $fname $(< $($a $(: $b)?),* >)? (
            $($arg_name : $ArgTy),*
        ) $(-> $Ret)?
        {
            $( #[$attrs] )*
            #[allow(improper_ctypes)]
            extern $($abi)? {
                $(#[$fn_attrs])*
                $pub
                fn $fname $(< $($a $(: $b)?),* >)? (
                    $($arg_name : $ArgTy),*
                ) $(-> $Ret)?
                ;
            }
            unsafe {
                $fname($($arg_name),*)
            }
        }

        $crate::ffi_utils::extern_type_polyfill! {
            @internal [$($attrs)*] [$($abi)?]
            $($rest)*
        }
    );

    (
        @internal $attrs:tt $abi:tt
        /* Nothing left to parse */
    ) => (
        /* Done. */
    );
}
pub(crate) use extern_type_polyfill;

#[cfg_attr(all(rustfmt), rustfmt::skip)]
macro_rules! guarded_type_alias {
    (
        $( #[doc = $doc:tt] )*
        $pub:vis
        type $TypeName:ident [$($generics:tt)*]
        where {
            $($where_clauses:tt)*
        } = $T:ty;
    ) => (::paste::paste! {
        #[cfg(not(doc))]
        #[allow(nonstandard_style)]
        $pub
        trait [<__ChecksForTyAlias__$TypeName>] <$($generics)*>
        where
            $($where_clauses)*
        {
            type T : ?Sized;
        }

        #[cfg(not(doc))]
        impl<$($generics)*>
            [<__ChecksForTyAlias__$TypeName>] <$($generics)*>
        for
            ()
        where
            $($where_clauses)*
        {
            type T = $T;
        }

        #[cfg(not(doc))]
        $pub type $TypeName<$($generics)*> =
            <
                ()
                as
                [<__ChecksForTyAlias__$TypeName>] <$($generics)*>
            >::T
        ;

        $( #[doc = $doc] )*
        #[cfg(doc)]
        $pub type $TypeName<$($generics)*> where $($where_clauses)* = $T;

    });

    (
        $( #[doc = $doc:expr] )*
        $pub:vis type $TypeName:ident
        <
            $($lt:lifetime),*
            $(,)?
            $($T:ident),*
        > $($rest:tt)*
    ) => (
        $crate::ffi_utils::guarded_type_alias! {
            $(#[doc = $doc])*
            $pub type $TypeName [$($lt ,)* $($T),*] $($rest)*
        }
    );
}
pub(crate) use guarded_type_alias;

/// Defines a `#[repr(i32)] enum Foo { Bar, Baz }` as a
/// `struct Foo { discriminant: i32 }` with `Foo::Bar, Foo::Baz` as `const`
/// aliases of `Foo { discriminant: Bar as _ }`, _etc._
///
/// This properly models the non-exhaustive nature, **AT THE ABI-LEVEL**, of
/// these FFI C `enum`s.
#[cfg_attr(all(rustfmt), rustfmt::skip)]
macro_rules! non_exhaustive_ffi_enum {(
    #[repr($int:tt)]
    $( #$attr:tt )*
    $pub:vis
    enum $EnumName:ident {
        $(
            $( #[doc $($doc:tt)*] )*
            $Variant:ident $(= $value:expr)?
        ),* $(,)?
    }
) => (
    #[repr(transparent)]
    $( #$attr )*
    $pub
    struct $EnumName {
        discriminant: $int,
    }

    #[allow(warnings, clippy::all)]
    const _: () = {
        #[derive(Debug)]
        #[repr($int)]
        enum Discriminants {
            $(
                $Variant $(= $value)?
            ),*
        }

        impl $EnumName {
            $(
                $( #[doc $($doc)*] )*
                $pub const $Variant: Self = Self {
                    discriminant: Discriminants::$Variant as _,
                };
            )*
        }

        impl ::core::fmt::Debug for $EnumName {
            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>)
              -> ::core::fmt::Result
            {
                match *self {
                $(
                    | Self::$Variant => Discriminants::$Variant.fmt(f),
                )*
                    | Self { discriminant: unknown_discriminant } => {
                        #[::core::prelude::v1::derive(::core::fmt::Debug)]
                        struct $EnumName {
                            unknown_discriminant: $int,
                        }

                        $EnumName { unknown_discriminant }.fmt(f)
                    }
                }
            }
        }
    };
)}
pub(crate) use non_exhaustive_ffi_enum;

pub mod diagnostic_hack {
    mod seal {
        pub trait NeverImplemented {}
    }

    #[rustfmt::skip]
    #[diagnostic::on_unimplemented(
        message = "\
            The pointee type is missing its companion `#[drop(…)] fn …;` declaration.",
        note = "\
`extern type`s have no known size or statically-dispatchable `drop_in_place()` logic, \
which is otherwise ordinarily the case, and the reason `drop::<Box<T>>()` is \
well-defined.

Since it is not the case here, we need to manually tell Rust how \"an owned pointed to \
a `T`\" (a `c::Box<T>`) is to be dropped.

This is done through the `FfiDropBox` trait, which is automagically defined and \
implemented for `extern type`s which have the following kind of companion annotation:

    #[apply(extern_type_polyfill!)]
    extern \"C\" {{
        …

        type Foo : …;

        #[drop(to impl FfiDropBox for Foo)]
        fn dittoffi_current_type_free(_: c::Box<Foo>);

        …
    }}
\
        ",
    )]
    pub trait ExternDropFnAnnotation: Sized + seal::NeverImplemented {}
}
