//===----------------------------------------------------------------------===//
//
// 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 LHS, class RHS>
// concept assignable_from =
//   std::is_lvalue_reference_v<LHS> &&
//   std::common_reference_with<
//     const std::remove_reference_t<LHS>&,
//     const std::remove_reference_t<RHS>&> &&
//   requires (LHS lhs, RHS&& rhs) {
//     { lhs = std::forward<RHS>(rhs) } -> std::same_as<LHS>;
//   };

#include <concepts>
#include <type_traits>

#include "MoveOnly.h"

struct NoCommonRef {
  NoCommonRef& operator=(const int&);
};
static_assert(std::is_assignable_v<NoCommonRef&, const int&>);
static_assert(!std::assignable_from<NoCommonRef&, const int&>); // no common reference type

struct Base {};
struct Derived : Base {};
static_assert(!std::assignable_from<Base*, Derived*>);
static_assert( std::assignable_from<Base*&, Derived*>);
static_assert( std::assignable_from<Base*&, Derived*&>);
static_assert( std::assignable_from<Base*&, Derived*&&>);
static_assert( std::assignable_from<Base*&, Derived* const>);
static_assert( std::assignable_from<Base*&, Derived* const&>);
static_assert( std::assignable_from<Base*&, Derived* const&&>);
static_assert(!std::assignable_from<Base*&, const Derived*>);
static_assert(!std::assignable_from<Base*&, const Derived*&>);
static_assert(!std::assignable_from<Base*&, const Derived*&&>);
static_assert(!std::assignable_from<Base*&, const Derived* const>);
static_assert(!std::assignable_from<Base*&, const Derived* const&>);
static_assert(!std::assignable_from<Base*&, const Derived* const&&>);
static_assert( std::assignable_from<const Base*&, Derived*>);
static_assert( std::assignable_from<const Base*&, Derived*&>);
static_assert( std::assignable_from<const Base*&, Derived*&&>);
static_assert( std::assignable_from<const Base*&, Derived* const>);
static_assert( std::assignable_from<const Base*&, Derived* const&>);
static_assert( std::assignable_from<const Base*&, Derived* const&&>);
static_assert( std::assignable_from<const Base*&, const Derived*>);
static_assert( std::assignable_from<const Base*&, const Derived*&>);
static_assert( std::assignable_from<const Base*&, const Derived*&&>);
static_assert( std::assignable_from<const Base*&, const Derived* const>);
static_assert( std::assignable_from<const Base*&, const Derived* const&>);
static_assert( std::assignable_from<const Base*&, const Derived* const&&>);

struct VoidResultType {
    void operator=(const VoidResultType&);
};
static_assert(std::is_assignable_v<VoidResultType&, const VoidResultType&>);
static_assert(!std::assignable_from<VoidResultType&, const VoidResultType&>);

struct ValueResultType {
    ValueResultType operator=(const ValueResultType&);
};
static_assert(std::is_assignable_v<ValueResultType&, const ValueResultType&>);
static_assert(!std::assignable_from<ValueResultType&, const ValueResultType&>);

struct Locale {
    const Locale& operator=(const Locale&);
};
static_assert(std::is_assignable_v<Locale&, const Locale&>);
static_assert(!std::assignable_from<Locale&, const Locale&>);

struct Tuple {
    Tuple& operator=(const Tuple&);
    const Tuple& operator=(const Tuple&) const;
};
static_assert(!std::assignable_from<Tuple, const Tuple&>);
static_assert( std::assignable_from<Tuple&, const Tuple&>);
static_assert(!std::assignable_from<Tuple&&, const Tuple&>);
static_assert(!std::assignable_from<const Tuple, const Tuple&>);
static_assert( std::assignable_from<const Tuple&, const Tuple&>);
static_assert(!std::assignable_from<const Tuple&&, const Tuple&>);

// Finally, check a few simple cases.
static_assert( std::assignable_from<int&, int>);
static_assert( std::assignable_from<int&, int&>);
static_assert( std::assignable_from<int&, int&&>);
static_assert(!std::assignable_from<const int&, int>);
static_assert(!std::assignable_from<const int&, int&>);
static_assert(!std::assignable_from<const int&, int&&>);
static_assert( std::assignable_from<volatile int&, int>);
static_assert( std::assignable_from<volatile int&, int&>);
static_assert( std::assignable_from<volatile int&, int&&>);
static_assert(!std::assignable_from<int(&)[10], int>);
static_assert(!std::assignable_from<int(&)[10], int(&)[10]>);
static_assert( std::assignable_from<MoveOnly&, MoveOnly>);
static_assert(!std::assignable_from<MoveOnly&, MoveOnly&>);
static_assert( std::assignable_from<MoveOnly&, MoveOnly&&>);
static_assert(!std::assignable_from<void, int>);
static_assert(!std::assignable_from<void, void>);
