/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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 <cstdint>
#include <limits>
#include <type_traits>
#include <utility>

#include <folly/CPortability.h>
#include <folly/Portability.h>
#include <folly/Traits.h>

namespace folly {

/*
 * FOLLY_DECLVAL(T)
 *
 * This macro works like std::declval<T>() but does the same thing in a way
 * that does not require instantiating a function template.
 *
 * Use this macro instead of std::declval<T>() in places that are widely
 * instantiated to reduce compile-time overhead of instantiating function
 * templates.
 *
 * Note that, like std::declval<T>(), this macro can only be used in
 * unevaluated contexts.
 *
 * There are some small differences between this macro and std::declval<T>().
 * - This macro results in a value of type 'T' instead of 'T&&'.
 * - This macro requires the type T to be a complete type at the
 *   point of use.
 *   If this is a problem then use FOLLY_DECLVAL(T&&) instead, or if T might
 *   be 'void', then use FOLLY_DECLVAL(std::add_rvalue_reference_t<T>).
 */
#define FOLLY_DECLVAL(...) static_cast<__VA_ARGS__ (*)() noexcept>(nullptr)()

namespace detail {
template <typename T>
T decay_1_(T const volatile&&);
template <typename T>
T decay_1_(T const&);
template <typename T>
T* decay_1_(T*);

template <typename T>
auto decay_0_(int) -> decltype(detail::decay_1_(FOLLY_DECLVAL(T&&)));
template <typename T>
auto decay_0_(short) -> void;

template <typename T>
using decay_t = decltype(detail::decay_0_<T>(0));
} // namespace detail

//  decay_t
//
//  Like std::decay_t but possibly faster to compile.
//
//  mimic: std::decay_t, C++14
using detail::decay_t;

/**
 *  copy
 *
 *  Usable when you have a function with two overloads:
 *
 *      class MyData;
 *      void something(MyData&&);
 *      void something(const MyData&);
 *
 *  Where the purpose is to make copies and moves explicit without having to
 *  spell out the full type names - in this case, for copies, to invoke copy
 *  constructors.
 *
 *  When the caller wants to pass a copy of an lvalue, the caller may:
 *
 *      void foo() {
 *        MyData data;
 *        something(folly::copy(data)); // explicit copy
 *        something(std::move(data)); // explicit move
 *        something(data); // const& - neither move nor copy
 *      }
 *
 *  Note: If passed an rvalue, invokes the move-ctor, not the copy-ctor. This
 *  can be used to to force a move, where just using std::move would not:
 *
 *      folly::copy(std::move(data)); // force-move, not just a cast to &&
 *
 *  Note: The following text appears in the standard:
 *
 *      In several places in this Clause the operation //DECAY_COPY(x)// is
 *      used. All such uses mean call the function `decay_copy(x)` and use the
 *      result, where `decay_copy` is defined as follows:
 *
 *        template <class T> decay_t<T> decay_copy(T&& v)
 *          { return std::forward<T>(v); }
 *
 *      http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
 *        30.2.6 `decay_copy` [thread.decaycopy].
 *
 *  We mimic it, with a `noexcept` specifier for good measure.
 */

template <typename T>
constexpr detail::decay_t<T> copy(T&& value) noexcept(
    noexcept(detail::decay_t<T>(static_cast<T&&>(value)))) {
  return static_cast<T&&>(value);
}

//  mimic: forward_like, p0847r0
template <typename Src, typename Dst>
constexpr like_t<Src, Dst>&& forward_like(Dst&& dst) noexcept {
  return std::forward<like_t<Src, Dst>>(static_cast<Dst&&>(dst));
}

/**
 * Initializer lists are a powerful compile time syntax introduced in C++11
 * but due to their often conflicting syntax they are not used by APIs for
 * construction.
 *
 * Further standard conforming compilers *strongly* favor an
 * std::initializer_list overload for construction if one exists.  The
 * following is a simple tag used to disambiguate construction with
 * initializer lists and regular uniform initialization.
 *
 * For example consider the following case
 *
 *  class Something {
 *  public:
 *    explicit Something(int);
 *    Something(std::initializer_list<int>);
 *
 *    operator int();
 *  };
 *
 *  ...
 *  Something something{1}; // SURPRISE!!
 *
 * The last call to instantiate the Something object will go to the
 * initializer_list overload.  Which may be surprising to users.
 *
 * If however this tag was used to disambiguate such construction it would be
 * easy for users to see which construction overload their code was referring
 * to.  For example
 *
 *  class Something {
 *  public:
 *    explicit Something(int);
 *    Something(folly::initlist_construct_t, std::initializer_list<int>);
 *
 *    operator int();
 *  };
 *
 *  ...
 *  Something something_one{1}; // not the initializer_list overload
 *  Something something_two{folly::initlist_construct, {1}}; // correct
 */
struct initlist_construct_t {};
constexpr initlist_construct_t initlist_construct{};

//  sorted_unique_t, sorted_unique
//
//  A generic tag type and value to indicate that some constructor or method
//  accepts a container in which the values are sorted and unique.
//
//  Example:
//
//    void takes_numbers(folly::sorted_unique_t, std::vector<int> alist) {
//      assert(std::is_sorted(alist.begin(), alist.end()));
//      assert(std::unique(alist.begin(), alist.end()) == alist.end());
//      for (i : alist) {
//        // some behavior which safe only when alist is sorted and unique
//      }
//    }
//    void takes_numbers(std::vector<int> alist) {
//      std::sort(alist.begin(), alist.end());
//      alist.erase(std::unique(alist.begin(), alist.end()), alist.end());
//      takes_numbers(folly::sorted_unique, alist);
//    }
//
//  mimic: std::sorted_unique_t, std::sorted_unique, p0429r6
struct sorted_unique_t {};
constexpr sorted_unique_t sorted_unique{};

//  sorted_equivalent_t, sorted_equivalent
//
//  A generic tag type and value to indicate that some constructor or method
//  accepts a container in which the values are sorted but not necessarily
//  unique.
//
//  Example:
//
//    void takes_numbers(folly::sorted_equivalent_t, std::vector<int> alist) {
//      assert(std::is_sorted(alist.begin(), alist.end()));
//      for (i : alist) {
//        // some behavior which safe only when alist is sorted
//      }
//    }
//    void takes_numbers(std::vector<int> alist) {
//      std::sort(alist.begin(), alist.end());
//      takes_numbers(folly::sorted_equivalent, alist);
//    }
//
//  mimic: std::sorted_equivalent_t, std::sorted_equivalent, p0429r6
struct sorted_equivalent_t {};
constexpr sorted_equivalent_t sorted_equivalent{};

template <typename T>
struct transparent : T {
  using is_transparent = void;
  using T::T;
};

/**
 * A simple function object that passes its argument through unchanged.
 *
 * Example:
 *
 *   int i = 42;
 *   int &j = identity(i);
 *   assert(&i == &j);
 *
 * Warning: passing a prvalue through identity turns it into an xvalue,
 * which can effect whether lifetime extension occurs or not. For instance:
 *
 *   auto&& x = std::make_unique<int>(42);
 *   cout << *x ; // OK, x refers to a valid unique_ptr.
 *
 *   auto&& y = identity(std::make_unique<int>(42));
 *   cout << *y ; // ERROR: y did not lifetime-extend the unique_ptr. It
 *                // is no longer valid
 */
struct identity_fn {
  template <class T>
  constexpr T&& operator()(T&& x) const noexcept {
    return static_cast<T&&>(x);
  }
};
using Identity = identity_fn;
inline constexpr identity_fn identity{};

/// literal_string
///
/// A structural type representing a literal string. A structural type may be
/// a non-type template argument.
///
/// May at times be useful since language-level literal strings are not allowed
/// as non-type template arguments.
///
/// This may typically be used with vtag for passing the literal string as a
/// constant-expression via a non-type template argument.
///
/// Example:
///
///   template <size_t N, literal_string<char, N> Str>
///   void do_something_with_literal_string(vtag_t<Str>);
///
///   void do_something() {
///     do_something_with_literal_string(vtag<literal_string{"foobar"}>);
///   }
template <typename C, std::size_t N>
struct literal_string {
  C buffer[N];

  FOLLY_CONSTEVAL /* implicit */ literal_string(C const (&buf)[N]) noexcept {
    for (std::size_t i = 0; i < N; ++i) {
      buffer[i] = buf[i];
    }
  }

  constexpr std::size_t size() const noexcept { return N - 1; }
  constexpr C const* data() const noexcept { return buffer; }
  constexpr C const* c_str() const noexcept { return buffer; }

  template <
      typename String,
      decltype((void(String(FOLLY_DECLVAL(C const*), N - 1)), 0)) = 0>
  constexpr explicit operator String() const //
      noexcept(noexcept(String(FOLLY_DECLVAL(C const*), N - 1))) {
    return String(data(), N - 1);
  }
};

inline namespace literals {
inline namespace string_literals {

#if FOLLY_CPLUSPLUS >= 202002 && !defined(__NVCC__)
template <literal_string Str>
FOLLY_CONSTEVAL decltype(Str) operator""_lit() noexcept {
  return Str;
}
template <literal_string Str>
FOLLY_CONSTEVAL vtag_t<Str> operator""_litv() noexcept {
  return vtag<Str>;
}
#endif

} // namespace string_literals
} // namespace literals

namespace detail {

template <typename T>
struct inheritable_inherit_ : T {
  using T::T;
  template <
      typename... A,
      std::enable_if_t<std::is_constructible<T, A...>::value, int> = 0>
  /* implicit */ FOLLY_ERASE inheritable_inherit_(A&&... a) noexcept(
      noexcept(T(static_cast<A&&>(a)...)))
      : T(static_cast<A&&>(a)...) {}
};

template <typename T>
struct inheritable_contain_ {
  T v;
  template <
      typename... A,
      std::enable_if_t<std::is_constructible<T, A...>::value, int> = 0>
  /* implicit */ FOLLY_ERASE inheritable_contain_(A&&... a) noexcept(
      noexcept(T(static_cast<A&&>(a)...)))
      : v(static_cast<A&&>(a)...) {}
  FOLLY_ERASE operator T&() & noexcept { return v; }
  FOLLY_ERASE operator T&&() && noexcept { return static_cast<T&&>(v); }
  FOLLY_ERASE operator T const&() const& noexcept { return v; }
  FOLLY_ERASE operator T const&&() const&& noexcept {
    return static_cast<T const&&>(v);
  }
};

template <bool>
struct inheritable_;
template <>
struct inheritable_<false> {
  template <typename T>
  using apply = inheritable_inherit_<T>;
};
template <>
struct inheritable_<true> {
  template <typename T>
  using apply = inheritable_contain_<T>;
};

//  inheritable
//
//  A class wrapping an arbitrary type T which is always inheritable, and which
//  enables empty-base-optimization when possible.
template <typename T>
using inheritable =
    typename inheritable_<std::is_final<T>::value>::template apply<T>;

} // namespace detail

// Prevent child classes from finding anything in folly:: by ADL.
namespace moveonly_ {

/**
 * Disallow copy but not move in derived types. This is essentially
 * boost::noncopyable (the implementation is almost identical), except:
 * 1) It doesn't delete move constructor and move assignment.
 * 2) It has public methods, enabling aggregate initialization.
 */
struct MoveOnly {
  constexpr MoveOnly() noexcept = default;
  ~MoveOnly() noexcept = default;

  MoveOnly(MoveOnly&&) noexcept = default;
  MoveOnly& operator=(MoveOnly&&) noexcept = default;
  MoveOnly(const MoveOnly&) = delete;
  MoveOnly& operator=(const MoveOnly&) = delete;
};

/**
 * Disallow copy and move for derived types. This is essentially
 * boost::noncopyable (the implementation is almost identical), except it has
 * public methods, enabling aggregate initialization.
 */
struct NonCopyableNonMovable {
  constexpr NonCopyableNonMovable() noexcept = default;
  ~NonCopyableNonMovable() noexcept = default;

  NonCopyableNonMovable(NonCopyableNonMovable&&) = delete;
  NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete;
  NonCopyableNonMovable(const NonCopyableNonMovable&) = delete;
  NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete;
};

struct Default {};

template <bool Copy, bool Move>
using EnableCopyMove = std::conditional_t<
    Copy,
    Default,
    std::conditional_t<Move, MoveOnly, NonCopyableNonMovable>>;

} // namespace moveonly_

using moveonly_::MoveOnly;
using moveonly_::NonCopyableNonMovable;

/// variadic_noop
/// variadic_noop_fn
///
/// An invocable object and type that has no side-effects - that does nothing
/// when invoked regardless of the arguments with which it is invoked - and that
/// returns void.
///
/// May be invoked with any arguments. Returns void.
struct variadic_noop_fn {
  template <typename... A>
  constexpr void operator()(A&&...) const noexcept {}
};
inline constexpr variadic_noop_fn variadic_noop;

/// variadic_constant_of
/// variadic_constant_of_fn
///
/// An invocable object and type that has no side-effects - that does nothing
/// when invoked regardless of the arguments with which it is invoked - and that
/// returns a constant value.
template <auto Value>
struct variadic_constant_of_fn {
  using value_type = decltype(Value);
  static inline constexpr value_type value = Value;
  template <typename... A>
  constexpr value_type operator()(A&&...) const noexcept {
    return value;
  }
};
template <auto Value>
inline constexpr variadic_constant_of_fn<Value> variadic_constant_of;

//  unsafe_default_initialized
//  unsafe_default_initialized_cv
//
//  An object which is explicitly convertible to any default-constructible type
//  and which, upon conversion, yields a default-initialized value of that type.
//
//  https://en.cppreference.com/w/cpp/language/default_initialization
//
//  For fundamental types, a default-initialized instance may have indeterminate
//  value. Reading an indeterminate value is undefined behavior but may offer a
//  performance optimization. When using an indeterminate value as a performance
//  optimization, it is best to be explicit.
//
//  Useful as an escape hatch when enabling warnings or errors:
//  * gcc:
//    * uninitialized
//    * maybe-uninitialized
//  * clang:
//    * uninitialized
//    * conditional-uninitialized
//    * sometimes-uninitialized
//    * uninitialized-const-reference
//  * msvc:
//    * C4701: potentially uninitialized local variable used
//    * C4703: potentially uninitialized local pointer variable used
//
//  Example:
//
//      int local = folly::unsafe_default_initialized;
//      store_value_into_int_ptr(&value); // suppresses possible warning
//      use_value(value); // suppresses possible warning
struct unsafe_default_initialized_cv {
  FOLLY_PUSH_WARNING
  // MSVC requires warning disables to be outside of function definition
  // Uninitialized local variable 'uninit' used
  FOLLY_MSVC_DISABLE_WARNING(4700)
  // Potentially uninitialized local variable 'uninit' used
  FOLLY_MSVC_DISABLE_WARNING(4701)
  // Potentially uninitialized local pointer variable 'uninit' used
  FOLLY_MSVC_DISABLE_WARNING(4703)
  FOLLY_GNU_DISABLE_WARNING("-Wuninitialized")
  // Clang doesn't implement -Wmaybe-uninitialized and warns about it
  FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
  template <typename T>
  FOLLY_ERASE constexpr /* implicit */ operator T() const noexcept {
#if defined(__cpp_lib_is_constant_evaluated)
#if __cpp_lib_is_constant_evaluated >= 201811L
#if (defined(_MSC_VER) && !defined(__MSVC_RUNTIME_CHECKS)) || \
    (defined(__clang__) && !defined(__GNUC__))
    if (!std::is_constant_evaluated()) {
      T uninit;
      return uninit;
    }
#endif
#endif
#endif
    return T();
  }
  FOLLY_POP_WARNING
};
inline constexpr unsafe_default_initialized_cv unsafe_default_initialized{};

/// to_bool
/// to_bool_fn
///
/// Constructs a boolean from the argument.
///
/// Particularly useful for testing sometimes-weak function declarations. They
/// may be declared weak on some platforms but not on others. GCC likes to warn
/// about them but the warning is unhelpful.
struct to_bool_fn {
  template <typename..., typename T>
  FOLLY_ERASE constexpr auto operator()(T const& t) const noexcept
      -> decltype(static_cast<bool>(t)) {
    FOLLY_PUSH_WARNING
    FOLLY_GCC_DISABLE_WARNING("-Waddress")
    FOLLY_GCC_DISABLE_WARNING("-Wnonnull-compare")
    return static_cast<bool>(t);
    FOLLY_POP_WARNING
  }
};
inline constexpr to_bool_fn to_bool{};

struct to_signed_fn {
  template <typename..., typename T>
  constexpr auto operator()(T const& t) const noexcept ->
      typename std::make_signed<T>::type {
    using S = typename std::make_signed<T>::type;
    // note: static_cast<S>(t) would be more straightforward, but it would also
    // be implementation-defined behavior and that is typically to be avoided;
    // the following code optimized into the same thing, though
    constexpr auto m = static_cast<T>(std::numeric_limits<S>::max());
    return m < t ? -static_cast<S>(~t) + S{-1} : static_cast<S>(t);
  }
};
inline constexpr to_signed_fn to_signed{};

struct to_unsigned_fn {
  template <typename..., typename T>
  constexpr auto operator()(T const& t) const noexcept ->
      typename std::make_unsigned<T>::type {
    using U = typename std::make_unsigned<T>::type;
    return static_cast<U>(t);
  }
};
inline constexpr to_unsigned_fn to_unsigned{};

namespace detail {
template <typename Src, typename Dst>
inline constexpr bool is_to_narrow_convertible_v =
    (std::is_integral<Dst>::value) &&
    (std::is_signed<Dst>::value == std::is_signed<Src>::value);
}

template <typename Src>
class to_narrow_convertible {
  static_assert(std::is_integral<Src>::value, "not an integer");

  template <typename Dst>
  struct to_
      : std::bool_constant<detail::is_to_narrow_convertible_v<Src, Dst>> {};

 public:
  explicit constexpr to_narrow_convertible(Src const& value) noexcept
      : value_(value) {}
  explicit to_narrow_convertible(to_narrow_convertible const&) = default;
  explicit to_narrow_convertible(to_narrow_convertible&&) = default;
  to_narrow_convertible& operator=(to_narrow_convertible const&) = default;
  to_narrow_convertible& operator=(to_narrow_convertible&&) = default;

  template <typename Dst, std::enable_if_t<to_<Dst>::value, int> = 0>
  /* implicit */ constexpr operator Dst() const noexcept {
    FOLLY_PUSH_WARNING
    FOLLY_MSVC_DISABLE_WARNING(4244) // lossy conversion: arguments
    FOLLY_MSVC_DISABLE_WARNING(4267) // lossy conversion: variables
    FOLLY_GNU_DISABLE_WARNING("-Wconversion")
    return value_;
    FOLLY_POP_WARNING
  }

 private:
  Src value_;
};

//  to_narrow
//
//  A utility for performing explicit possibly-narrowing integral conversion
//  without specifying the destination type. Does not permit changing signs.
//  Sometimes preferable to static_cast<Dst>(src) to document the intended
//  semantics of the cast.
//
//  Models explicit conversion with an elided destination type. Sits in between
//  a stricter explicit conversion with a named destination type and a more
//  lenient implicit conversion. Implemented with implicit conversion in order
//  to take advantage of the undefined-behavior sanitizer's inspection of all
//  implicit conversions - it checks for truncation, with suppressions in place
//  for warnings which guard against narrowing implicit conversions.
struct to_narrow_fn {
  template <typename..., typename Src>
  constexpr auto operator()(Src const& src) const noexcept
      -> to_narrow_convertible<Src> {
    return to_narrow_convertible<Src>{src};
  }
};
inline constexpr to_narrow_fn to_narrow{};

template <typename Src>
class to_integral_convertible {
  static_assert(std::is_floating_point<Src>::value, "not a floating-point");

  template <typename Dst>
  static constexpr bool to_ = std::is_integral<Dst>::value;

 public:
  explicit constexpr to_integral_convertible(Src const& value) noexcept
      : value_(value) {}

  explicit to_integral_convertible(to_integral_convertible const&) = default;
  explicit to_integral_convertible(to_integral_convertible&&) = default;
  to_integral_convertible& operator=(to_integral_convertible const&) = default;
  to_integral_convertible& operator=(to_integral_convertible&&) = default;

  template <typename Dst, std::enable_if_t<to_<Dst>, int> = 0>
  /* implicit */ constexpr operator Dst() const noexcept {
    FOLLY_PUSH_WARNING
    FOLLY_MSVC_DISABLE_WARNING(4244) // lossy conversion: arguments
    FOLLY_MSVC_DISABLE_WARNING(4267) // lossy conversion: variables
    FOLLY_GNU_DISABLE_WARNING("-Wconversion")
    return value_;
    FOLLY_POP_WARNING
  }

 private:
  Src value_;
};

//  to_integral
//
//  A utility for performing explicit floating-point-to-integral conversion
//  without specifying the destination type. Sometimes preferable to
//  static_cast<Dst>(src) to document the intended semantics of the cast.
//
//  Models explicit conversion with an elided destination type. Sits in between
//  a stricter explicit conversion with a named destination type and a more
//  lenient implicit conversion. Implemented with implicit conversion in order
//  to take advantage of the undefined-behavior sanitizer's inspection of all
//  implicit conversions.
struct to_integral_fn {
  template <typename..., typename Src>
  constexpr auto operator()(Src const& src) const noexcept
      -> to_integral_convertible<Src> {
    return to_integral_convertible<Src>{src};
  }
};
inline constexpr to_integral_fn to_integral{};

template <typename Src>
class to_floating_point_convertible {
  static_assert(std::is_integral<Src>::value, "not a floating-point");

  template <typename Dst>
  static constexpr bool to_ = std::is_floating_point<Dst>::value;

 public:
  explicit constexpr to_floating_point_convertible(Src const& value) noexcept
      : value_(value) {}

  explicit to_floating_point_convertible(to_floating_point_convertible const&) =
      default;
  explicit to_floating_point_convertible(to_floating_point_convertible&&) =
      default;
  to_floating_point_convertible& operator=(
      to_floating_point_convertible const&) = default;
  to_floating_point_convertible& operator=(to_floating_point_convertible&&) =
      default;

  template <typename Dst, std::enable_if_t<to_<Dst>, int> = 0>
  /* implicit */ constexpr operator Dst() const noexcept {
    FOLLY_PUSH_WARNING
    FOLLY_GNU_DISABLE_WARNING("-Wconversion")
    return value_;
    FOLLY_POP_WARNING
  }

 private:
  Src value_;
};

//  to_floating_point
//
//  A utility for performing explicit integral-to-floating-point conversion
//  without specifying the destination type. Sometimes preferable to
//  static_cast<Dst>(src) to document the intended semantics of the cast.
//
//  Models explicit conversion with an elided destination type. Sits in between
//  a stricter explicit conversion with a named destination type and a more
//  lenient implicit conversion. Implemented with implicit conversion in order
//  to take advantage of the undefined-behavior sanitizer's inspection of all
//  implicit conversions.
struct to_floating_point_fn {
  template <typename..., typename Src>
  constexpr auto operator()(Src const& src) const noexcept
      -> to_floating_point_convertible<Src> {
    return to_floating_point_convertible<Src>{src};
  }
};
inline constexpr to_floating_point_fn to_floating_point{};

struct to_underlying_fn {
  template <typename..., class E>
  constexpr std::underlying_type_t<E> operator()(E e) const noexcept {
    static_assert(std::is_enum<E>::value, "not an enum type");
    return static_cast<std::underlying_type_t<E>>(e);
  }
};
inline constexpr to_underlying_fn to_underlying{};

namespace detail {
template <typename R>
using invocable_to_detect = decltype(FOLLY_DECLVAL(R)());

template <
    typename F,
    //  MSVC 14.16.27023 does not permit these to be in the class body:
    //    error C2833: 'operator decltype' is not a recognized operator or type
    //  TODO: return these to the class body and remove the static assertions
    typename TML = detected_t<invocable_to_detect, F&>,
    typename TCL = detected_t<invocable_to_detect, F const&>,
    typename TMR = detected_t<invocable_to_detect, F&&>,
    typename TCR = detected_t<invocable_to_detect, F const&&>>
class invocable_to_convertible : private inheritable<F> {
 private:
  static_assert(std::is_same<F, decay_t<F>>::value, "mismatch");

  template <typename R>
  using result_t = detected_t<invocable_to_detect, R>;
  template <typename R>
  static constexpr bool detected_v = is_detected_v<invocable_to_detect, R>;
  template <typename R>
  using if_invocable_as_v = std::enable_if_t<detected_v<R>, int>;
  template <typename R>
  static constexpr bool nx_v = noexcept(FOLLY_DECLVAL(R)());
  template <typename G>
  static constexpr bool constructible_v = std::is_constructible<F, G&&>::value;

  using FML = F&;
  using FCL = F const&;
  using FMR = F&&;
  using FCR = F const&&;
  static_assert(std::is_same<TML, result_t<FML>>::value, "mismatch");
  static_assert(std::is_same<TCL, result_t<FCL>>::value, "mismatch");
  static_assert(std::is_same<TMR, result_t<FMR>>::value, "mismatch");
  static_assert(std::is_same<TCR, result_t<FCR>>::value, "mismatch");

 public:
  template <typename G, std::enable_if_t<constructible_v<G&&>, int> = 0>
  FOLLY_ERASE explicit constexpr invocable_to_convertible(G&& g) noexcept(
      noexcept(F(static_cast<G&&>(g))))
      : inheritable<F>(static_cast<G&&>(g)) {}

  template <typename..., typename R = FML, if_invocable_as_v<R> = 0>
  FOLLY_ERASE constexpr operator TML() & noexcept(nx_v<R>) {
    return static_cast<FML>(*this)();
  }
  template <typename..., typename R = FCL, if_invocable_as_v<R> = 0>
  FOLLY_ERASE constexpr operator TCL() const& noexcept(nx_v<R>) {
    return static_cast<FCL>(*this)();
  }
  template <typename..., typename R = FMR, if_invocable_as_v<R> = 0>
  FOLLY_ERASE constexpr operator TMR() && noexcept(nx_v<R>) {
    return static_cast<FMR>(*this)();
  }
  template <typename..., typename R = FCR, if_invocable_as_v<R> = 0>
  FOLLY_ERASE constexpr operator TCR() const&& noexcept(nx_v<R>) {
    return static_cast<FCR>(*this)();
  }
};
} // namespace detail

//  invocable_to
//  invocable_to_fn
//
//  Given an invocable, returns an object which is implicitly convertible to the
//  type which the invocable returns when invoked with no arguments. Conversion
//  invokes the invocables and returns the value.
//
//  The return object has unspecified type with the following semantics:
//  * It stores a decay-copy of the passed invocable.
//  * It defines four-way conversion operators. Each conversion operator purely
//    forwards to the invocable as forwarded-like the convertible, and has the
//    same exception specification and the same participation in overload
//    resolution as invocation of the invocable.
//
//  Example:
//
//    Given a setup:
//
//      struct stable {
//        int value = 0;
//        stable() = default;
//        stable(stable const&); // expensive!
//      };
//      std::list<stable const> list;
//
//    The goal is to insert a stable with a value of 7 to the back of the list.
//
//    The obvious ways are expensive:
//
//      stable obj;
//      obj.value = 7;
//      list.push_back(obj); // or variations with emplace_back or std::move
//
//    With a lambda and copy elision optimization (NRVO), the expense remains:
//
//      list.push_back(std::invoke([] {
//        stable obj;
//        obj.value = 7;
//        return obj;
//      }));
//
//    But conversion, as done with this utility, makes this goal achievable.
//
//      list.emplace_back(folly::invocable_to([] {
//        stable obj;
//        obj.value = 7;
//        return obj;
//      }));
struct invocable_to_fn {
  template <
      typename F,
      typename...,
      typename D = detail::decay_t<F>,
      typename R = detail::invocable_to_convertible<D>,
      std::enable_if_t<std::is_constructible<D, F&&>::value, int> = 0>
  FOLLY_ERASE constexpr R operator()(F&& f) const
      noexcept(noexcept(R(static_cast<F&&>(f)))) {
    return R(static_cast<F&&>(f));
  }
};
inline constexpr invocable_to_fn invocable_to{};
} // namespace folly
