// RUN: %clang_cc1 -fsyntax-only -Wno-ext-cxx-type-aware-allocators -verify %s -DNO_TADD -std=c++26    -fsized-deallocation    -faligned-allocation
// RUN: %clang_cc1 -fsyntax-only -Wno-ext-cxx-type-aware-allocators -verify %s -DNO_TADD -std=c++26 -fno-sized-deallocation    -faligned-allocation
// RUN: %clang_cc1 -fsyntax-only -Wno-ext-cxx-type-aware-allocators -verify %s -DNO_TADD -std=c++26 -fno-sized-deallocation -fno-aligned-allocation
// RUN: %clang_cc1 -fsyntax-only -Wno-ext-cxx-type-aware-allocators -verify %s -DNO_TADD -std=c++26    -fsized-deallocation -fno-aligned-allocation
namespace std {
  template <class T> struct type_identity {
    typedef T type;
  };
  enum class align_val_t : __SIZE_TYPE__ {};
  struct destroying_delete_t { explicit destroying_delete_t() = default; };
}

using size_t = __SIZE_TYPE__;


template <class Tp> struct is_const {
  static const bool value = false;
};
template <class Tp> struct is_const<Tp const> {
  static const bool value = true;
};

template <class Tp> struct is_volatile {
  static const bool value = false;
};
template <class Tp> struct is_volatile<Tp volatile> {
  static const bool value = true;
};

template <class T> static const bool is_const_v = is_const<T>::value;
template <class T> static const bool is_volatile_v = is_volatile<T>::value;

struct VerifyQualifiers {
  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) throw() {
    static_assert(is_const_v<T> == false); // #1
    static_assert(is_volatile_v<T> == false); // #2
    return 0;
  }
  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) {
    static_assert(is_const_v<T> == false); // #3
    static_assert(is_volatile_v<T> == false); // #4
  }
  template <typename T> void *operator new(std::type_identity<_Atomic T>, size_t, std::align_val_t) throw() {
    static_assert(is_const_v<T> == false);
    static_assert(is_volatile_v<T> == false);
  }
};

void *operator new(std::type_identity<VerifyQualifiers> type, size_t, std::align_val_t) throw() { // #11
  static_assert(is_const_v<typename decltype(type)::type> == false); // #5
  static_assert(is_volatile_v<typename decltype(type)::type> == false); // #6
  return 0;
}

void operator delete(std::type_identity<VerifyQualifiers> type, void*, size_t, std::align_val_t) {
  static_assert(is_const_v<typename decltype(type)::type> == false); // #7
  static_assert(is_volatile_v<typename decltype(type)::type> == false); // #8
}

void *operator new(std::type_identity<int>, size_t, std::align_val_t) throw() = delete; // #12
void operator delete(std::type_identity<int>, void*, size_t, std::align_val_t) = delete;

struct TestAtomic1 {

};
struct TestAtomic2 {
};

void *operator new(std::type_identity<TestAtomic1>, size_t, std::align_val_t) throw() = delete; // #13
void operator delete(std::type_identity<_Atomic TestAtomic1>, void*, size_t, std::align_val_t) = delete; // #9
void *operator new(std::type_identity<_Atomic TestAtomic2>, size_t, std::align_val_t) = delete; // #10
void operator delete(std::type_identity<TestAtomic2>, void*, size_t, std::align_val_t) = delete;

// Success tests
void test_member_allocators() {
  auto *unqualified_obj = new VerifyQualifiers();
  delete unqualified_obj;
  auto *const_obj = new const VerifyQualifiers();
  delete const_obj;
  auto *volatile_obj = new volatile VerifyQualifiers();
  delete volatile_obj;
  auto *const_volatile_obj = new const volatile VerifyQualifiers();
  delete const_volatile_obj;
  auto *atomic_obj = new _Atomic VerifyQualifiers();
  delete atomic_obj;
  auto *atomic_test1 = new _Atomic TestAtomic1;
  delete atomic_test1;
  // expected-error@-1 {{attempt to use a deleted function}}
  // expected-note@#9 {{'operator delete' has been explicitly marked deleted here}}
  auto *atomic_test2 = new _Atomic TestAtomic2;
  // expected-error@-1 {{call to deleted function 'operator new'}}
  // expected-note@#10 {{candidate function has been explicitly deleted}}
  // expected-note@#11 {{candidate function not viable}}
  // expected-note@#12 {{candidate function not viable}}
  // expected-note@#13 {{candidate function not viable}}
  delete atomic_test2;
}



void test_global_allocators() {
  auto *unqualified_obj = ::new VerifyQualifiers();
  ::delete unqualified_obj;
  auto *const_obj = ::new const VerifyQualifiers();
  ::delete const_obj;
  auto *volatile_obj = ::new volatile VerifyQualifiers();
  ::delete volatile_obj;
  auto *const_volatile_obj = ::new const volatile VerifyQualifiers();
  ::delete const_volatile_obj;
  _Atomic VerifyQualifiers *atomic_obj = ::new _Atomic VerifyQualifiers();
  ::delete atomic_obj;
  _Atomic int *atomic_int = new _Atomic int;
  delete atomic_int;
}
