// FCP guest_std (rexglue/UPSTREAM.md, change 140): the part of <type_traits> the recompiled code uses, on clang
// builtins. See README.md.
#pragma once

#include <cstddef>

namespace std {

template <class T, T v>
struct integral_constant {
  static constexpr T value = v;
  using value_type = T;
  using type = integral_constant;
  constexpr operator value_type() const noexcept { return value; }
  constexpr value_type operator()() const noexcept { return value; }
};
template <bool B>
using bool_constant = integral_constant<bool, B>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;

template <bool B, class T = void>
struct enable_if {};
template <class T>
struct enable_if<true, T> {
  using type = T;
};
template <bool B, class T = void>
using enable_if_t = typename enable_if<B, T>::type;

template <bool B, class T, class F>
struct conditional {
  using type = T;
};
template <class T, class F>
struct conditional<false, T, F> {
  using type = F;
};
template <bool B, class T, class F>
using conditional_t = typename conditional<B, T, F>::type;

template <class T>
struct type_identity {
  using type = T;
};
template <class T>
using type_identity_t = typename type_identity<T>::type;

template <class T>
struct remove_cv {
  using type = __remove_cv(T);
};
template <class T>
using remove_cv_t = __remove_cv(T);
template <class T>
struct remove_const {
  using type = __remove_const(T);
};
template <class T>
using remove_const_t = __remove_const(T);
template <class T>
struct remove_volatile {
  using type = __remove_volatile(T);
};
template <class T>
using remove_volatile_t = __remove_volatile(T);
template <class T>
struct remove_reference {
  using type = __remove_reference_t(T);
};
template <class T>
using remove_reference_t = __remove_reference_t(T);
template <class T>
struct remove_cvref {
  using type = __remove_cvref(T);
};
template <class T>
using remove_cvref_t = __remove_cvref(T);
template <class T>
struct remove_pointer {
  using type = __remove_pointer(T);
};
template <class T>
using remove_pointer_t = __remove_pointer(T);
template <class T>
struct remove_extent {
  using type = __remove_extent(T);
};
template <class T>
using remove_extent_t = __remove_extent(T);
template <class T>
struct remove_all_extents {
  using type = __remove_all_extents(T);
};
template <class T>
using remove_all_extents_t = __remove_all_extents(T);
template <class T>
struct add_const {
  using type = const T;
};
template <class T>
using add_const_t = const T;
template <class T>
struct add_pointer {
  using type = __add_pointer(T);
};
template <class T>
using add_pointer_t = __add_pointer(T);
template <class T>
struct add_lvalue_reference {
  using type = __add_lvalue_reference(T);
};
template <class T>
using add_lvalue_reference_t = __add_lvalue_reference(T);
template <class T>
struct add_rvalue_reference {
  using type = __add_rvalue_reference(T);
};
template <class T>
using add_rvalue_reference_t = __add_rvalue_reference(T);
template <class T>
struct decay {
  using type = __decay(T);
};
template <class T>
using decay_t = __decay(T);
template <class T>
struct make_unsigned {
  using type = __make_unsigned(T);
};
template <class T>
using make_unsigned_t = __make_unsigned(T);
template <class T>
struct make_signed {
  using type = __make_signed(T);
};
template <class T>
using make_signed_t = __make_signed(T);
template <class T>
struct underlying_type {
  using type = __underlying_type(T);
};
template <class T>
using underlying_type_t = __underlying_type(T);

template <class T>
add_rvalue_reference_t<T> declval() noexcept;

#define FCP_STD_TRAIT(name, expr)                         \
  template <class T>                                      \
  struct name : bool_constant<expr> {};                   \
  template <class T>                                      \
  inline constexpr bool name##_v = expr;

FCP_STD_TRAIT(is_void, __is_same(__remove_cv(T), void))
FCP_STD_TRAIT(is_null_pointer, __is_same(__remove_cv(T), decltype(nullptr)))
FCP_STD_TRAIT(is_integral, __is_integral(T))
FCP_STD_TRAIT(is_floating_point, __is_floating_point(T))
FCP_STD_TRAIT(is_arithmetic, __is_arithmetic(T))
FCP_STD_TRAIT(is_fundamental, __is_fundamental(T))
FCP_STD_TRAIT(is_scalar, __is_scalar(T))
FCP_STD_TRAIT(is_object, __is_object(T))
FCP_STD_TRAIT(is_compound, __is_compound(T))
FCP_STD_TRAIT(is_array, __is_array(T))
FCP_STD_TRAIT(is_pointer, __is_pointer(T))
FCP_STD_TRAIT(is_lvalue_reference, __is_lvalue_reference(T))
FCP_STD_TRAIT(is_rvalue_reference, __is_rvalue_reference(T))
FCP_STD_TRAIT(is_reference, __is_reference(T))
FCP_STD_TRAIT(is_member_pointer, __is_member_pointer(T))
FCP_STD_TRAIT(is_enum, __is_enum(T))
FCP_STD_TRAIT(is_union, __is_union(T))
FCP_STD_TRAIT(is_class, __is_class(T))
FCP_STD_TRAIT(is_function, __is_function(T))
FCP_STD_TRAIT(is_const, __is_const(T))
FCP_STD_TRAIT(is_volatile, __is_volatile(T))
FCP_STD_TRAIT(is_trivial, __is_trivial(T))
FCP_STD_TRAIT(is_trivially_copyable, __is_trivially_copyable(T))
FCP_STD_TRAIT(is_standard_layout, __is_standard_layout(T))
FCP_STD_TRAIT(is_empty, __is_empty(T))
FCP_STD_TRAIT(is_polymorphic, __is_polymorphic(T))
FCP_STD_TRAIT(is_abstract, __is_abstract(T))
FCP_STD_TRAIT(is_final, __is_final(T))
FCP_STD_TRAIT(is_aggregate, __is_aggregate(T))
FCP_STD_TRAIT(is_signed, __is_signed(T))
FCP_STD_TRAIT(is_unsigned, __is_unsigned(T))
FCP_STD_TRAIT(is_trivially_destructible, __is_trivially_destructible(T))
FCP_STD_TRAIT(is_destructible, __is_destructible(T))
FCP_STD_TRAIT(is_nothrow_destructible, __is_nothrow_destructible(T))
FCP_STD_TRAIT(is_default_constructible, __is_constructible(T))
FCP_STD_TRAIT(is_trivially_default_constructible, __is_trivially_constructible(T))
FCP_STD_TRAIT(is_nothrow_default_constructible, __is_nothrow_constructible(T))
FCP_STD_TRAIT(is_copy_constructible, __is_constructible(T, __add_lvalue_reference(const T)))
FCP_STD_TRAIT(is_move_constructible, __is_constructible(T, __add_rvalue_reference(T)))
FCP_STD_TRAIT(is_nothrow_move_constructible, __is_nothrow_constructible(T, __add_rvalue_reference(T)))
FCP_STD_TRAIT(is_trivially_copy_constructible,
              __is_trivially_constructible(T, __add_lvalue_reference(const T)))
FCP_STD_TRAIT(is_trivially_move_constructible,
              __is_trivially_constructible(T, __add_rvalue_reference(T)))
FCP_STD_TRAIT(is_copy_assignable,
              __is_assignable(__add_lvalue_reference(T), __add_lvalue_reference(const T)))
FCP_STD_TRAIT(is_move_assignable, __is_assignable(__add_lvalue_reference(T), __add_rvalue_reference(T)))
FCP_STD_TRAIT(is_trivially_copy_assignable,
              __is_trivially_assignable(__add_lvalue_reference(T), __add_lvalue_reference(const T)))
FCP_STD_TRAIT(is_trivially_move_assignable,
              __is_trivially_assignable(__add_lvalue_reference(T), __add_rvalue_reference(T)))
FCP_STD_TRAIT(is_bounded_array, __is_bounded_array(T))
FCP_STD_TRAIT(is_unbounded_array, __is_unbounded_array(T))
FCP_STD_TRAIT(has_unique_object_representations, __has_unique_object_representations(T))
#undef FCP_STD_TRAIT

template <class T, class U>
struct is_same : bool_constant<__is_same(T, U)> {};
template <class T, class U>
inline constexpr bool is_same_v = __is_same(T, U);
template <class B, class D>
struct is_base_of : bool_constant<__is_base_of(B, D)> {};
template <class B, class D>
inline constexpr bool is_base_of_v = __is_base_of(B, D);
template <class F, class T>
struct is_convertible : bool_constant<__is_convertible(F, T)> {};
template <class F, class T>
inline constexpr bool is_convertible_v = __is_convertible(F, T);
template <class F, class T>
inline constexpr bool is_nothrow_convertible_v = __is_nothrow_convertible(F, T);
template <class T, class... A>
struct is_constructible : bool_constant<__is_constructible(T, A...)> {};
template <class T, class... A>
inline constexpr bool is_constructible_v = __is_constructible(T, A...);
template <class T, class... A>
struct is_trivially_constructible : bool_constant<__is_trivially_constructible(T, A...)> {};
template <class T, class... A>
inline constexpr bool is_trivially_constructible_v = __is_trivially_constructible(T, A...);
template <class T, class... A>
struct is_nothrow_constructible : bool_constant<__is_nothrow_constructible(T, A...)> {};
template <class T, class... A>
inline constexpr bool is_nothrow_constructible_v = __is_nothrow_constructible(T, A...);
template <class T, class U>
struct is_assignable : bool_constant<__is_assignable(T, U)> {};
template <class T, class U>
inline constexpr bool is_assignable_v = __is_assignable(T, U);
template <class T, class U>
struct is_trivially_assignable : bool_constant<__is_trivially_assignable(T, U)> {};
template <class T, class U>
inline constexpr bool is_trivially_assignable_v = __is_trivially_assignable(T, U);
template <class T, class U>
inline constexpr bool is_nothrow_assignable_v = __is_nothrow_assignable(T, U);

template <class T>
struct is_member_function_pointer : bool_constant<__is_member_function_pointer(T)> {};
template <class T>
inline constexpr bool is_member_function_pointer_v = __is_member_function_pointer(T);

template <class T>
struct alignment_of : integral_constant<size_t, alignof(T)> {};
template <class T>
inline constexpr size_t alignment_of_v = alignof(T);
template <class T>
struct rank : integral_constant<size_t, __array_rank(T)> {};
template <class T>
inline constexpr size_t rank_v = __array_rank(T);
template <class T, unsigned I = 0>
struct extent : integral_constant<size_t, __array_extent(T, I)> {};
template <class T, unsigned I = 0>
inline constexpr size_t extent_v = __array_extent(T, I);

template <class... T>
struct conjunction : true_type {};
template <class T>
struct conjunction<T> : T {};
template <class T, class... R>
struct conjunction<T, R...> : conditional_t<bool(T::value), conjunction<R...>, T> {};
template <class... T>
inline constexpr bool conjunction_v = conjunction<T...>::value;
template <class... T>
struct disjunction : false_type {};
template <class T>
struct disjunction<T> : T {};
template <class T, class... R>
struct disjunction<T, R...> : conditional_t<bool(T::value), T, disjunction<R...>> {};
template <class... T>
inline constexpr bool disjunction_v = disjunction<T...>::value;
template <class T>
struct negation : bool_constant<!bool(T::value)> {};
template <class T>
inline constexpr bool negation_v = !bool(T::value);

template <class...>
using void_t = void;

template <class... T>
struct common_type {};
template <class T>
struct common_type<T> : common_type<T, T> {};
template <class T, class U>
struct common_type<T, U> {
  using type = __decay(decltype(false ? declval<__decay(T)>() : declval<__decay(U)>()));
};
template <class... T>
using common_type_t = typename common_type<T...>::type;

constexpr bool is_constant_evaluated() noexcept { return __builtin_is_constant_evaluated(); }

}  // namespace std
