// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP_EXPERIMENTAL_TYPE_TRAITS
#define _LIBCPP_EXPERIMENTAL_TYPE_TRAITS

/**
    experimental/type_traits synopsis

// C++1y
#include <type_traits>

namespace std {
namespace experimental {
inline namespace fundamentals_v1 {

  // 3.3.2, Other type transformations
  template <class> class invocation_type; // not defined
  template <class F, class... ArgTypes> class invocation_type<F(ArgTypes...)>;
  template <class> class raw_invocation_type; // not defined
  template <class F, class... ArgTypes> class raw_invocation_type<F(ArgTypes...)>;

  template <class T>
    using invocation_type_t = typename invocation_type<T>::type;
  template <class T>
    using raw_invocation_type_t = typename raw_invocation_type<T>::type;

  // 3.3.4, Detection idiom
  template <class...> using void_t = void;

  struct nonesuch {
    nonesuch() = delete;
    ~nonesuch() = delete;
    nonesuch(nonesuch const&) = delete;
    void operator=(nonesuch const&) = delete;
  };

  template <template<class...> class Op, class... Args>
    using is_detected = see below;
  template <template<class...> class Op, class... Args>
    constexpr bool is_detected_v = is_detected<Op, Args...>::value;
  template <template<class...> class Op, class... Args>
    using detected_t = see below;
  template <class Default, template<class...> class Op, class... Args>
    using detected_or = see below;
  template <class Default, template<class...> class Op, class... Args>
    using detected_or_t = typename detected_or<Default, Op, Args...>::type;
  template <class Expected, template<class...> class Op, class... Args>
    using is_detected_exact = is_same<Expected, detected_t<Op, Args...>>;
  template <class Expected, template<class...> class Op, class... Args>
    constexpr bool is_detected_exact_v
      = is_detected_exact<Expected, Op, Args...>::value;
  template <class To, template<class...> class Op, class... Args>
     using is_detected_convertible = is_convertible<detected_t<Op, Args...>, To>;
  template <class To, template<class...> class Op, class... Args>
     constexpr bool is_detected_convertible_v
       = is_detected_convertible<To, Op, Args...>::value;

} // namespace fundamentals_v1
} // namespace experimental
} // namespace std

 */

#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
#  include <__cxx03/__config>
#else
#  include <__config>

#  if _LIBCPP_STD_VER >= 14

#    include <initializer_list>
#    include <type_traits>

#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#      pragma GCC system_header
#    endif

_LIBCPP_BEGIN_NAMESPACE_LFTS

// 3.3.2, Other type transformations
/*
template <class>
class raw_invocation_type;

template <class _Fn, class ..._Args>
class raw_invocation_type<_Fn(_Args...)>;

template <class>
class invokation_type;

template <class _Fn, class ..._Args>
class invokation_type<_Fn(_Args...)>;

template <class _Tp>
using invokation_type_t = typename invokation_type<_Tp>::type;

template <class _Tp>
using raw_invocation_type_t = typename raw_invocation_type<_Tp>::type;
*/

// 3.3.4, Detection idiom
template <class...>
using void_t = void;

struct nonesuch : private __nat { // make nonesuch "not an aggregate"
  ~nonesuch()                     = delete;
  nonesuch(nonesuch const&)       = delete;
  void operator=(nonesuch const&) = delete;
};

template <class _Default, class _AlwaysVoid, template <class...> class _Op, class... _Args>
struct _DETECTOR {
  using value_t = false_type;
  using type    = _Default;
};

template <class _Default, template <class...> class _Op, class... _Args>
struct _DETECTOR<_Default, void_t<_Op<_Args...>>, _Op, _Args...> {
  using value_t = true_type;
  using type    = _Op<_Args...>;
};

template <template <class...> class _Op, class... _Args>
using is_detected = typename _DETECTOR<nonesuch, void, _Op, _Args...>::value_t;
template <template <class...> class _Op, class... _Args>
using detected_t = typename _DETECTOR<nonesuch, void, _Op, _Args...>::type;
template <template <class...> class _Op, class... _Args>
constexpr bool is_detected_v = is_detected<_Op, _Args...>::value;

template <class _Default, template <class...> class _Op, class... _Args>
using detected_or = _DETECTOR<_Default, void, _Op, _Args...>;
template <class _Default, template <class...> class _Op, class... _Args>
using detected_or_t = typename detected_or<_Default, _Op, _Args...>::type;

template <class _Expected, template <class...> class _Op, class... _Args>
using is_detected_exact = is_same<_Expected, detected_t<_Op, _Args...>>;
template <class _Expected, template <class...> class _Op, class... _Args>
constexpr bool is_detected_exact_v = is_detected_exact<_Expected, _Op, _Args...>::value;

template <class _To, template <class...> class _Op, class... _Args>
using is_detected_convertible = is_convertible<detected_t<_Op, _Args...>, _To>;
template <class _To, template <class...> class _Op, class... _Args>
constexpr bool is_detected_convertible_v = is_detected_convertible<_To, _Op, _Args...>::value;

_LIBCPP_END_NAMESPACE_LFTS

#    if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
#      include <cstddef>
#    endif

#  endif /* _LIBCPP_STD_VER >= 14 */
#endif   // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)

#endif /* _LIBCPP_EXPERIMENTAL_TYPE_TRAITS */
