//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// We voluntarily use std::default_initializable on types that have redundant
// or ignored cv-qualifiers -- don't warn about it.
// ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-ignored-qualifiers

// template<class T>
//     concept default_initializable = constructible_from<T> &&
//     requires { T{}; } &&
//     is-default-initializable<T>;

#include <array>
#include <concepts>
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <memory>
#include <queue>
#include <set>
#include <span>
#include <stack>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "test_macros.h"

struct Empty {};

struct CtorDefaulted {
  CtorDefaulted() = default;
};
struct CtorDeleted {
  CtorDeleted() = delete;
};
struct DtorDefaulted {
  ~DtorDefaulted() = default;
};
struct DtorDeleted {
  ~DtorDeleted() = delete;
};

struct Noexcept {
  ~Noexcept() noexcept;
};
struct NoexceptTrue {
  ~NoexceptTrue() noexcept(true);
};
struct NoexceptFalse {
  ~NoexceptFalse() noexcept(false);
};

struct CtorProtected {
protected:
  CtorProtected() = default;
};
struct CtorPrivate {
private:
  CtorPrivate() = default;
};
struct DtorProtected {
protected:
  ~DtorProtected() = default;
};
struct DtorPrivate {
private:
  ~DtorPrivate() = default;
};

template <class T>
struct NoexceptDependant {
  ~NoexceptDependant() noexcept(std::is_same_v<T, int>);
};

struct CtorExplicit {
  explicit CtorExplicit() = default;
};
struct CtorArgument {
  CtorArgument(int) {}
};
struct CtorDefaultArgument {
  CtorDefaultArgument(int = 0) {}
};
struct CtorExplicitDefaultArgument {
  explicit CtorExplicitDefaultArgument(int = 0) {}
};

struct Derived : public Empty {};

class Abstract {
  virtual void foo() = 0;
};

class AbstractDestructor {
  virtual ~AbstractDestructor() = 0;
};

class OperatorNewDeleted {
  void* operator new(std::size_t) = delete;
  void operator delete(void* ptr) = delete;
};

[[maybe_unused]] auto Lambda = [](const int&, int&&, double){};

template<class T>
void test_not_const()
{
    static_assert( std::default_initializable<               T>);
    static_assert(!std::default_initializable<const          T>);
    static_assert( std::default_initializable<      volatile T>);
    static_assert(!std::default_initializable<const volatile T>);
}

template<class T>
void test_true()
{
    static_assert( std::default_initializable<               T>);
    static_assert( std::default_initializable<const          T>);
    static_assert( std::default_initializable<      volatile T>);
    static_assert( std::default_initializable<const volatile T>);
}

template<class T>
void test_false()
{
    static_assert(!std::default_initializable<               T>);
    static_assert(!std::default_initializable<const          T>);
    static_assert(!std::default_initializable<      volatile T>);
    static_assert(!std::default_initializable<const volatile T>);
}

void test()
{
    test_not_const<bool>();
    test_not_const<char>();
    test_not_const<int>();
    test_not_const<double>();

    test_false    <void>();
    test_not_const<void*>();

    test_not_const<int*>();
    test_false    <int[]>();
    test_not_const<int[1]>();
    test_false    <int&>();
    test_false    <int&&>();

    test_true     <Empty>();

    test_true     <CtorDefaulted>();
    test_false    <CtorDeleted>();
    test_true     <DtorDefaulted>();
    test_false    <DtorDeleted>();

    test_true     <Noexcept>();
    test_true     <NoexceptTrue>();
    test_false    <NoexceptFalse>();

    test_false    <CtorProtected>();
    test_false    <CtorPrivate>();
    test_false    <DtorProtected>();
    test_false    <DtorPrivate>();

    test_true     <NoexceptDependant<int>>();
    test_false    <NoexceptDependant<double>>();

    test_true     <CtorExplicit>();
    test_false    <CtorArgument>();
    test_true     <CtorDefaultArgument>();
    test_true     <CtorExplicitDefaultArgument>();

    test_true     <Derived>();
    test_false    <Abstract>();
    test_false    <AbstractDestructor>();

    test_true     <OperatorNewDeleted>();

    test_true     <decltype(Lambda)>();
    test_not_const<void(*)(const int&)>();
    test_not_const<void(Empty::*)(const int&)               >();
    test_not_const<void(Empty::*)(const int&) const         >();
    test_not_const<void(Empty::*)(const int&)       volatile>();
    test_not_const<void(Empty::*)(const int&) const volatile>();
    test_not_const<void(Empty::*)(const int&) &>();
    test_not_const<void(Empty::*)(const int&) &&>();
    test_not_const<void(Empty::*)(const int&) noexcept>();
    test_not_const<void(Empty::*)(const int&) noexcept(true)>();
    test_not_const<void(Empty::*)(const int&) noexcept(false)>();

    // Sequence containers
    test_true     <std::array<               int, 0>>();
    test_not_const<std::array<               int, 1>>();
    test_false    <std::array<const          int, 1>>();
    test_not_const<std::array<      volatile int, 1>>();
    test_false    <std::array<const volatile int, 1>>();
    test_true     <std::deque<               int>>();
    test_true     <std::forward_list<int>>();
    test_true     <std::list<int>>();
    test_true     <std::vector<int>>();

    // Associative containers
    test_true     <std::set<int>>();
    test_true     <std::map<int, int>>();
    test_true     <std::multiset<int>>();
    test_true     <std::multimap<int, int>>();

    // Unordered associative containers
    test_true     <std::unordered_set<int>>();
    test_true     <std::unordered_map<int, int>>();
    test_true     <std::unordered_multiset<int>>();
    test_true     <std::unordered_multimap<int, int>>();

    // Container adaptors
    test_true     <std::stack<               int>>();
    test_true     <std::queue<int>>();
    test_true     <std::priority_queue<int>>();

    test_true     <std::span<               int>>();
    test_true     <std::span<const          int>>();
    test_true     <std::span<      volatile int>>();
    test_true     <std::span<const volatile int>>();

    // Strings
    test_true     <std::string>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
    test_true     <std::wstring>();
#endif
    test_true     <std::u8string>();
    test_true     <std::u16string>();
    test_true     <std::u32string>();

    // String views
    test_true     <std::string_view>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
    test_true     <std::wstring_view>();
#endif
    test_true     <std::u8string_view>();
    test_true     <std::u16string_view>();
    test_true     <std::u32string_view>();

    // Smart pointers
    test_true     <std::unique_ptr<int>>();
    test_true     <std::shared_ptr<int>>();
    test_true     <std::weak_ptr<int>>();

}
