//===----------------------------------------------------------------------===//
//
// 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 From, class To>
// concept convertible_to;

#include <concepts>
#include <type_traits>

namespace {
enum ClassicEnum { a, b };
enum class ScopedEnum { x, y };
struct Empty {};
using nullptr_t = decltype(nullptr);

template <class T, class U>
void CheckConvertibleTo() {
  static_assert(std::convertible_to<T, U>);
  static_assert(std::convertible_to<const T, U>);
  static_assert(std::convertible_to<T, const U>);
  static_assert(std::convertible_to<const T, const U>);
}

template <class T, class U>
void CheckNotConvertibleTo() {
  static_assert(!std::convertible_to<T, U>);
  static_assert(!std::convertible_to<const T, U>);
  static_assert(!std::convertible_to<T, const U>);
  static_assert(!std::convertible_to<const T, const U>);
}

template <class T, class U>
void CheckIsConvertibleButNotConvertibleTo() {
  // Sanity check T is either implicitly xor explicitly convertible to U.
  static_assert(std::is_convertible_v<T, U>);
  static_assert(std::is_convertible_v<const T, U>);
  static_assert(std::is_convertible_v<T, const U>);
  static_assert(std::is_convertible_v<const T, const U>);
  CheckNotConvertibleTo<T, U>();
}

// Tests that should objectively return false (except for bool and nullptr_t)
template <class T>
constexpr void CommonlyNotConvertibleTo() {
  CheckNotConvertibleTo<T, void>();
  CheckNotConvertibleTo<T, nullptr_t>();
  CheckNotConvertibleTo<T, T*>();
  CheckNotConvertibleTo<T, T Empty::*>();
  CheckNotConvertibleTo<T, T (Empty::*)()>();
  CheckNotConvertibleTo<T, T[sizeof(T)]>();
  CheckNotConvertibleTo<T, T (*)()>();
  CheckNotConvertibleTo<T, T (&)()>();
  CheckNotConvertibleTo<T, T(&&)()>();
}

template <std::same_as<bool> >
constexpr void CommonlyNotConvertibleTo() {
  CheckNotConvertibleTo<bool, void>();
  CheckNotConvertibleTo<bool, nullptr_t>();
  CheckConvertibleTo<bool Empty::*, bool>();
  CheckConvertibleTo<bool (Empty::*)(), bool>();
  CheckConvertibleTo<bool[2], bool>();
  CheckConvertibleTo<bool (*)(), bool>();
  CheckConvertibleTo<bool (&)(), bool>();
  CheckConvertibleTo<bool(&&)(), bool>();
}

template <std::same_as<nullptr_t> >
constexpr void CommonlyNotConvertibleTo() {
  CheckNotConvertibleTo<nullptr_t, void>();
  CheckConvertibleTo<nullptr_t, nullptr_t>();
  CheckConvertibleTo<nullptr_t, void*>();
  CheckConvertibleTo<nullptr_t, int Empty::*>();
  CheckConvertibleTo<nullptr_t, void (Empty::*)()>();
  CheckNotConvertibleTo<nullptr_t, int[2]>();
  CheckConvertibleTo<nullptr_t, void (*)()>();
  CheckNotConvertibleTo<nullptr_t, void (&)()>();
  CheckNotConvertibleTo<nullptr_t, void(&&)()>();
}
} // namespace

using Function = void();
using NoexceptFunction = void() noexcept;
using ConstFunction = void() const;
using Array = char[1];

struct StringType {
  StringType(const char*) {}
};

class NonCopyable {
  NonCopyable(NonCopyable&);
};

template <typename T>
class CannotInstantiate {
  enum { X = T::ThisExpressionWillBlowUp };
};

struct abstract {
  virtual int f() = 0;
};

struct ExplicitlyConvertible;
struct ImplicitlyConvertible;

struct ExplicitlyConstructible {
  explicit ExplicitlyConstructible(int);
  explicit ExplicitlyConstructible(ExplicitlyConvertible);
  explicit ExplicitlyConstructible(ImplicitlyConvertible) = delete;
};

struct ExplicitlyConvertible {
  explicit operator ExplicitlyConstructible() const {
    return ExplicitlyConstructible(0);
  }
};

struct ImplicitlyConstructible;

struct ImplicitlyConvertible {
  operator ExplicitlyConstructible() const;
  operator ImplicitlyConstructible() const = delete;
};

struct ImplicitlyConstructible {
  ImplicitlyConstructible(ImplicitlyConvertible);
};

int main(int, char**) {
  // void
  CheckConvertibleTo<void, void>();
  CheckNotConvertibleTo<void, Function>();
  CheckNotConvertibleTo<void, Function&>();
  CheckNotConvertibleTo<void, Function*>();
  CheckNotConvertibleTo<void, NoexceptFunction>();
  CheckNotConvertibleTo<void, NoexceptFunction&>();
  CheckNotConvertibleTo<void, NoexceptFunction*>();
  CheckNotConvertibleTo<void, Array>();
  CheckNotConvertibleTo<void, Array&>();
  CheckNotConvertibleTo<void, char>();
  CheckNotConvertibleTo<void, char&>();
  CheckNotConvertibleTo<void, char*>();
  CheckNotConvertibleTo<char, void>();

  // Function
  CheckNotConvertibleTo<Function, void>();
  CheckNotConvertibleTo<Function, Function>();
  CheckNotConvertibleTo<Function, NoexceptFunction>();
  CheckNotConvertibleTo<Function, NoexceptFunction&>();
  CheckNotConvertibleTo<Function, NoexceptFunction*>();
  CheckNotConvertibleTo<Function, NoexceptFunction* const>();
  CheckConvertibleTo<Function, Function&>();
  CheckConvertibleTo<Function, Function*>();
  CheckConvertibleTo<Function, Function* const>();

  static_assert(std::convertible_to<Function, Function&&>);
  static_assert(!std::convertible_to<Function, NoexceptFunction&&>);

  CheckNotConvertibleTo<Function, Array>();
  CheckNotConvertibleTo<Function, Array&>();
  CheckNotConvertibleTo<Function, char>();
  CheckNotConvertibleTo<Function, char&>();
  CheckNotConvertibleTo<Function, char*>();

  // Function&
  CheckNotConvertibleTo<Function&, void>();
  CheckNotConvertibleTo<Function&, Function>();
  CheckConvertibleTo<Function&, Function&>();

  CheckConvertibleTo<Function&, Function*>();
  CheckNotConvertibleTo<Function&, Array>();
  CheckNotConvertibleTo<Function&, Array&>();
  CheckNotConvertibleTo<Function&, char>();
  CheckNotConvertibleTo<Function&, char&>();
  CheckNotConvertibleTo<Function&, char*>();

  // Function*
  CheckNotConvertibleTo<Function*, void>();
  CheckNotConvertibleTo<Function*, Function>();
  CheckNotConvertibleTo<Function*, Function&>();
  CheckConvertibleTo<Function*, Function*>();

  CheckNotConvertibleTo<Function*, Array>();
  CheckNotConvertibleTo<Function*, Array&>();
  CheckNotConvertibleTo<Function*, char>();
  CheckNotConvertibleTo<Function*, char&>();
  CheckNotConvertibleTo<Function*, char*>();

  // Non-referencable function type
  static_assert(!std::convertible_to<ConstFunction, Function>);
  static_assert(!std::convertible_to<ConstFunction, Function*>);
  static_assert(!std::convertible_to<ConstFunction, Function&>);
  static_assert(!std::convertible_to<ConstFunction, Function&&>);
  static_assert(!std::convertible_to<Function*, ConstFunction>);
  static_assert(!std::convertible_to<Function&, ConstFunction>);
  static_assert(!std::convertible_to<ConstFunction, ConstFunction>);
  static_assert(!std::convertible_to<ConstFunction, void>);

  // NoexceptFunction
  CheckNotConvertibleTo<NoexceptFunction, void>();
  CheckNotConvertibleTo<NoexceptFunction, Function>();
  CheckNotConvertibleTo<NoexceptFunction, NoexceptFunction>();
  CheckConvertibleTo<NoexceptFunction, NoexceptFunction&>();
  CheckConvertibleTo<NoexceptFunction, NoexceptFunction*>();
  CheckConvertibleTo<NoexceptFunction, NoexceptFunction* const>();
  CheckConvertibleTo<NoexceptFunction, Function&>();
  CheckConvertibleTo<NoexceptFunction, Function*>();
  CheckConvertibleTo<NoexceptFunction, Function* const>();

  static_assert(std::convertible_to<NoexceptFunction, Function&&>);
  static_assert(std::convertible_to<NoexceptFunction, NoexceptFunction&&>);

  CheckNotConvertibleTo<NoexceptFunction, Array>();
  CheckNotConvertibleTo<NoexceptFunction, Array&>();
  CheckNotConvertibleTo<NoexceptFunction, char>();
  CheckNotConvertibleTo<NoexceptFunction, char&>();
  CheckNotConvertibleTo<NoexceptFunction, char*>();

  // NoexceptFunction&
  CheckNotConvertibleTo<NoexceptFunction&, void>();
  CheckNotConvertibleTo<NoexceptFunction&, Function>();
  CheckNotConvertibleTo<NoexceptFunction&, NoexceptFunction>();
  CheckConvertibleTo<NoexceptFunction&, Function&>();
  CheckConvertibleTo<NoexceptFunction&, NoexceptFunction&>();

  CheckConvertibleTo<NoexceptFunction&, Function*>();
  CheckConvertibleTo<NoexceptFunction&, NoexceptFunction*>();
  CheckNotConvertibleTo<NoexceptFunction&, Array>();
  CheckNotConvertibleTo<NoexceptFunction&, Array&>();
  CheckNotConvertibleTo<NoexceptFunction&, char>();
  CheckNotConvertibleTo<NoexceptFunction&, char&>();
  CheckNotConvertibleTo<NoexceptFunction&, char*>();

  // NoexceptFunction*
  CheckNotConvertibleTo<NoexceptFunction*, void>();
  CheckNotConvertibleTo<NoexceptFunction*, Function>();
  CheckNotConvertibleTo<NoexceptFunction*, Function&>();
  CheckNotConvertibleTo<NoexceptFunction*, NoexceptFunction>();
  CheckNotConvertibleTo<NoexceptFunction*, NoexceptFunction&>();
  CheckConvertibleTo<NoexceptFunction*, Function*>();
  CheckConvertibleTo<NoexceptFunction*, NoexceptFunction*>();

  CheckNotConvertibleTo<NoexceptFunction*, Array>();
  CheckNotConvertibleTo<NoexceptFunction*, Array&>();
  CheckNotConvertibleTo<NoexceptFunction*, char>();
  CheckNotConvertibleTo<NoexceptFunction*, char&>();
  CheckNotConvertibleTo<NoexceptFunction*, char*>();

  // Array
  CheckNotConvertibleTo<Array, void>();
  CheckNotConvertibleTo<Array, Function>();
  CheckNotConvertibleTo<Array, Function&>();
  CheckNotConvertibleTo<Array, Function*>();
  CheckNotConvertibleTo<Array, NoexceptFunction>();
  CheckNotConvertibleTo<Array, NoexceptFunction&>();
  CheckNotConvertibleTo<Array, NoexceptFunction*>();
  CheckNotConvertibleTo<Array, Array>();

  static_assert(!std::convertible_to<Array, Array&>);
  static_assert(std::convertible_to<Array, const Array&>);
  static_assert(!std::convertible_to<Array, const volatile Array&>);

  static_assert(!std::convertible_to<const Array, Array&>);
  static_assert(std::convertible_to<const Array, const Array&>);
  static_assert(!std::convertible_to<Array, volatile Array&>);
  static_assert(!std::convertible_to<Array, const volatile Array&>);

  static_assert(std::convertible_to<Array, Array&&>);
  static_assert(std::convertible_to<Array, const Array&&>);
  static_assert(std::convertible_to<Array, volatile Array&&>);
  static_assert(std::convertible_to<Array, const volatile Array&&>);
  static_assert(std::convertible_to<const Array, const Array&&>);
  static_assert(!std::convertible_to<Array&, Array&&>);
  static_assert(!std::convertible_to<Array&&, Array&>);

  CheckNotConvertibleTo<Array, char>();
  CheckNotConvertibleTo<Array, char&>();

  static_assert(std::convertible_to<Array, char*>);
  static_assert(std::convertible_to<Array, const char*>);
  static_assert(std::convertible_to<Array, char* const>);
  static_assert(std::convertible_to<Array, char* const volatile>);

  static_assert(!std::convertible_to<const Array, char*>);
  static_assert(std::convertible_to<const Array, const char*>);

  static_assert(!std::convertible_to<char[42][42], char*>);
  static_assert(!std::convertible_to<char[][1], char*>);

  // Array&
  CheckNotConvertibleTo<Array&, void>();
  CheckNotConvertibleTo<Array&, Function>();
  CheckNotConvertibleTo<Array&, Function&>();
  CheckNotConvertibleTo<Array&, Function*>();
  CheckNotConvertibleTo<Array&, NoexceptFunction>();
  CheckNotConvertibleTo<Array&, NoexceptFunction&>();
  CheckNotConvertibleTo<Array&, NoexceptFunction*>();
  CheckNotConvertibleTo<Array&, Array>();

  static_assert(std::convertible_to<Array&, Array&>);
  static_assert(std::convertible_to<Array&, const Array&>);
  static_assert(!std::convertible_to<const Array&, Array&>);
  static_assert(std::convertible_to<const Array&, const Array&>);

  CheckNotConvertibleTo<Array&, char>();
  CheckNotConvertibleTo<Array&, char&>();

  static_assert(std::convertible_to<Array&, char*>);
  static_assert(std::convertible_to<Array&, const char*>);
  static_assert(!std::convertible_to<const Array&, char*>);
  static_assert(std::convertible_to<const Array&, const char*>);

  static_assert(std::convertible_to<Array, StringType>);
  static_assert(std::convertible_to<char(&)[], StringType>);

  // char
  CheckNotConvertibleTo<char, void>();
  CheckNotConvertibleTo<char, Function>();
  CheckNotConvertibleTo<char, Function&>();
  CheckNotConvertibleTo<char, Function*>();
  CheckNotConvertibleTo<char, NoexceptFunction>();
  CheckNotConvertibleTo<char, NoexceptFunction&>();
  CheckNotConvertibleTo<char, NoexceptFunction*>();
  CheckNotConvertibleTo<char, Array>();
  CheckNotConvertibleTo<char, Array&>();

  CheckConvertibleTo<char, char>();

  static_assert(!std::convertible_to<char, char&>);
  static_assert(std::convertible_to<char, const char&>);
  static_assert(!std::convertible_to<const char, char&>);
  static_assert(std::convertible_to<const char, const char&>);

  CheckNotConvertibleTo<char, char*>();

  // char&
  CheckNotConvertibleTo<char&, void>();
  CheckNotConvertibleTo<char&, Function>();
  CheckNotConvertibleTo<char&, Function&>();
  CheckNotConvertibleTo<char&, Function*>();
  CheckNotConvertibleTo<char&, NoexceptFunction>();
  CheckNotConvertibleTo<char&, NoexceptFunction&>();
  CheckNotConvertibleTo<char&, NoexceptFunction*>();
  CheckNotConvertibleTo<char&, Array>();
  CheckNotConvertibleTo<char&, Array&>();

  CheckConvertibleTo<char&, char>();

  static_assert(std::convertible_to<char&, char&>);
  static_assert(std::convertible_to<char&, const char&>);
  static_assert(!std::convertible_to<const char&, char&>);
  static_assert(std::convertible_to<const char&, const char&>);

  CheckNotConvertibleTo<char&, char*>();

  // char*
  CheckNotConvertibleTo<char*, void>();
  CheckNotConvertibleTo<char*, Function>();
  CheckNotConvertibleTo<char*, Function&>();
  CheckNotConvertibleTo<char*, Function*>();
  CheckNotConvertibleTo<char*, NoexceptFunction>();
  CheckNotConvertibleTo<char*, NoexceptFunction&>();
  CheckNotConvertibleTo<char*, NoexceptFunction*>();
  CheckNotConvertibleTo<char*, Array>();
  CheckNotConvertibleTo<char*, Array&>();

  CheckNotConvertibleTo<char*, char>();
  CheckNotConvertibleTo<char*, char&>();

  static_assert(std::convertible_to<char*, char*>);
  static_assert(std::convertible_to<char*, const char*>);
  static_assert(!std::convertible_to<const char*, char*>);
  static_assert(std::convertible_to<const char*, const char*>);

  // NonCopyable
  static_assert(std::convertible_to<NonCopyable&, NonCopyable&>);
  static_assert(std::convertible_to<NonCopyable&, const NonCopyable&>);
  static_assert(std::convertible_to<NonCopyable&, const volatile NonCopyable&>);
  static_assert(std::convertible_to<NonCopyable&, volatile NonCopyable&>);
  static_assert(std::convertible_to<const NonCopyable&, const NonCopyable&>);
  static_assert(
      std::convertible_to<const NonCopyable&, const volatile NonCopyable&>);
  static_assert(
      std::convertible_to<volatile NonCopyable&, const volatile NonCopyable&>);
  static_assert(std::convertible_to<const volatile NonCopyable&,
                                    const volatile NonCopyable&>);
  static_assert(!std::convertible_to<const NonCopyable&, NonCopyable&>);

  // This test requires Access control SFINAE which we only have in C++11 or when
  // we are using the compiler builtin for convertible_to.
  CheckNotConvertibleTo<NonCopyable&, NonCopyable>();

  // Ensure that CannotInstantiate is not instantiated by convertible_to when it is not needed.
  // For example CannotInstantiate is instantiated as a part of ADL lookup for arguments of type CannotInstantiate*.
  static_assert(
      std::convertible_to<CannotInstantiate<int>*, CannotInstantiate<int>*>);

  // Test for PR13592
  static_assert(!std::convertible_to<abstract, abstract>);

  CommonlyNotConvertibleTo<int>();
  CommonlyNotConvertibleTo<bool>();
  CommonlyNotConvertibleTo<nullptr_t>();

  CheckNotConvertibleTo<int, ExplicitlyConstructible>();
  CheckNotConvertibleTo<ExplicitlyConvertible, ExplicitlyConstructible>();
  CheckNotConvertibleTo<ExplicitlyConstructible, ExplicitlyConvertible>();
  CheckIsConvertibleButNotConvertibleTo<ImplicitlyConvertible,
                                        ExplicitlyConstructible>();
  CheckNotConvertibleTo<ImplicitlyConstructible, ImplicitlyConvertible>();

  return 0;
}
