//===----------------------------------------------------------------------===//
//
// 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: no-exceptions

// ensure that __uninitialized_allocator_copy calls the proper construct and destruct functions

#include <algorithm>
#include <iterator>
#include <memory>

#include "test_allocator.h"

template <class T>
class construct_counting_allocator {
public:
  using value_type = T;

  int* constructed_count_;
  int* max_constructed_count_;

  construct_counting_allocator(int* constructed_count, int* max_constructed_count)
      : constructed_count_(constructed_count), max_constructed_count_(max_constructed_count) {}

  template <class... Args>
  void construct(T* ptr, Args&&... args) {
    ::new (static_cast<void*>(ptr)) T(args...);
    ++*constructed_count_;
    *max_constructed_count_ = std::max(*max_constructed_count_, *constructed_count_);
  }

  void destroy(T* ptr) {
    --*constructed_count_;
    ptr->~T();
  }
};

int throw_if_zero = 15;

struct ThrowSometimes {
  ThrowSometimes() = default;
  ThrowSometimes(const ThrowSometimes&) {
    if (--throw_if_zero == 0)
      throw 1;
  }
};

int main(int, char**) {
  int constructed_count     = 0;
  int max_constructed_count = 0;
  construct_counting_allocator<ThrowSometimes> alloc(&constructed_count, &max_constructed_count);
  ThrowSometimes in[20];
  TEST_ALIGNAS_TYPE(ThrowSometimes) char out[sizeof(ThrowSometimes) * 20];
  try {
    std::__uninitialized_allocator_copy(
        alloc, std::begin(in), std::end(in), reinterpret_cast<ThrowSometimes*>(std::begin(out)));
  } catch (...) {
  }

  assert(constructed_count == 0);
  assert(max_constructed_count == 14);
}
