/*
 * Copyright 2018-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#pragma once

#include <type_traits>

#ifndef __has_builtin
#define __has_builtin(x) 0
#endif

// disable buggy compatibility warning about "requires" and "concept" being
// C++20 keywords.
#if defined(__clang__) && !defined(__APPLE__)
#define PUSHMI_PP_IGNORE_CXX2A_COMPAT_BEGIN \
    _Pragma("GCC diagnostic push") \
    _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") \
    _Pragma("GCC diagnostic ignored \"-Wpragmas\"") \
    _Pragma("GCC diagnostic ignored \"-Wc++2a-compat\"") \
    _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") \
    /**/
#define PUSHMI_PP_IGNORE_CXX2A_COMPAT_END \
    _Pragma("GCC diagnostic pop")
#elif defined(__clang__) && defined(__APPLE__)
#define PUSHMI_PP_IGNORE_CXX2A_COMPAT_BEGIN \
    _Pragma("GCC diagnostic push") \
    _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") \
    _Pragma("GCC diagnostic ignored \"-Wpragmas\"") \
    _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") \
    /**/
#define PUSHMI_PP_IGNORE_CXX2A_COMPAT_END \
    _Pragma("GCC diagnostic pop")
#else

#define PUSHMI_PP_IGNORE_CXX2A_COMPAT_BEGIN
#define PUSHMI_PP_IGNORE_CXX2A_COMPAT_END
// #pragma GCC diagnostic push
// #pragma GCC diagnostic ignored "-Wunknown-pragmas"
// #pragma GCC diagnostic ignored "-Wpragmas"
// #pragma GCC diagnostic ignored "-Wc++2a-compat"
#endif

PUSHMI_PP_IGNORE_CXX2A_COMPAT_BEGIN

#if defined(__GNUC__)
#define PUSHMI_NOT_ON_WINDOWS 1
#else
#define PUSHMI_NOT_ON_WINDOWS 0
#endif

#if __cpp_inline_variables >= 201606
#define PUSHMI_INLINE_VAR inline
#else
#define PUSHMI_INLINE_VAR
#endif

#ifdef __clang__
#define PUSHMI_PP_IS_SAME(...) __is_same(__VA_ARGS__)
#elif defined(__GNUC__) && __GNUC__ >= 6 && !defined(__NVCC__)
#define PUSHMI_PP_IS_SAME(...) __is_same_as(__VA_ARGS__)
#else
#define PUSHMI_PP_IS_SAME(...) std::is_same<__VA_ARGS__>::value
#endif

#ifdef __clang__
#define PUSHMI_PP_IS_CONSTRUCTIBLE(...)  __is_constructible(__VA_ARGS__)
#elif defined(__GNUC__) && __GNUC__ >= 8
#define PUSHMI_PP_IS_CONSTRUCTIBLE(...)  __is_constructible(__VA_ARGS__)
#else
#define PUSHMI_PP_IS_CONSTRUCTIBLE(...) std::is_constructible<__VA_ARGS__>::value
#endif

#if __COUNTER__ != __COUNTER__
#define PUSHMI_COUNTER __COUNTER__
#else
#define PUSHMI_COUNTER __LINE__
#endif

#if PUSHMI_NOT_ON_WINDOWS
#define PUSHMI_PP_VA_ARGS_EXPANDER_INNER(...) __VA_ARGS__
#define PUSHMI_PP_VA_ARGS_EXPANDER(...) __VA_ARGS__
#define PUSHMI_PP_VA_ARGS_EXPANDER2_INNER(...) __VA_ARGS__
#define PUSHMI_PP_VA_ARGS_EXPANDER2(...) __VA_ARGS__

#define PUSHMI_PP_ENABLE_IF_INT(...)                                      \
    std::enable_if_t<__VA_ARGS__, int> = 0                                \
    /**/

#define PUSHMI_PP_EVAL(X, ...) X(__VA_ARGS__)
#define PUSHMI_PP_EVAL2(X, ...) X(__VA_ARGS__)

#define PUSHMI_PP_EXPAND(...) __VA_ARGS__
#define PUSHMI_PP_EXPAND2(...) __VA_ARGS__

#define PUSHMI_PP_CHECK(...) PUSHMI_PP_CHECK_N(__VA_ARGS__, 0,)
#define PUSHMI_PP_CHECK_N(x, n, ...) n
#define PUSHMI_PP_PROBE(x) x, 1,
#else
// https://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly
#define PUSHMI_PP_VA_ARGS_EXPANDER_INNER(X) X
#define PUSHMI_PP_VA_ARGS_EXPANDER(X) PUSHMI_PP_VA_ARGS_EXPANDER_INNER(X)
#define PUSHMI_PP_VA_ARGS_EXPANDER2_INNER(X) X
#define PUSHMI_PP_VA_ARGS_EXPANDER2(X) PUSHMI_PP_VA_ARGS_EXPANDER2_INNER(X)

#define PUSHMI_PP_ENABLE_IF_INT(...)                                      \
    decltype(std::declval<std::enable_if_t<__VA_ARGS__>>(), 0) = 0        \
    /**/

#define PUSHMI_PP_EVAL(X, ...) PUSHMI_PP_VA_ARGS_EXPANDER(X(__VA_ARGS__))
#define PUSHMI_PP_EVAL2(X, ...) PUSHMI_PP_VA_ARGS_EXPANDER2(X(__VA_ARGS__))

#define PUSHMI_PP_EXPAND_(...) __VA_ARGS__
#define PUSHMI_PP_EXPAND(...) PUSHMI_PP_VA_ARGS_EXPANDER_INNER(PUSHMI_PP_EXPAND_(__VA_ARGS__))
#define PUSHMI_PP_EXPAND2_(...) __VA_ARGS__
#define PUSHMI_PP_EXPAND2(...) PUSHMI_PP_VA_ARGS_EXPANDER2_INNER(PUSHMI_PP_EXPAND2_(__VA_ARGS__))

#define PUSHMI_PP_CHECK(...) PUSHMI_PP_CHECK_(__VA_ARGS__, 0,)
#define PUSHMI_PP_CHECK_(...) PUSHMI_PP_VA_ARGS_EXPANDER_INNER(PUSHMI_PP_CHECK_N(__VA_ARGS__))
#define PUSHMI_PP_CHECK_N(x, n, ...) n
#define PUSHMI_PP_PROBE(x) x, 1,
#endif

// PUSHMI_CXX_VA_OPT
#ifndef PUSHMI_CXX_VA_OPT
#if __cplusplus > 201703L
#define PUSHMI_CXX_VA_OPT_(...) PUSHMI_PP_CHECK(__VA_OPT__(,) 1)
#define PUSHMI_CXX_VA_OPT PUSHMI_CXX_VA_OPT_(~)
#else
#define PUSHMI_CXX_VA_OPT 0
#endif
#endif // PUSHMI_CXX_VA_OPT

#define PUSHMI_PP_CAT_(X, ...)  X ## __VA_ARGS__
#define PUSHMI_PP_CAT(X, ...)   PUSHMI_PP_CAT_(X, __VA_ARGS__)
#define PUSHMI_PP_CAT2_(X, ...) X ## __VA_ARGS__
#define PUSHMI_PP_CAT2(X, ...)  PUSHMI_PP_CAT2_(X, __VA_ARGS__)

#define PUSHMI_PP_EAT(...)

#define PUSHMI_PP_IS_PAREN(x) PUSHMI_PP_CHECK(PUSHMI_PP_IS_PAREN_PROBE x)
#define PUSHMI_PP_IS_PAREN_PROBE(...) PUSHMI_PP_PROBE(~)

#define PUSHMI_PP_COUNT(...)                                                   \
    PUSHMI_PP_VA_ARGS_EXPANDER(PUSHMI_PP_COUNT_(__VA_ARGS__,                   \
        50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,           \
        30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,           \
        10,9,8,7,6,5,4,3,2,1,))                                                \
        /**/
#define PUSHMI_PP_COUNT_(                                                      \
    _1, _2, _3, _4, _5, _6, _7, _8, _9, _10,                                   \
    _11, _12, _13, _14, _15, _16, _17, _18, _19, _20,                          \
    _21, _22, _23, _24, _25, _26, _27, _28, _29, _30,                          \
    _31, _32, _33, _34, _35, _36, _37, _38, _39, _40,                          \
    _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, N, ...)                  \
    N                                                                          \
    /**/

#define PUSHMI_PP_IIF(BIT) PUSHMI_PP_CAT_(PUSHMI_PP_IIF_, BIT)
#define PUSHMI_PP_IIF_0(TRUE, ...) __VA_ARGS__
#define PUSHMI_PP_IIF_1(TRUE, ...) TRUE

#define PUSHMI_PP_EMPTY()
#define PUSHMI_PP_COMMA() ,
#define PUSHMI_PP_COMMA_IIF(X)                                                 \
    PUSHMI_PP_IIF(X)(PUSHMI_PP_EMPTY, PUSHMI_PP_COMMA)()

#define PUSHMI_CONCEPT_ASSERT(...)                                             \
    static_assert(static_cast<bool>(__VA_ARGS__),                              \
        "Concept assertion failed : " #__VA_ARGS__)

////////////////////////////////////////////////////////////////////////////////
// PUSHMI_CONCEPT_DEF
//   For defining concepts with a syntax symilar to C++20. For example:
//
//     PUSHMI_CONCEPT_DEF(
//         // The Assignable concept from the C++20
//         template(class T, class U)
//         concept Assignable,
//             requires (T t, U &&u) (
//                 t = (U &&) u,
//                 ::folly::pushmi::concepts::requires_<Same<decltype(t = (U &&)
//                 u), T>>
//             ) &&
//             std::is_lvalue_reference_v<T>
//     );
#define PUSHMI_CONCEPT_DEF(DECL, ...)                                          \
    PUSHMI_PP_EVAL(                                                            \
        PUSHMI_PP_DECL_DEF,                                                    \
        PUSHMI_PP_EXPAND(PUSHMI_PP_CAT(PUSHMI_PP_DEF_DECL_, DECL)),            \
        PUSHMI_PP_EXPAND(__VA_ARGS__))                                         \
    /**/
#define PUSHMI_PP_DECL_DEF_NAME(...)                                           \
    PUSHMI_PP_CAT(PUSHMI_PP_DEF_, __VA_ARGS__),                                \
    /**/
#define PUSHMI_PP_DECL_DEF(TPARAM, NAME, ...)                                  \
    PUSHMI_PP_CAT(PUSHMI_PP_DECL_DEF_, PUSHMI_PP_IS_PAREN(NAME))(              \
        TPARAM,                                                                \
        NAME,                                                                  \
        PUSHMI_PP_EXPAND(__VA_ARGS__))                                         \
    /**/
// The defn is of the form:
//   template(class A, class B = void, class... Rest)
//   (concept Name)(A, B, Rest...),
//      // requirements...
#define PUSHMI_PP_DECL_DEF_1(TPARAM, NAME, ...)                                \
    PUSHMI_PP_EVAL2(                                                           \
        PUSHMI_PP_DECL_DEF_IMPL,                                               \
        TPARAM,                                                                \
        PUSHMI_PP_VA_ARGS_EXPANDER2(PUSHMI_PP_DECL_DEF_NAME NAME),             \
        PUSHMI_PP_EXPAND2(__VA_ARGS__))                                        \
    /**/
// The defn is of the form:
//   template(class A, class B)
//   concept Name,
//      // requirements...
// Compute the template arguments (A, B) from the template introducer.
#define PUSHMI_PP_DECL_DEF_0(TPARAM, NAME, ...)                                \
    PUSHMI_PP_DECL_DEF_IMPL(                                                   \
        TPARAM,                                                                \
        PUSHMI_PP_CAT(PUSHMI_PP_DEF_, NAME),                                   \
        (PUSHMI_PP_CAT(PUSHMI_PP_AUX_, TPARAM)),                               \
        PUSHMI_PP_EXPAND(__VA_ARGS__))                                         \
    /**/
// Expand the template definition into a struct and template alias like:
//    struct NameConcept {
//      template<class A, class B>
//      static auto Requires_(/* args (optional)*/) ->
//          decltype(/*requirements...*/);
//      template<class A, class B>
//      static constexpr auto is_satisfied_by(int) ->
//          decltype(bool(&Requires_<A,B>)) { return true; }
//      template<class A, class B>
//      static constexpr bool is_satisfied_by(long) { return false; }
//    };
//    template<class A, class B>
//    inline constexpr bool Name = NameConcept::is_satisfied_by<A, B>(0);
#if __cpp_concepts
// No requires expression
#define PUSHMI_PP_DEF_IMPL_0(...)                                              \
    PUSHMI_PP_EXPAND(__VA_ARGS__)                                              \
    /**/
// Requires expression
#define PUSHMI_PP_DEF_IMPL_1(...)                                              \
    PUSHMI_PP_CAT(PUSHMI_PP_DEF_IMPL_1_, PUSHMI_PP_EXPAND(__VA_ARGS__))        \
    /**/
#define PUSHMI_PP_DEF_IMPL_1_requires                                          \
    requires PUSHMI_PP_DEF_IMPL_1_REQUIRES                                     \
    /**/
#define PUSHMI_PP_DEF_IMPL_1_REQUIRES(...)                                     \
    (PUSHMI_PP_EXPAND(__VA_ARGS__)) PUSHMI_PP_DEF_IMPL_1_REQUIRES_BODY         \
    /**/
#define PUSHMI_PP_DEF_IMPL_1_REQUIRES_BODY(...)                                \
    { __VA_ARGS__; }                                                           \
    /**/
#define PUSHMI_PP_DECL_DEF_IMPL(TPARAM, NAME, ARGS, ...)                       \
    inline namespace _eager_ {                                                 \
        PUSHMI_PP_CAT(PUSHMI_PP_DEF_, TPARAM)                                  \
        concept bool NAME = PUSHMI_PP_DEF_IMPL(                                \
            PUSHMI_PP_EXPAND(__VA_ARGS__),)(                                   \
                PUSHMI_PP_EXPAND(__VA_ARGS__));                                \
    }                                                                          \
    namespace lazy = _eager_;                                                  \
    /**/
#else
// No requires expression:
#define PUSHMI_PP_DEF_IMPL_0(...)                                              \
    () -> decltype(std::enable_if_t<static_cast<bool>(__VA_ARGS__), int>{})    \
    /**/
// Requires expression:
#define PUSHMI_PP_DEF_IMPL_1(...)                                              \
    PUSHMI_PP_CAT(PUSHMI_PP_DEF_IMPL_1_, __VA_ARGS__) ), int>{} )              \
    /**/
#define PUSHMI_PP_DEF_IMPL_1_requires                                          \
    PUSHMI_PP_DEF_IMPL_1_REQUIRES                                              \
    /**/
#define PUSHMI_PP_DEF_IMPL_1_REQUIRES(...)                                     \
    (__VA_ARGS__) -> decltype(std::enable_if_t<bool(                           \
        ::folly::pushmi::concepts::detail::requires_                           \
        PUSHMI_PP_DEF_REQUIRES_BODY                                            \
    /**/
#define PUSHMI_PP_DEF_REQUIRES_BODY(...)                                       \
    <decltype(__VA_ARGS__, void())>()                                          \
    /**/
#define PUSHMI_PP_DECL_DEF_IMPL(TPARAM, NAME, ARGS, ...)                      \
  struct PUSHMI_PP_CAT(NAME, Concept) {                                       \
    using Concept = PUSHMI_PP_CAT(NAME, Concept);                             \
    PUSHMI_PP_IGNORE_CXX2A_COMPAT_BEGIN                                       \
    PUSHMI_PP_CAT(PUSHMI_PP_DEF_, TPARAM)                                     \
    static auto Requires_ PUSHMI_PP_DEF_IMPL(__VA_ARGS__,)(                   \
        __VA_ARGS__) {                                                        \
      return 0;                                                               \
    }                                                                         \
    PUSHMI_PP_IGNORE_CXX2A_COMPAT_END                                         \
    PUSHMI_PP_CAT(PUSHMI_PP_DEF_, TPARAM)                                     \
    struct Eval {                                                             \
      template <class C_ = Concept,                                           \
        decltype(::folly::pushmi::concepts::detail::gcc_bugs(                 \
            &C_::template Requires_<PUSHMI_PP_EXPAND ARGS>)) = nullptr>       \
      static constexpr bool impl(int) noexcept {                              \
        return true;                                                          \
      }                                                                       \
      static constexpr bool impl(long) noexcept {                             \
        return false;                                                         \
      }                                                                       \
      explicit constexpr operator bool() const noexcept {                     \
        return Eval::impl(0);                                                 \
      }                                                                       \
      constexpr auto operator!() const noexcept {                             \
        return ::folly::pushmi::concepts::detail::Not<Eval>{};                \
      }                                                                       \
      template <class That>                                                   \
      constexpr auto operator&&(That) const noexcept {                        \
        return ::folly::pushmi::concepts::detail::And<Eval, That>{};          \
      }                                                                       \
      template <class That>                                                   \
      constexpr auto operator||(That) const noexcept {                        \
        return ::folly::pushmi::concepts::detail::Or<Eval, That>{};           \
      }                                                                       \
    };                                                                        \
  };                                                                          \
  namespace lazy {                                                            \
  PUSHMI_PP_CAT(PUSHMI_PP_DEF_, TPARAM)                                       \
  PUSHMI_INLINE_VAR constexpr auto NAME =                                     \
      PUSHMI_PP_CAT(NAME, Concept)::Eval<PUSHMI_PP_EXPAND ARGS>{};            \
  }                                                                           \
  PUSHMI_PP_CAT(PUSHMI_PP_DEF_, TPARAM)                                       \
  PUSHMI_INLINE_VAR constexpr bool NAME =                                     \
      static_cast<bool>(                                                      \
          PUSHMI_PP_CAT(NAME, Concept)::Eval<PUSHMI_PP_EXPAND ARGS>{})        \
  /**/
#endif

#define PUSHMI_PP_IS_REQUIRES(REQUIRES, ...)                                   \
    PUSHMI_PP_CHECK(PUSHMI_PP_REQUIRES_PROBE(REQUIRES))                        \
    /**/
#define PUSHMI_PP_REQUIRES_PROBE(REQUIRES)                                     \
    PUSHMI_PP_EXPAND2(PUSHMI_PP_CAT(PUSHMI_PP_REQUIRES_PROBE_, REQUIRES))      \
    /**/
#define PUSHMI_PP_REQUIRES_PROBE_requires                                      \
    PUSHMI_PP_PROBE(~)                                                         \
    /**/
#define PUSHMI_PP_DEF_IMPL(REQUIRES, ...)                                      \
    PUSHMI_PP_CAT(                                                             \
        PUSHMI_PP_DEF_IMPL_,                                                   \
        PUSHMI_PP_IS_REQUIRES(REQUIRES))                                       \
    /**/
#define PUSHMI_PP_DEF_DECL_template(...)                                       \
    template(__VA_ARGS__),                                                     \
    /**/
#define PUSHMI_PP_DEF_template(...)                                            \
    template<__VA_ARGS__>                                                      \
    /**/
#define PUSHMI_PP_DEF_concept
#define PUSHMI_PP_DEF_class
#define PUSHMI_PP_DEF_typename
#define PUSHMI_PP_DEF_int
#define PUSHMI_PP_DEF_bool
#define PUSHMI_PP_DEF_size_t
#define PUSHMI_PP_DEF_unsigned
#if PUSHMI_NOT_ON_WINDOWS
#define PUSHMI_PP_AUX_template(...)                                            \
    PUSHMI_PP_CAT2(                                                            \
        PUSHMI_PP_TPARAM_,                                                     \
        PUSHMI_PP_COUNT(__VA_ARGS__))(                                         \
            __VA_ARGS__)                                                       \
    /**/
#else
// MSVC
#define PUSHMI_PP_AUX_template(...)                                            \
    PUSHMI_PP_CAT2(                                                            \
        PUSHMI_PP_TPARAM_,                                                     \
        PUSHMI_PP_COUNT(PUSHMI_PP_EXPAND(__VA_ARGS__)))(                       \
            PUSHMI_PP_EXPAND(__VA_ARGS__))                                     \
    /**/
#endif

// using varargs in these was failing on msvc AND clang
#define PUSHMI_PP_TPARAM_1(_1)                                                 \
    PUSHMI_PP_CAT2(PUSHMI_PP_DEF_, _1)
#define PUSHMI_PP_TPARAM_2(_1, _2)                                             \
    PUSHMI_PP_CAT2(PUSHMI_PP_DEF_, _1), PUSHMI_PP_TPARAM_1(_2)
#define PUSHMI_PP_TPARAM_3(_1, _2, _3)                                         \
    PUSHMI_PP_CAT2(PUSHMI_PP_DEF_, _1), PUSHMI_PP_TPARAM_2(_2, _3)
#define PUSHMI_PP_TPARAM_4(_1, _2, _3, _4)                                     \
    PUSHMI_PP_CAT2(PUSHMI_PP_DEF_, _1), PUSHMI_PP_TPARAM_3(_2, _3, _4)
#define PUSHMI_PP_TPARAM_5(_1, _2, _3, _4, _5)                                 \
    PUSHMI_PP_CAT2(PUSHMI_PP_DEF_, _1), PUSHMI_PP_TPARAM_3(_2, _3, _4, _5)

////////////////////////////////////////////////////////////////////////////////
// PUSHMI_TEMPLATE
// Usage:
//   PUSHMI_TEMPLATE (class A, class B)
//     (requires Concept1<A> && Concept2<B>)
//   void foo(A a, B b)
//   {}
// or
//   PUSHMI_TEMPLATE (class A, class B)
//     (requires requires (expr1, expr2, expr3) && Concept1<A> && Concept2<B>)
//   void foo(A a, B b)
//   {}
#if __cpp_concepts
#define PUSHMI_TEMPLATE(...)                                                   \
    template<__VA_ARGS__> PUSHMI_TEMPLATE_AUX_                                 \
    /**/
#define PUSHMI_TEMPLATE_AUX_(...)                                              \
    PUSHMI_TEMPLATE_AUX_4(PUSHMI_PP_CAT(PUSHMI_TEMPLATE_AUX_3_, __VA_ARGS__))  \
    /**/
#define PUSHMI_TEMPLATE_AUX_3_requires
#define PUSHMI_TEMPLATE_AUX_4(...)                                             \
    PUSHMI_TEMPLATE_AUX_5(__VA_ARGS__)(__VA_ARGS__)                            \
    /**/
#define PUSHMI_TEMPLATE_AUX_5(REQUIRES, ...)                                   \
    PUSHMI_PP_CAT(                                                             \
        PUSHMI_TEMPLATE_AUX_5_,                                                \
        PUSHMI_PP_IS_REQUIRES(REQUIRES))                                       \
    /**/
// No requires expression:
#define PUSHMI_TEMPLATE_AUX_5_0(...)                                           \
    requires __VA_ARGS__                                                       \
    /**/
// Requires expression
#define PUSHMI_TEMPLATE_AUX_5_1(...)                                           \
    PUSHMI_PP_CAT(PUSHMI_TEMPLATE_AUX_6_, __VA_ARGS__)                         \
    /**/
#define PUSHMI_TEMPLATE_AUX_6_requires(...)                                    \
    requires requires { __VA_ARGS__; }
#else
#define PUSHMI_TEMPLATE(...)                                                   \
    template<__VA_ARGS__ PUSHMI_TEMPLATE_AUX_                                  \
    /**/
#define PUSHMI_TEMPLATE_AUX_(...) ,                                            \
    bool PUSHMI_PP_CAT(_pushmi_false_, __LINE__) = false,                      \
    PUSHMI_PP_ENABLE_IF_INT(                                                   \
        static_cast<bool>(PUSHMI_TEMPLATE_AUX_4(PUSHMI_PP_CAT(                 \
            PUSHMI_TEMPLATE_AUX_3_, __VA_ARGS__))) ||                          \
            PUSHMI_PP_CAT(_pushmi_false_, __LINE__))>                          \
    /**/
#define PUSHMI_TEMPLATE_AUX_3_requires
#define PUSHMI_TEMPLATE_AUX_4(...)                                             \
    PUSHMI_TEMPLATE_AUX_5(                                                     \
      PUSHMI_PP_EXPAND(__VA_ARGS__))(PUSHMI_PP_EXPAND(__VA_ARGS__))            \
    /**/
#define PUSHMI_TEMPLATE_AUX_5(REQUIRES, ...)                                   \
    PUSHMI_PP_CAT(                                                             \
        PUSHMI_TEMPLATE_AUX_5_,                                                \
        PUSHMI_PP_IS_REQUIRES(REQUIRES))                                       \
    /**/
// No requires expression:
#define PUSHMI_TEMPLATE_AUX_5_0(...)                                           \
    __VA_ARGS__                                                                \
    /**/
#define PUSHMI_TEMPLATE_AUX_5_1(...)                                           \
    PUSHMI_PP_CAT(PUSHMI_TEMPLATE_AUX_6_, __VA_ARGS__)                         \
    /**/
#define PUSHMI_TEMPLATE_AUX_6_requires(...)                                    \
    ::folly::pushmi::concepts::detail::requires_<decltype(__VA_ARGS__)>()      \
    /**/
#endif

////////////////////////////////////////////////////////////////////////////////
// PUSHMI_TEMPLATE_DEBUG
//  templates declared with this macro can have the concepts compiled out
//  by adding -DPUSHMI_DEBUG_CONCEPTS=1 in build.
//  This is applied to templates that
//    a - have no overloads disambiguated by the concepts checks
//    b - obscure concept checks that provide more information about a compile
//        error
// Usage:
//   PUSHMI_TEMPLATE_DEBUG (class A, class B)
//     (requires Concept1<A> && Concept2<B>)
//   void foo(A a, B b)
//   {}
// or
//   PUSHMI_TEMPLATE_DEBUG (class A, class B)
//     (requires requires (expr1, expr2, expr3) && Concept1<A> && Concept2<B>)
//   void foo(A a, B b)
//   {}
#if PUSHMI_DEBUG_CONCEPTS
#define PUSHMI_TEMPLATE_DEBUG(...)                                             \
    template<__VA_ARGS__> PUSHMI_PP_EAT
#else
#define PUSHMI_TEMPLATE_DEBUG(...)                                             \
    PUSHMI_TEMPLATE(PUSHMI_PP_EXPAND(__VA_ARGS__))                             \
/**/
#endif

#if __cpp_concepts
#define PUSHMI_BROKEN_SUBSUMPTION(...)
#define PUSHMI_TYPE_CONSTRAINT(...) __VA_ARGS__
#define PUSHMI_EXP(...) __VA_ARGS__
#define PUSHMI_AND &&
#else
#define PUSHMI_BROKEN_SUBSUMPTION(...) __VA_ARGS__
#define PUSHMI_TYPE_CONSTRAINT(...) class
#define PUSHMI_EXP(...)  decltype(::folly::pushmi::expAnd(__VA_ARGS__)){}
#define PUSHMI_AND ,
#endif


#if __cpp_concepts
#define PUSHMI_PP_CONSTRAINED_USING(REQUIRES, NAME, ...)                       \
    requires REQUIRES                                                          \
  using NAME __VA_ARGS__                                                       \
  /**/
#else
#define PUSHMI_PP_CONSTRAINED_USING(REQUIRES, NAME, ...)                       \
  using NAME std::enable_if_t<bool(REQUIRES), __VA_ARGS__>                     \
  /**/
#endif

namespace folly {
namespace pushmi {

template <bool B>
using bool_ = std::integral_constant<bool, B>;

namespace concepts {
namespace detail {
template<class T>
T gcc_bugs(T);

template <class>
inline constexpr bool requires_() {
  return true;
}

template <class T, class U>
struct And;
template <class T, class U>
struct Or;

template <class T>
struct Not {
  explicit constexpr operator bool() const noexcept {
    return !static_cast<bool>(T{});
  }
  constexpr auto operator!() const noexcept {
    return T{};
  }
  template <class That>
  constexpr auto operator&&(That) const noexcept {
    return And<Not, That>{};
  }
  template <class That>
  constexpr auto operator||(That) const noexcept {
    return Or<Not, That>{};
  }
};

template <class T, class U>
struct And {
  explicit constexpr operator bool() const noexcept {
    return static_cast<bool>(std::conditional_t<static_cast<bool>(T{}), U, std::false_type>{});
  }
  constexpr auto operator!() const noexcept {
    return Not<And>{};
  }
  template <class That>
  constexpr auto operator&&(That) const noexcept {
    return And<And, That>{};
  }
  template <class That>
  constexpr auto operator||(That) const noexcept {
    return Or<And, That>{};
  }
};

template <class T, class U>
struct Or {
  explicit constexpr operator bool() const noexcept {
    return static_cast<bool>(std::conditional_t<static_cast<bool>(T{}), std::true_type, U>{});
  }
  constexpr auto operator!() const noexcept {
    return Not<Or>{};
  }
  template <class That>
  constexpr auto operator&&(That) const noexcept {
    return And<Or, That>{};
  }
  template <class That>
  constexpr auto operator||(That) const noexcept {
    return Or<Or, That>{};
  }
};
} // namespace detail
} // namespace concepts

namespace isolated {

template <class T0>
constexpr auto expAnd(T0&& t0) {
  return (T0 &&) t0;
}
template <class T0, class... TN>
constexpr auto expAnd(T0&&, TN&&... tn) {
  return concepts::detail::And<T0, decltype(isolated::expAnd((TN &&) tn...))>{};
}

} // namespace isolated

template <class... TN>
constexpr auto expAnd(TN&&... tn) {
  return isolated::expAnd((TN &&) tn...);
}

template <class T>
constexpr bool implicitly_convertible_to(T) {
  return true;
}
#ifdef __clang__
template <bool B>
std::enable_if_t<B> requires_() {}
#else
template <bool B>
PUSHMI_INLINE_VAR constexpr std::enable_if_t<B, int> requires_ = 0;
#endif
} // namespace pushmi
} // namespace folly

PUSHMI_PP_IGNORE_CXX2A_COMPAT_END
