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

// iter_common_reference_t

#include <concepts>
#include <iterator>
#include <type_traits>

struct X { };

// value_type and dereferencing are the same
struct T1 {
  using value_type = X;
  X operator*() const;
};
static_assert(std::same_as<std::iter_common_reference_t<T1>, X>);

// value_type and dereferencing are the same (modulo qualifiers)
struct T2 {
  using value_type = X;
  X& operator*() const;
};
static_assert(std::same_as<std::iter_common_reference_t<T2>, X&>);

// There's a custom common reference between value_type and the type of dereferencing
struct A { };
struct B { };
struct Common { Common(A); Common(B); };
template <template <class> class TQual, template <class> class QQual>
struct std::basic_common_reference<A, B, TQual, QQual> {
  using type = Common;
};
template <template <class> class TQual, template <class> class QQual>
struct std::basic_common_reference<B, A, TQual, QQual>
  : std::basic_common_reference<A, B, TQual, QQual>
{ };

struct T3 {
  using value_type = A;
  B&& operator*() const;
};
static_assert(std::same_as<std::iter_common_reference_t<T3>, Common>);

// Make sure we're SFINAE-friendly
template <class T>
constexpr bool has_common_reference = requires {
  typename std::iter_common_reference_t<T>;
};
struct NotIndirectlyReadable { };
static_assert(!has_common_reference<NotIndirectlyReadable>);
