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

// ITER_TRAITS(I)

// -- If the qualified-id ITER_TRAITS(I)::iterator_concept is valid and names a
//   type, then ITER_CONCEPT(I) denotes that type.
// (1.2) -- Otherwise, if the qualified-id ITER_TRAITS(I)::iterator_category is
// valid and names a type, then ITER_CONCEPT(I) denotes that type.
// (1.3) -- Otherwise, if iterator_traits<I> names a specialization generated
// from the primary template, then ITER_CONCEPT(I) denotes
// random_access_iterator_tag.
// (1.4) -- Otherwise, ITER_CONCEPT(I) does not denote a type.

// ADDITIONAL_COMPILE_FLAGS: -Wno-private-header

#include "test_macros.h"

#include <__type_traits/is_valid_expansion.h>
#include <iterator>

struct OtherTag : std::input_iterator_tag {};
struct OtherTagTwo : std::output_iterator_tag {};

struct MyIter {
  using iterator_category = std::random_access_iterator_tag;
  using iterator_concept = int;
  using value_type = char;
  using difference_type = std::ptrdiff_t;
  using pointer = char*;
  using reference = char&;
};

struct MyIter2 {
  using iterator_category = OtherTag;
  using value_type = char;
  using difference_type = std::ptrdiff_t;
  using pointer = char*;
  using reference = char&;
};

struct MyIter3 {};

struct Empty {};
struct EmptyWithSpecial {};
template <>
struct std::iterator_traits<MyIter3> {
  using iterator_category = OtherTagTwo;
  using value_type = char;
  using difference_type = std::ptrdiff_t;
  using pointer = char*;
  using reference = char&;
};

template <>
struct std::iterator_traits<EmptyWithSpecial> {
  // empty non-default.
};

int main(int, char**) {
  // If the qualified-id ITER_TRAITS(I)::iterator_concept is valid and names a type,
  // then ITER_CONCEPT(I) denotes that type.
  {
#if TEST_STD_VER > 17
    ASSERT_SAME_TYPE(std::_ITER_CONCEPT<char*>, std::contiguous_iterator_tag);
#endif
    ASSERT_SAME_TYPE(std::_ITER_CONCEPT<MyIter>, int);
  }
  // Otherwise, if the qualified-id ITER_TRAITS(I)::iterator_category is valid
  // and names a type, then ITER_CONCEPT(I) denotes that type.
  {
    ASSERT_SAME_TYPE(std::_ITER_CONCEPT<MyIter2>, OtherTag);
    ASSERT_SAME_TYPE(std::_ITER_CONCEPT<MyIter3>, OtherTagTwo);
  }
  // FIXME - This requirement makes no sense to me. Why does an empty type with
  // an empty default iterator_traits get a category of random?
  {
    ASSERT_SAME_TYPE(std::_ITER_CONCEPT<Empty>, std::random_access_iterator_tag);
  }
  {
    static_assert(!std::_IsValidExpansion<std::_ITER_CONCEPT, EmptyWithSpecial>::value, "");
  }

  return 0;
}
