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

// template<class T>
// concept copy_constructible;

#include <concepts>
#include <type_traits>

#include "type_classification/moveconstructible.h"

// Tests in this namespace are shared with moveconstructible.pass.cpp
// There are some interesting differences, so it's best if they're tested here
// too.
namespace MoveConstructibleTests {
static_assert(std::copy_constructible<int>);
static_assert(std::copy_constructible<int*>);
static_assert(std::copy_constructible<int&>);
static_assert(std::copy_constructible<const int>);
static_assert(std::copy_constructible<const int&>);
static_assert(std::copy_constructible<volatile int>);
static_assert(std::copy_constructible<volatile int&>);
static_assert(std::copy_constructible<int (*)()>);
static_assert(std::copy_constructible<int (&)()>);
static_assert(std::copy_constructible<HasDefaultOps>);
static_assert(std::copy_constructible<const CustomMoveCtor&>);
static_assert(std::copy_constructible<volatile CustomMoveCtor&>);
static_assert(std::copy_constructible<const CustomMoveAssign&>);
static_assert(std::copy_constructible<volatile CustomMoveAssign&>);
static_assert(std::copy_constructible<int HasDefaultOps::*>);
static_assert(std::copy_constructible<void (HasDefaultOps::*)(int)>);
static_assert(std::copy_constructible<MemberLvalueReference>);

static_assert(!std::copy_constructible<void>);
static_assert(!std::copy_constructible<CustomMoveAssign>);
static_assert(!std::copy_constructible<const CustomMoveCtor>);
static_assert(!std::copy_constructible<volatile CustomMoveCtor>);
static_assert(!std::copy_constructible<const CustomMoveAssign>);
static_assert(!std::copy_constructible<volatile CustomMoveAssign>);
static_assert(!std::copy_constructible<int[10]>);
static_assert(!std::copy_constructible<DeletedMoveCtor>);
static_assert(!std::copy_constructible<ImplicitlyDeletedMoveCtor>);
static_assert(!std::copy_constructible<DeletedMoveAssign>);
static_assert(!std::copy_constructible<ImplicitlyDeletedMoveAssign>);

static_assert(std::copy_constructible<DeletedMoveCtor&>);
static_assert(std::copy_constructible<const DeletedMoveCtor&>);
static_assert(std::copy_constructible<ImplicitlyDeletedMoveCtor&>);
static_assert(std::copy_constructible<const ImplicitlyDeletedMoveCtor&>);
static_assert(std::copy_constructible<DeletedMoveAssign&>);
static_assert(std::copy_constructible<const DeletedMoveAssign&>);
static_assert(std::copy_constructible<ImplicitlyDeletedMoveAssign&>);
static_assert(std::copy_constructible<const ImplicitlyDeletedMoveAssign&>);

// different to moveconstructible.pass.cpp
static_assert(!std::copy_constructible<int&&>);
static_assert(!std::copy_constructible<const int&&>);
static_assert(!std::copy_constructible<volatile int&&>);
static_assert(!std::copy_constructible<CustomMoveCtor>);
static_assert(!std::copy_constructible<MoveOnly>);
static_assert(!std::copy_constructible<const CustomMoveCtor&&>);
static_assert(!std::copy_constructible<volatile CustomMoveCtor&&>);
static_assert(!std::copy_constructible<const CustomMoveAssign&&>);
static_assert(!std::copy_constructible<volatile CustomMoveAssign&&>);
static_assert(!std::copy_constructible<DeletedMoveCtor&&>);
static_assert(!std::copy_constructible<const DeletedMoveCtor&&>);
static_assert(!std::copy_constructible<ImplicitlyDeletedMoveCtor&&>);
static_assert(!std::copy_constructible<const ImplicitlyDeletedMoveCtor&&>);
static_assert(!std::copy_constructible<DeletedMoveAssign&&>);
static_assert(!std::copy_constructible<const DeletedMoveAssign&&>);
static_assert(!std::copy_constructible<ImplicitlyDeletedMoveAssign&&>);
static_assert(!std::copy_constructible<const ImplicitlyDeletedMoveAssign&&>);
static_assert(!std::copy_constructible<MemberRvalueReference>);
} // namespace MoveConstructibleTests

namespace CopyConstructibleTests {
struct CopyCtorUserDefined {
  CopyCtorUserDefined(CopyCtorUserDefined&&) noexcept = default;
  CopyCtorUserDefined(const CopyCtorUserDefined&);
};
static_assert(std::copy_constructible<CopyCtorUserDefined>);

struct CopyAssignUserDefined {
  CopyAssignUserDefined& operator=(CopyAssignUserDefined&&) noexcept = default;
  CopyAssignUserDefined& operator=(const CopyAssignUserDefined&);
};
static_assert(!std::copy_constructible<CopyAssignUserDefined>);

struct CopyCtorAndAssignUserDefined {
  CopyCtorAndAssignUserDefined(CopyCtorAndAssignUserDefined&&) noexcept =
      default;
  CopyCtorAndAssignUserDefined(const CopyCtorAndAssignUserDefined&);
  CopyCtorAndAssignUserDefined&
  operator=(CopyCtorAndAssignUserDefined&&) noexcept = default;
  CopyCtorAndAssignUserDefined& operator=(const CopyCtorAndAssignUserDefined&);
};
static_assert(std::copy_constructible<CopyCtorAndAssignUserDefined>);

struct CopyCtorDeleted {
  CopyCtorDeleted(CopyCtorDeleted&&) noexcept = default;
  CopyCtorDeleted(const CopyCtorDeleted&) = delete;
};
static_assert(!std::copy_constructible<CopyCtorDeleted>);

struct CopyAssignDeleted {
  CopyAssignDeleted(CopyAssignDeleted&&) noexcept = default;
  CopyAssignDeleted(const CopyAssignDeleted&) = delete;
};
static_assert(!std::copy_constructible<CopyAssignDeleted>);

struct CopyCtorHasMutableRef {
  CopyCtorHasMutableRef(CopyCtorHasMutableRef&&) noexcept = default;
  CopyCtorHasMutableRef(CopyCtorHasMutableRef&) = default;
};
static_assert(!std::copy_constructible<CopyCtorHasMutableRef>);

struct CopyCtorProhibitsMutableRef {
  CopyCtorProhibitsMutableRef(CopyCtorProhibitsMutableRef&&) noexcept = default;
  CopyCtorProhibitsMutableRef(const CopyCtorProhibitsMutableRef&) = default;
  CopyCtorProhibitsMutableRef(CopyCtorProhibitsMutableRef&) = delete;
};
static_assert(!std::copy_constructible<CopyCtorProhibitsMutableRef>);

struct CopyAssignHasMutableRef {
  CopyAssignHasMutableRef&
  operator=(CopyAssignHasMutableRef&&) noexcept = default;
  CopyAssignHasMutableRef& operator=(CopyAssignHasMutableRef&) = default;
};
static_assert(!std::copy_constructible<CopyAssignHasMutableRef>);

struct CopyAssignProhibitsMutableRef {
  CopyAssignProhibitsMutableRef&
  operator=(CopyAssignProhibitsMutableRef&&) noexcept = default;
  CopyAssignProhibitsMutableRef&
  operator=(const CopyAssignProhibitsMutableRef&) = default;
  CopyAssignProhibitsMutableRef&
  operator=(CopyAssignProhibitsMutableRef&) = delete;
};
static_assert(!std::copy_constructible<CopyAssignProhibitsMutableRef>);

struct CopyCtorOnly {
  CopyCtorOnly(CopyCtorOnly&&) noexcept = delete;
  CopyCtorOnly(const CopyCtorOnly&) = default;
};
static_assert(!std::copy_constructible<CopyCtorOnly>);

struct CopyAssignOnly {
  CopyAssignOnly& operator=(CopyAssignOnly&&) noexcept = delete;
  CopyAssignOnly& operator=(const CopyAssignOnly&) = default;
};
static_assert(!std::copy_constructible<CopyAssignOnly>);

struct CopyOnly {
  CopyOnly(CopyOnly&&) noexcept = delete;
  CopyOnly(const CopyOnly&) = default;

  CopyOnly& operator=(CopyOnly&&) noexcept = delete;
  CopyOnly& operator=(const CopyOnly&) = default;
};
static_assert(!std::copy_constructible<CopyOnly>);

struct ExplicitlyCopyable {
  ExplicitlyCopyable(ExplicitlyCopyable&&) = default;
  explicit ExplicitlyCopyable(const ExplicitlyCopyable&);
};
static_assert(!std::copy_constructible<ExplicitlyCopyable>);
} // namespace CopyConstructibleTests
