// { dg-options "-std=gnu++23" }
// { dg-do compile { target c++23 } }
// { dg-require-effective-target hosted }

#include <functional>

#ifndef __cpp_lib_move_only_function
# error "Feature-test macro for move_only_function missing in <functional>"
#elif __cpp_lib_move_only_function != 202110L
# error "Feature-test macro for move_only_function has wrong value in <functional>"
#endif

using std::move_only_function;

using std::is_constructible_v;
using std::is_copy_constructible_v;
using std::is_nothrow_default_constructible_v;
using std::is_nothrow_move_constructible_v;
using std::is_nothrow_constructible_v;
using std::nullptr_t;
using std::in_place_type_t;

static_assert( is_nothrow_default_constructible_v<move_only_function<void()>> );
static_assert( is_nothrow_constructible_v<move_only_function<void()>, nullptr_t> );
static_assert( is_nothrow_move_constructible_v<move_only_function<void()>> );
static_assert( ! is_copy_constructible_v<move_only_function<void()>> );

static_assert( is_constructible_v<move_only_function<void()>, void()> );
static_assert( is_constructible_v<move_only_function<void()>, void(&)()> );
static_assert( is_constructible_v<move_only_function<void()>, void(*)()> );
static_assert( is_constructible_v<move_only_function<void()>, int()> );
static_assert( is_constructible_v<move_only_function<void()>, int(&)()> );
static_assert( is_constructible_v<move_only_function<void()>, int(*)()> );
static_assert( ! is_constructible_v<move_only_function<void()>, void(int)> );
static_assert( is_constructible_v<move_only_function<void(int)>, void(int)> );

static_assert( is_constructible_v<move_only_function<void(int)>,
				  in_place_type_t<void(*)(int)>, void(int)> );

static_assert( is_constructible_v<move_only_function<void()>,
				  void() noexcept> );
static_assert( is_constructible_v<move_only_function<void() noexcept>,
				  void() noexcept> );
static_assert( ! is_constructible_v<move_only_function<void() noexcept>,
				    void() > );

struct Q
{
  void operator()() const &;
  void operator()() &&;
};

static_assert( is_constructible_v<move_only_function<void()>, Q> );
static_assert( is_constructible_v<move_only_function<void() const>, Q> );
static_assert( is_constructible_v<move_only_function<void() &>, Q> );
static_assert( is_constructible_v<move_only_function<void() const &>, Q> );
static_assert( is_constructible_v<move_only_function<void() &&>, Q> );
static_assert( is_constructible_v<move_only_function<void() const &&>, Q> );

struct R
{
  void operator()() &;
  void operator()() &&;
};

static_assert( is_constructible_v<move_only_function<void()>, R> );
static_assert( is_constructible_v<move_only_function<void()&>, R> );
static_assert( is_constructible_v<move_only_function<void()&&>, R> );
static_assert( ! is_constructible_v<move_only_function<void() const>, R> );
static_assert( ! is_constructible_v<move_only_function<void() const&>, R> );
static_assert( ! is_constructible_v<move_only_function<void() const&&>, R> );

// The following nothrow-constructible guarantees are a GCC extension,
// not required by the standard.

static_assert( is_nothrow_constructible_v<move_only_function<void()>, void()> );
static_assert( is_nothrow_constructible_v<move_only_function<void(int)>,
					  in_place_type_t<void(*)(int)>,
					  void(int)> );

// These types are all small and nothrow move constructible
struct F { void operator()(); };
struct G { void operator()() const; };
static_assert( is_nothrow_constructible_v<move_only_function<void()>, F> );
static_assert( is_nothrow_constructible_v<move_only_function<void()>, G> );
static_assert( is_nothrow_constructible_v<move_only_function<void() const>, G> );

struct H {
  H(int);
  H(int, int) noexcept;
  void operator()() noexcept;
};
static_assert( is_nothrow_constructible_v<move_only_function<void()>, H> );
static_assert( is_nothrow_constructible_v<move_only_function<void() noexcept>,
					  H> );
static_assert( ! is_nothrow_constructible_v<move_only_function<void() noexcept>,
					    in_place_type_t<H>, int> );
static_assert( is_nothrow_constructible_v<move_only_function<void() noexcept>,
					  in_place_type_t<H>, int, int> );

struct I {
  I(int, const char*);
  I(std::initializer_list<char>);
  int operator()() const noexcept;
};

static_assert( is_constructible_v<move_only_function<void()>,
				  std::in_place_type_t<I>,
				  int, const char*> );
static_assert( is_constructible_v<move_only_function<void()>,
				  std::in_place_type_t<I>,
				  std::initializer_list<char>> );

void
test_instantiation()
{
  // Instantiate the constructor bodies
  move_only_function<void()> f0;
  move_only_function<void()> f1(nullptr);
  move_only_function<void()> f2( I(1, "two") );
  move_only_function<void()> f3(std::in_place_type<I>, 3, "four");
  move_only_function<void()> f4(std::in_place_type<I>, // PR libstdc++/102825
				{ 'P', 'R', '1', '0', '2', '8', '2', '5'});
  auto f5 = std::move(f4);
  f4 = std::move(f5);
}
