//===----------------------------------------------------------------------===//
//
// 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, c++20
// ADDITIONAL_COMPILE_FLAGS: -Xclang -verify-ignore-unexpected=error

// Test the mandates
// template<class U> constexpr T value_or(U&& v) const &;
// Mandates: is_copy_constructible_v<T> is true and is_convertible_v<U, T> is true.

// template<class U> constexpr T value_or(U&& v) &&;
// Mandates: is_move_constructible_v<T> is true and is_convertible_v<U, T> is true.

#include <expected>
#include <utility>

struct NonCopyable {
  NonCopyable(int) {}
  NonCopyable(const NonCopyable&) = delete;
};

struct NonMovable {
  NonMovable(int) {}
  NonMovable(NonMovable&&) = delete;
};

struct NotConvertibleFromInt {};

void test() {
  // const & overload
  // !is_copy_constructible_v<T>,
  {
    const std::expected<NonCopyable, int> f1{5};
    f1.value_or(5); // expected-note{{in instantiation of function template specialization 'std::expected<NonCopyable, int>::value_or<int>' requested here}}
    // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}value_type has to be copy constructible}}
  }

  // const & overload
  // !is_convertible_v<U, T>
  {
    const std::expected<NotConvertibleFromInt, int> f1{std::in_place};
    f1.value_or(5); // expected-note{{in instantiation of function template specialization 'std::expected<NotConvertibleFromInt, int>::value_or<int>' requested here}}
    //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}argument has to be convertible to value_type}}
  }

  // && overload
  // !is_move_constructible_v<T>,
  {
    std::expected<NonMovable, int> f1{5};
    std::move(f1).value_or(5); // expected-note{{in instantiation of function template specialization 'std::expected<NonMovable, int>::value_or<int>' requested here}}
    //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}value_type has to be move constructible}}
  }

  // && overload
  // !is_convertible_v<U, T>
  {
    std::expected<NotConvertibleFromInt, int> f1{std::in_place};
    std::move(f1).value_or(5); // expected-note{{in instantiation of function template specialization 'std::expected<NotConvertibleFromInt, int>::value_or<int>' requested here}}
    //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}argument has to be convertible to value_type}}
  }
}
