// Copyright (C) 2015-2017 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3.  If not see
// <http://www.gnu.org/licenses/>.

#include <type_traits>
#include <testsuite_tr1.h>
#include <utility>
#include <array>
#include <tuple>
#include <queue>
#include <stack>

#if defined(test_std_is_nothrow_swappable)
#  ifndef __cpp_lib_is_swappable
#   error "Feature-test macro for is_nothrow_swappable missing"
#  elif __cpp_lib_is_swappable != 201603
#   error "Feature-test macro for is_nothrow_swappable has wrong value"
#  endif
// Test std::is_nothrow_swappable:
template<class T>
using is_nothrow_swappable = std::is_nothrow_swappable<T>;
#elif defined(test_std_is_nothrow_swappable_ext)
// Test our __is_nothrow_swappable extension:
template<class T>
using is_nothrow_swappable = std::__is_nothrow_swappable<T>;
#else
# error "Either test_std_is_nothrow_swappable or " \
        "test_std_is_nothrow_swappable_ext need to be defined"
#endif

namespace funny {
  struct F {};
  void swap(F&, F&) = delete;
  void swap(F(&)[5], F(&)[5]) noexcept;
  void swap(F(&)[6], F(&)[6]);
  struct A {};
  void swap(A&, A&) noexcept(false);
}
namespace std {
  template<>
  void swap<funny::A>(funny::A&, funny::A&) noexcept
  {
  }

  template<>
  void swap<funny::A>(funny::A(&)[3], funny::A(&)[3]) noexcept(false)
  {
  }
}
namespace ns1 {
  struct SwapThrow {};
  void swap(SwapThrow&, SwapThrow&);
  void swap(SwapThrow(&)[3], SwapThrow(&)[3]) noexcept;
}

namespace ns2 {
  struct SwapThrow {
    SwapThrow() noexcept = default;
    SwapThrow(const SwapThrow&) noexcept(false);
    SwapThrow& operator=(const SwapThrow&) noexcept(false);
  };
}

namespace ns3 {
  struct SwapNoThrow {
    SwapNoThrow() noexcept = default;
    SwapNoThrow(const SwapNoThrow&) noexcept(false);
    SwapNoThrow& operator =(const SwapNoThrow&) noexcept(false);
  };
  void swap(SwapNoThrow&, SwapNoThrow&) noexcept;
}

namespace ns4 {
  struct SwapNoThrow {};
}

namespace ns5 {
  struct SwapThrow {
    SwapThrow() noexcept = default;
    SwapThrow(SwapThrow&&) noexcept;
    SwapThrow& operator=(const SwapThrow&) noexcept(false);
  };
}

namespace comps {
  struct CompareNoThrowCopyable
  {
    template<class T>
    bool operator()(const T&, const T&) const
    { return false; }
  };

  struct CompareNonCopyable
  {
    CompareNonCopyable() = default;
    CompareNonCopyable(const CompareNonCopyable&) = delete;
    CompareNonCopyable& operator=(const CompareNonCopyable&) noexcept;

    template<class T>
    bool operator()(const T&, const T&) const
    { return false; }
  };

  struct CompareThrowCopyable
  {
    CompareThrowCopyable() = default;
    CompareThrowCopyable(const CompareThrowCopyable&) noexcept(false);
    CompareThrowCopyable& operator=(const CompareThrowCopyable&);

    template<class T>
    bool operator()(const T&, const T&) const
    { return false; }
  };
}

void test01()
{
  using namespace __gnu_test;
  // Positive tests.
  static_assert(test_property<is_nothrow_swappable, int>(true), "");
  static_assert(test_property<is_nothrow_swappable, bool>(true), "");
  static_assert(test_property<is_nothrow_swappable,
    decltype(nullptr)>(true), "");
  static_assert(test_property<is_nothrow_swappable, int&>(true), "");
  static_assert(test_property<is_nothrow_swappable, int&&>(true), "");
  static_assert(test_property<is_nothrow_swappable, int[1]>(true), "");
  static_assert(test_property<is_nothrow_swappable, int[1][2]>(true), "");
  static_assert(test_property<is_nothrow_swappable, int[1][2][3]>(true), "");
  static_assert(test_property<is_nothrow_swappable, funny::F[5]>(true), "");
  static_assert(test_property<is_nothrow_swappable, EnumType>(true), "");
  static_assert(test_property<is_nothrow_swappable, PODType>(true), "");
  static_assert(test_property<is_nothrow_swappable, UnionType>(true), "");
  static_assert(test_property<is_nothrow_swappable,
    construct::SE>(true), "");
  static_assert(test_property<is_nothrow_swappable,
    construct::Empty>(true), "");
  static_assert(test_property<is_nothrow_swappable, void*>(true), "");
  static_assert(test_property<is_nothrow_swappable, void(*)()>(true), "");
  static_assert(test_property<is_nothrow_swappable, int const*>(true), "");
  static_assert(test_property<is_nothrow_swappable, ClassType*>(true), "");
  static_assert(test_property<is_nothrow_swappable,
    int ClassType::*>(true), "");
  static_assert(test_property<is_nothrow_swappable,
    void (ClassType::*)()>(true), "");
  static_assert(test_property<is_nothrow_swappable,
    int (ClassType::*)() const volatile>(true), "");
  static_assert(test_property<is_nothrow_swappable,
    ns1::SwapThrow[3]>(true), "");
  static_assert(!noexcept(std::swap(std::declval<ns1::SwapThrow(&)[3]>(),
                                    std::declval<ns1::SwapThrow(&)[3]>())),
                                    "");
  static_assert(test_property<is_nothrow_swappable,
        ns3::SwapNoThrow>(true), "");
  static_assert(!noexcept(std::swap(std::declval<ns3::SwapNoThrow&>(),
                                    std::declval<ns3::SwapNoThrow&>())), "");
  static_assert(test_property<is_nothrow_swappable,
        ns3::SwapNoThrow[1]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        ns3::SwapNoThrow[3]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        ns3::SwapNoThrow[2][3][4]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        ns4::SwapNoThrow>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        ns4::SwapNoThrow[1]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        ns4::SwapNoThrow[3]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        ns4::SwapNoThrow[2][3][4]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::pair<int, int>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::pair<int, int>[1]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::pair<int, int>[1][2]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::tuple<int>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::tuple<int>[1]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::tuple<int>[1][2]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::tuple<>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::tuple<>[1]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::tuple<>[1][2]>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::array<int, 1>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::array<int, 0>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::array<construct::DelCopy, 0>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::array<ns1::SwapThrow, 0>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::queue<int>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::priority_queue<int>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::stack<int>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::priority_queue<int, std::vector<int>,
        comps::CompareNoThrowCopyable>>(true), "");

  // Negative tests.
  static_assert(test_property<is_nothrow_swappable, void>(false), "");
  static_assert(test_property<is_nothrow_swappable, const void>(false), "");
  static_assert(test_property<is_nothrow_swappable, void()>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    void() const>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    void() volatile>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    void() const volatile>(false), "");
  static_assert(test_property<is_nothrow_swappable, const int>(false), "");
  static_assert(test_property<is_nothrow_swappable, const bool>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    const int[1]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    const int[1][2]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    const int[1][2][3]>(false), "");
  static_assert(test_property<is_nothrow_swappable, int[]>(false), "");
  static_assert(test_property<is_nothrow_swappable, const int[]>(false), "");
  static_assert(test_property<is_nothrow_swappable, int[][1]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    const funny::F[5]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    construct::Abstract>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    construct::DelCopy>(false), "");
  static_assert(test_property<is_nothrow_swappable, funny::F>(false), "");
  static_assert(test_property<is_nothrow_swappable, funny::F[1]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    funny::F[1][2]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    funny::F[1][2][3]>(false), "");
  static_assert(test_property<is_nothrow_swappable, funny::F[6]>(false), "");
  static_assert(test_property<is_nothrow_swappable, funny::A>(false), "");
  static_assert(noexcept(std::swap(std::declval<funny::A&>(),
                                   std::declval<funny::A&>())), "");
  static_assert(test_property<is_nothrow_swappable, funny::A[3]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    ns1::SwapThrow>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    ns1::SwapThrow[1]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    ns1::SwapThrow[3][2]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    ns1::SwapThrow[2][3][4]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    ns2::SwapThrow>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    ns2::SwapThrow[1]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    ns2::SwapThrow[2][3][4]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    ns5::SwapThrow>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    ns5::SwapThrow[1]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
    ns5::SwapThrow[2][3][4]>(false), "");
  static_assert(test_property<is_nothrow_swappable,
		ThrowCopyConsClass>(false), "");
  static_assert(test_property<is_nothrow_swappable,
		std::pair<ThrowCopyConsClass, ThrowCopyConsClass>>(false), "");
  static_assert(test_property<is_nothrow_swappable,
		std::tuple<ThrowCopyConsClass>>(false), "");
  static_assert(test_property<is_nothrow_swappable,
		std::array<ThrowCopyConsClass, 1>>(false), "");
  static_assert(test_property<is_nothrow_swappable,
		std::queue<ThrowCopyConsClass>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
		std::priority_queue<ThrowCopyConsClass>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
		std::stack<ThrowCopyConsClass>>(true), "");
  static_assert(test_property<is_nothrow_swappable,
        std::priority_queue<int, std::vector<int>,
        comps::CompareNonCopyable>>(false), "");
  static_assert(test_property<is_nothrow_swappable,
        std::priority_queue<int, std::vector<int>,
        comps::CompareThrowCopyable>>(false), "");
}
