/*
 * 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.
 */

#include <folly/functional/traits.h>

#include <folly/portability/GTest.h>

struct TraitsTest : testing::Test {
  template <typename... T>
  static constexpr bool same_v = std::is_same<T...>::value;

  template <typename S>
  using traits = folly::function_traits<S>;

  template <typename S>
  using result_type_t = typename traits<S>::result_type;

  template <typename R, typename S>
  static constexpr bool is_result_type_v =
      std::is_same<R, result_type_t<S>>::value;

  template <typename S>
  static constexpr bool is_nothrow_v = traits<S>::is_nothrow;

  template <size_t I, typename S>
  using argument_type_t = typename traits<S>::template argument<I>;

  template <typename R, size_t I, typename S>
  static constexpr bool is_arg_type_v =
      std::is_same<R, argument_type_t<I, S>>::value;

  template <typename S>
  static constexpr size_t args_size_v =
      traits<S>::template arguments<folly::type_pack_size_t>::value;

  template <typename S>
  static constexpr bool is_variadic_v = traits<S>::is_variadic;

  template <typename T, typename S>
  using cvref_t = typename traits<S>::template value_like<T>;

  template <typename D, typename T, typename S>
  static constexpr bool is_cvref_t = std::is_same<D, cvref_t<T, S>>::value;

  template <typename F>
  using rem_cvref_t = folly::function_remove_cvref_t<F>;

  template <typename D, typename S, typename I>
  static constexpr bool is_like_v =
      same_v<D, folly::function_like_value_t<S, I>>;
};

TEST_F(TraitsTest, function_traits) {
  using vc = void const;

  //  result_type

  EXPECT_TRUE((is_result_type_v<vc, vc()>));
  EXPECT_TRUE((is_result_type_v<vc, vc() const>));
  EXPECT_TRUE((is_result_type_v<vc, vc() volatile>));
  EXPECT_TRUE((is_result_type_v<vc, vc() const volatile>));
  EXPECT_TRUE((is_result_type_v<vc, vc()&>));
  EXPECT_TRUE((is_result_type_v<vc, vc() const&>));
  EXPECT_TRUE((is_result_type_v<vc, vc() volatile&>));
  EXPECT_TRUE((is_result_type_v<vc, vc() const volatile&>));
  EXPECT_TRUE((is_result_type_v<vc, vc() &&>));
  EXPECT_TRUE((is_result_type_v<vc, vc() const&&>));
  EXPECT_TRUE((is_result_type_v<vc, vc() volatile&&>));
  EXPECT_TRUE((is_result_type_v<vc, vc() const volatile&&>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...)>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) const>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) volatile>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) const volatile>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...)&>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) const&>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) volatile&>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) const volatile&>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) &&>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) const&&>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) volatile&&>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) const volatile&&>));
  EXPECT_TRUE((is_result_type_v<vc, vc() noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc() const noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc() volatile noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc() const volatile noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc() & noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc() const & noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc() volatile & noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc() const volatile & noexcept>));
  EXPECT_TRUE((is_result_type_v < vc, vc() && noexcept >));
  EXPECT_TRUE((is_result_type_v < vc, vc() const&& noexcept >));
  EXPECT_TRUE((is_result_type_v < vc, vc() volatile && noexcept >));
  EXPECT_TRUE((is_result_type_v < vc, vc() const volatile&& noexcept >));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) const noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) volatile noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) const volatile noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) & noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) const & noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) volatile & noexcept>));
  EXPECT_TRUE((is_result_type_v<vc, vc(...) const volatile & noexcept>));
  EXPECT_TRUE((is_result_type_v < vc, vc(...) && noexcept >));
  EXPECT_TRUE((is_result_type_v < vc, vc(...) const&& noexcept >));
  EXPECT_TRUE((is_result_type_v < vc, vc(...) volatile && noexcept >));
  EXPECT_TRUE((is_result_type_v < vc, vc(...) const volatile&& noexcept >));

  //  is_nothrow

  EXPECT_FALSE((is_nothrow_v<void()>));
  EXPECT_FALSE((is_nothrow_v<void() const>));
  EXPECT_FALSE((is_nothrow_v<void() volatile>));
  EXPECT_FALSE((is_nothrow_v<void() const volatile>));
  EXPECT_FALSE((is_nothrow_v<void()&>));
  EXPECT_FALSE((is_nothrow_v<void() const&>));
  EXPECT_FALSE((is_nothrow_v<void() volatile&>));
  EXPECT_FALSE((is_nothrow_v<void() const volatile&>));
  EXPECT_FALSE((is_nothrow_v<void() &&>));
  EXPECT_FALSE((is_nothrow_v<void() const&&>));
  EXPECT_FALSE((is_nothrow_v<void() volatile&&>));
  EXPECT_FALSE((is_nothrow_v<void() const volatile&&>));
  EXPECT_FALSE((is_nothrow_v<void(...)>));
  EXPECT_FALSE((is_nothrow_v<void(...) const>));
  EXPECT_FALSE((is_nothrow_v<void(...) volatile>));
  EXPECT_FALSE((is_nothrow_v<void(...) const volatile>));
  EXPECT_FALSE((is_nothrow_v<void(...)&>));
  EXPECT_FALSE((is_nothrow_v<void(...) const&>));
  EXPECT_FALSE((is_nothrow_v<void(...) volatile&>));
  EXPECT_FALSE((is_nothrow_v<void(...) const volatile&>));
  EXPECT_FALSE((is_nothrow_v<void(...) &&>));
  EXPECT_FALSE((is_nothrow_v<void(...) const&&>));
  EXPECT_FALSE((is_nothrow_v<void(...) volatile&&>));
  EXPECT_FALSE((is_nothrow_v<void(...) const volatile&&>));
  EXPECT_TRUE((is_nothrow_v<void() noexcept>));
  EXPECT_TRUE((is_nothrow_v<void() const noexcept>));
  EXPECT_TRUE((is_nothrow_v<void() volatile noexcept>));
  EXPECT_TRUE((is_nothrow_v<void() const volatile noexcept>));
  EXPECT_TRUE((is_nothrow_v<void() & noexcept>));
  EXPECT_TRUE((is_nothrow_v<void() const & noexcept>));
  EXPECT_TRUE((is_nothrow_v<void() volatile & noexcept>));
  EXPECT_TRUE((is_nothrow_v<void() const volatile & noexcept>));
  EXPECT_TRUE((is_nothrow_v < void() && noexcept >));
  EXPECT_TRUE((is_nothrow_v < void() const&& noexcept >));
  EXPECT_TRUE((is_nothrow_v < void() volatile && noexcept >));
  EXPECT_TRUE((is_nothrow_v < void() const volatile&& noexcept >));
  EXPECT_TRUE((is_nothrow_v<void(...) noexcept>));
  EXPECT_TRUE((is_nothrow_v<void(...) const noexcept>));
  EXPECT_TRUE((is_nothrow_v<void(...) volatile noexcept>));
  EXPECT_TRUE((is_nothrow_v<void(...) const volatile noexcept>));
  EXPECT_TRUE((is_nothrow_v<void(...) & noexcept>));
  EXPECT_TRUE((is_nothrow_v<void(...) const & noexcept>));
  EXPECT_TRUE((is_nothrow_v<void(...) volatile & noexcept>));
  EXPECT_TRUE((is_nothrow_v<void(...) const volatile & noexcept>));
  EXPECT_TRUE((is_nothrow_v < void(...) && noexcept >));
  EXPECT_TRUE((is_nothrow_v < void(...) const&& noexcept >));
  EXPECT_TRUE((is_nothrow_v < void(...) volatile && noexcept >));
  EXPECT_TRUE((is_nothrow_v < void(...) const volatile&& noexcept >));

  //  variadic

  EXPECT_FALSE((is_variadic_v<void()>));
  EXPECT_FALSE((is_variadic_v<void() const>));
  EXPECT_FALSE((is_variadic_v<void() volatile>));
  EXPECT_FALSE((is_variadic_v<void() const volatile>));
  EXPECT_FALSE((is_variadic_v<void()&>));
  EXPECT_FALSE((is_variadic_v<void() const&>));
  EXPECT_FALSE((is_variadic_v<void() volatile&>));
  EXPECT_FALSE((is_variadic_v<void() const volatile&>));
  EXPECT_FALSE((is_variadic_v<void() &&>));
  EXPECT_FALSE((is_variadic_v<void() const&&>));
  EXPECT_FALSE((is_variadic_v<void() volatile&&>));
  EXPECT_FALSE((is_variadic_v<void() const volatile&&>));
  EXPECT_TRUE((is_variadic_v<void(...)>));
  EXPECT_TRUE((is_variadic_v<void(...) const>));
  EXPECT_TRUE((is_variadic_v<void(...) volatile>));
  EXPECT_TRUE((is_variadic_v<void(...) const volatile>));
  EXPECT_TRUE((is_variadic_v<void(...)&>));
  EXPECT_TRUE((is_variadic_v<void(...) const&>));
  EXPECT_TRUE((is_variadic_v<void(...) volatile&>));
  EXPECT_TRUE((is_variadic_v<void(...) const volatile&>));
  EXPECT_TRUE((is_variadic_v<void(...) &&>));
  EXPECT_TRUE((is_variadic_v<void(...) const&&>));
  EXPECT_TRUE((is_variadic_v<void(...) volatile&&>));
  EXPECT_TRUE((is_variadic_v<void(...) const volatile&&>));
  EXPECT_FALSE((is_variadic_v<void() noexcept>));
  EXPECT_FALSE((is_variadic_v<void() const noexcept>));
  EXPECT_FALSE((is_variadic_v<void() volatile noexcept>));
  EXPECT_FALSE((is_variadic_v<void() const volatile noexcept>));
  EXPECT_FALSE((is_variadic_v<void() & noexcept>));
  EXPECT_FALSE((is_variadic_v<void() const & noexcept>));
  EXPECT_FALSE((is_variadic_v<void() volatile & noexcept>));
  EXPECT_FALSE((is_variadic_v<void() const volatile & noexcept>));
  EXPECT_FALSE((is_variadic_v < void() && noexcept >));
  EXPECT_FALSE((is_variadic_v < void() const&& noexcept >));
  EXPECT_FALSE((is_variadic_v < void() volatile && noexcept >));
  EXPECT_FALSE((is_variadic_v < void() const volatile&& noexcept >));
  EXPECT_TRUE((is_variadic_v<void(...) noexcept>));
  EXPECT_TRUE((is_variadic_v<void(...) const noexcept>));
  EXPECT_TRUE((is_variadic_v<void(...) volatile noexcept>));
  EXPECT_TRUE((is_variadic_v<void(...) const volatile noexcept>));
  EXPECT_TRUE((is_variadic_v<void(...) & noexcept>));
  EXPECT_TRUE((is_variadic_v<void(...) const & noexcept>));
  EXPECT_TRUE((is_variadic_v<void(...) volatile & noexcept>));
  EXPECT_TRUE((is_variadic_v<void(...) const volatile & noexcept>));
  EXPECT_TRUE((is_variadic_v < void(...) && noexcept >));
  EXPECT_TRUE((is_variadic_v < void(...) const&& noexcept >));
  EXPECT_TRUE((is_variadic_v < void(...) volatile && noexcept >));
  EXPECT_TRUE((is_variadic_v < void(...) const volatile&& noexcept >));

  //  argument

  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*)>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) volatile>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const volatile>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*)&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) volatile&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const volatile&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) &&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const&&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) volatile&&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const volatile&&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...)>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) volatile>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const volatile>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...)&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) volatile&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const volatile&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) &&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const&&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) volatile&&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const volatile&&>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) volatile noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const volatile noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) & noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const & noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) volatile & noexcept>));
  EXPECT_TRUE(
      (is_arg_type_v<vc*, 1, void(int, vc*) const volatile & noexcept>));
  EXPECT_TRUE((is_arg_type_v < vc*, 1, void(int, vc*) && noexcept >));
  EXPECT_TRUE((is_arg_type_v < vc*, 1, void(int, vc*) const&& noexcept >));
  EXPECT_TRUE((is_arg_type_v < vc*, 1, void(int, vc*) volatile && noexcept >));
  EXPECT_TRUE(( //
      is_arg_type_v < vc*,
      1,
      void(int, vc*) const volatile&& noexcept >));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) volatile noexcept>));
  EXPECT_TRUE(( //
      is_arg_type_v<vc*, 1, void(int, vc*, ...) const volatile noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) & noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const & noexcept>));
  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) volatile & noexcept>));
  EXPECT_TRUE(( //
      is_arg_type_v<vc*, 1, void(int, vc*, ...) const volatile & noexcept>));
  EXPECT_TRUE((is_arg_type_v < vc*, 1, void(int, vc*, ...) && noexcept >));
  EXPECT_TRUE((is_arg_type_v < vc*, 1, void(int, vc*, ...) const&& noexcept >));
  EXPECT_TRUE(
      (is_arg_type_v < vc*, 1, void(int, vc*, ...) volatile && noexcept >));
  EXPECT_TRUE(( //
      is_arg_type_v < vc*,
      1,
      void(int, vc*, ...) const volatile&& noexcept >));

  //  arguments

  EXPECT_EQ(3, (args_size_v<void(int, vc*, float)>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) volatile>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const volatile>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float)&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) volatile&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const volatile&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) &&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const&&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) volatile&&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const volatile&&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...)>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) volatile>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const volatile>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...)&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) volatile&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const volatile&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) &&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const&&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) volatile&&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const volatile&&>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) volatile noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const volatile noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) & noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const & noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) volatile & noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const volatile & noexcept>));
  EXPECT_EQ(3, (args_size_v < void(int, vc*, float) && noexcept >));
  EXPECT_EQ(3, (args_size_v < void(int, vc*, float) const&& noexcept >));
  EXPECT_EQ(3, (args_size_v < void(int, vc*, float) volatile && noexcept >));
  EXPECT_EQ(
      3, (args_size_v < void(int, vc*, float) const volatile&& noexcept >));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) volatile noexcept>));
  EXPECT_EQ(
      3, (args_size_v<void(int, vc*, float, ...) const volatile noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) & noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const & noexcept>));
  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) volatile & noexcept>));
  EXPECT_EQ(
      3, (args_size_v<void(int, vc*, float, ...) const volatile & noexcept>));
  EXPECT_EQ(3, (args_size_v < void(int, vc*, float, ...) && noexcept >));
  EXPECT_EQ(3, (args_size_v < void(int, vc*, float, ...) const&& noexcept >));
  EXPECT_EQ(
      3, (args_size_v < void(int, vc*, float, ...) volatile && noexcept >));
  EXPECT_EQ(
      3,
      (args_size_v < void(int, vc*, float, ...) const volatile&& noexcept >));

  //  value_like

  EXPECT_TRUE((is_cvref_t<int, int, void()>));
  EXPECT_TRUE((is_cvref_t<int const, int, void() const>));
  EXPECT_TRUE((is_cvref_t<int volatile, int, void() volatile>));
  EXPECT_TRUE((is_cvref_t<int const volatile, int, void() const volatile>));
  EXPECT_TRUE((is_cvref_t<int&, int, void()&>));
  EXPECT_TRUE((is_cvref_t<int const&, int, void() const&>));
  EXPECT_TRUE((is_cvref_t<int volatile&, int, void() volatile&>));
  EXPECT_TRUE((is_cvref_t<int const volatile&, int, void() const volatile&>));
  EXPECT_TRUE((is_cvref_t<int&&, int, void() &&>));
  EXPECT_TRUE((is_cvref_t<int const&&, int, void() const&&>));
  EXPECT_TRUE((is_cvref_t<int volatile&&, int, void() volatile&&>));
  EXPECT_TRUE((is_cvref_t<int const volatile&&, int, void() const volatile&&>));
  EXPECT_TRUE((is_cvref_t<int, int, void(...)>));
  EXPECT_TRUE((is_cvref_t<int const, int, void(...) const>));
  EXPECT_TRUE((is_cvref_t<int volatile, int, void(...) volatile>));
  EXPECT_TRUE((is_cvref_t<int const volatile, int, void(...) const volatile>));
  EXPECT_TRUE((is_cvref_t<int&, int, void(...)&>));
  EXPECT_TRUE((is_cvref_t<int const&, int, void(...) const&>));
  EXPECT_TRUE((is_cvref_t<int volatile&, int, void(...) volatile&>));
  EXPECT_TRUE(( //
      is_cvref_t<int const volatile&, int, void(...) const volatile&>));
  EXPECT_TRUE((is_cvref_t<int&&, int, void(...) &&>));
  EXPECT_TRUE((is_cvref_t<int const&&, int, void(...) const&&>));
  EXPECT_TRUE((is_cvref_t<int volatile&&, int, void(...) volatile&&>));
  EXPECT_TRUE(( //
      is_cvref_t<int const volatile&&, int, void(...) const volatile&&>));
  EXPECT_TRUE((is_cvref_t<int, int, void() noexcept>));
  EXPECT_TRUE((is_cvref_t<int const, int, void() const noexcept>));
  EXPECT_TRUE((is_cvref_t<int volatile, int, void() volatile noexcept>));
  EXPECT_TRUE(( //
      is_cvref_t<int const volatile, int, void() const volatile noexcept>));
  EXPECT_TRUE((is_cvref_t<int&, int, void() & noexcept>));
  EXPECT_TRUE((is_cvref_t<int const&, int, void() const & noexcept>));
  EXPECT_TRUE((is_cvref_t<int volatile&, int, void() volatile & noexcept>));
  EXPECT_TRUE(( //
      is_cvref_t<int const volatile&, int, void() const volatile & noexcept>));
  EXPECT_TRUE((is_cvref_t < int&&, int, void()&& noexcept >));
  EXPECT_TRUE((is_cvref_t < int const&&, int, void() const&& noexcept >));
  EXPECT_TRUE((is_cvref_t < int volatile&&, int, void() volatile&& noexcept >));
  EXPECT_TRUE(
      (is_cvref_t < int const volatile&&,
       int,
       void() const volatile&& noexcept >));
  EXPECT_TRUE((is_cvref_t<int, int, void(...) noexcept>));
  EXPECT_TRUE((is_cvref_t<int const, int, void(...) const noexcept>));
  EXPECT_TRUE((is_cvref_t<int volatile, int, void(...) volatile noexcept>));
  EXPECT_TRUE(( //
      is_cvref_t<int const volatile, int, void(...) const volatile noexcept>));
  EXPECT_TRUE((is_cvref_t<int&, int, void(...) & noexcept>));
  EXPECT_TRUE((is_cvref_t<int const&, int, void(...) const & noexcept>));
  EXPECT_TRUE((is_cvref_t<int volatile&, int, void(...) volatile & noexcept>));
  EXPECT_TRUE(( //
      is_cvref_t<
          int const volatile&,
          int,
          void(...) const volatile & noexcept>));
  EXPECT_TRUE((is_cvref_t < int&&, int, void(...)&& noexcept >));
  EXPECT_TRUE((is_cvref_t < int const&&, int, void(...) const&& noexcept >));
  EXPECT_TRUE(
      (is_cvref_t < int volatile&&, int, void(...) volatile&& noexcept >));
  EXPECT_TRUE(( //
      is_cvref_t < int const volatile&&,
      int,
      void(...) const volatile&& noexcept >));
}

TEST_F(TraitsTest, function_remove_cvref) {
  using f = char(int*);
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*)>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) volatile>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const volatile>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*)&>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const&>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) volatile&>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const volatile&>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) &&>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const&&>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) volatile&&>>));
  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const volatile&&>>));

  using fv = char(int*, ...);
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...)>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) volatile>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const volatile>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...)&>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const&>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) volatile&>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const volatile&>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) &&>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const&&>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) volatile&&>>));
  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const volatile&&>>));

  using g = char(int*) noexcept;
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) volatile noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const volatile noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) & noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const & noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) volatile & noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const volatile & noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) && noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const && noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) volatile && noexcept>>));
  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const volatile && noexcept>>));

  using gv = char(int*, ...) noexcept;
  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) noexcept>>));
  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) const noexcept>>));
  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) volatile noexcept>>));
  EXPECT_TRUE(( //
      same_v<gv, rem_cvref_t<char(int*, ...) const volatile noexcept>>));
  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) & noexcept>>));
  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) const & noexcept>>));
  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) volatile & noexcept>>));
  EXPECT_TRUE(( //
      same_v<gv, rem_cvref_t<char(int*, ...) const volatile & noexcept>>));
  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) && noexcept>>));
  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) const && noexcept>>));
  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) volatile && noexcept>>));
  EXPECT_TRUE(( //
      same_v<gv, rem_cvref_t<char(int*, ...) const volatile && noexcept>>));
}

TEST_F(TraitsTest, function_like_value) {
  using f = char(int*);
  EXPECT_TRUE((is_like_v<char(int*), int, f>));
  EXPECT_TRUE((is_like_v<char(int*) const, int const, f>));
  EXPECT_TRUE((is_like_v<char(int*) volatile, int volatile, f>));
  EXPECT_TRUE((is_like_v<char(int*) const volatile, int const volatile, f>));
  EXPECT_TRUE((is_like_v<char(int*)&, int&, f>));
  EXPECT_TRUE((is_like_v<char(int*) const&, int const&, f>));
  EXPECT_TRUE((is_like_v<char(int*) volatile&, int volatile&, f>));
  EXPECT_TRUE((is_like_v<char(int*) const volatile&, int const volatile&, f>));
  EXPECT_TRUE((is_like_v<char(int*)&&, int&&, f>));
  EXPECT_TRUE((is_like_v<char(int*) const&&, int const&&, f>));
  EXPECT_TRUE((is_like_v<char(int*) volatile&&, int volatile&&, f>));
  EXPECT_TRUE(( //
      is_like_v<char(int*) const volatile&&, int const volatile&&, f>));

  using fv = char(int*, ...);
  EXPECT_TRUE((is_like_v<char(int*, ...), int, fv>));
  EXPECT_TRUE((is_like_v<char(int*, ...) const, int const, fv>));
  EXPECT_TRUE((is_like_v<char(int*, ...) volatile, int volatile, fv>));
  EXPECT_TRUE(( //
      is_like_v<char(int*, ...) const volatile, int const volatile, fv>));
  EXPECT_TRUE((is_like_v<char(int*, ...)&, int&, fv>));
  EXPECT_TRUE((is_like_v<char(int*, ...) const&, int const&, fv>));
  EXPECT_TRUE((is_like_v<char(int*, ...) volatile&, int volatile&, fv>));
  EXPECT_TRUE(( //
      is_like_v<char(int*, ...) const volatile&, int const volatile&, fv>));
  EXPECT_TRUE((is_like_v<char(int*, ...)&&, int&&, fv>));
  EXPECT_TRUE((is_like_v<char(int*, ...) const&&, int const&&, fv>));
  EXPECT_TRUE((is_like_v<char(int*, ...) volatile&&, int volatile&&, fv>));
  EXPECT_TRUE(( //
      is_like_v<char(int*, ...) const volatile&&, int const volatile&&, fv>));

  using g = char(int*) noexcept;
  EXPECT_TRUE((is_like_v<char(int*) noexcept, int, g>));
  EXPECT_TRUE((is_like_v<char(int*) const noexcept, int const, g>));
  EXPECT_TRUE((is_like_v<char(int*) volatile noexcept, int volatile, g>));
  EXPECT_TRUE(( //
      is_like_v<char(int*) const volatile noexcept, int const volatile, g>));
  EXPECT_TRUE((is_like_v<char(int*) & noexcept, int&, g>));
  EXPECT_TRUE((is_like_v<char(int*) const & noexcept, int const&, g>));
  EXPECT_TRUE((is_like_v<char(int*) volatile & noexcept, int volatile&, g>));
  EXPECT_TRUE(( //
      is_like_v<char(int*) const volatile & noexcept, int const volatile&, g>));
  EXPECT_TRUE((is_like_v < char(int*) && noexcept, int&&, g >));
  EXPECT_TRUE((is_like_v < char(int*) const&& noexcept, int const&&, g >));
  EXPECT_TRUE(
      (is_like_v < char(int*) volatile && noexcept, int volatile&&, g >));
  EXPECT_TRUE(( //
      is_like_v < char(int*) const volatile&& noexcept,
      int const volatile&&,
      g >));

  using gv = char(int*, ...) noexcept;
  EXPECT_TRUE((is_like_v<char(int*, ...) noexcept, int, gv>));
  EXPECT_TRUE((is_like_v<char(int*, ...) const noexcept, int const, gv>));
  EXPECT_TRUE((is_like_v<char(int*, ...) volatile noexcept, int volatile, gv>));
  EXPECT_TRUE(( //
      is_like_v<
          char(int*, ...) const volatile noexcept,
          int const volatile,
          gv>));
  EXPECT_TRUE((is_like_v<char(int*, ...) & noexcept, int&, gv>));
  EXPECT_TRUE((is_like_v<char(int*, ...) const & noexcept, int const&, gv>));
  EXPECT_TRUE(( //
      is_like_v<char(int*, ...) volatile & noexcept, int volatile&, gv>));
  EXPECT_TRUE(( //
      is_like_v<
          char(int*, ...) const volatile & noexcept,
          int const volatile&,
          gv>));
  EXPECT_TRUE((is_like_v < char(int*, ...) && noexcept, int&&, gv >));
  EXPECT_TRUE(
      (is_like_v < char(int*, ...) const&& noexcept, int const&&, gv >));
  EXPECT_TRUE(( //
      is_like_v < char(int*, ...) volatile && noexcept,
      int volatile&&,
      gv >));
  EXPECT_TRUE(( //
      is_like_v < char(int*, ...) const volatile&& noexcept,
      int const volatile&&,
      gv >));
}
