brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.4 KiB · a983745 Raw
568 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// MSVC warning C4244: 'argument': conversion from '_Ty' to 'int', possible loss of data10// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd424411 12// UNSUPPORTED: c++03, c++11, c++14, c++17, c++2013 14// template<class C, input_range R, class... Args> requires (!view<C>)15//   constexpr C to(R&& r, Args&&... args);     // Since C++2316 17#include <ranges>18 19#include <algorithm>20#include <array>21#include <cassert>22#include <vector>23#include "container.h"24#include "test_iterators.h"25#include "test_macros.h"26#include "test_range.h"27 28template <class Container, class Range, class... Args>29concept HasTo = requires (Range&& range, Args ...args) {30  std::ranges::to<Container>(std::forward<Range>(range), std::forward<Args>(args)...);31};32 33struct InputRange {34  int x = 0;35  constexpr cpp20_input_iterator<int*> begin() {36    return cpp20_input_iterator<int*>(&x);37  }38  constexpr sentinel_wrapper<cpp20_input_iterator<int*>> end() {39    return sentinel_wrapper<cpp20_input_iterator<int*>>(begin());40  }41};42static_assert(std::ranges::input_range<InputRange>);43 44struct common_cpp20_input_iterator {45  using value_type = int;46  using difference_type = long long;47  using iterator_concept = std::input_iterator_tag;48  // Deliberately not defining `iterator_category` to make sure this class satisfies the `input_iterator` concept but49  // would fail `derived_from<iterator_category, input_iterator_tag>`.50 51  int x = 0;52 53  // Copyable so that it can be used as a sentinel against itself.54  constexpr decltype(auto) operator*() const { return x; }55  constexpr common_cpp20_input_iterator& operator++() { return *this; }56  constexpr void operator++(int) {}57  constexpr friend bool operator==(common_cpp20_input_iterator, common_cpp20_input_iterator) { return true; }58};59static_assert(std::input_iterator<common_cpp20_input_iterator>);60static_assert(std::sentinel_for<common_cpp20_input_iterator, common_cpp20_input_iterator>);61template <class T>62concept HasIteratorCategory = requires {63  typename std::iterator_traits<T>::iterator_category;64};65static_assert(!HasIteratorCategory<common_cpp20_input_iterator>);66 67struct CommonInputRange {68  int x = 0;69  constexpr common_cpp20_input_iterator begin() { return {}; }70  constexpr common_cpp20_input_iterator end() { return begin(); }71};72static_assert(std::ranges::input_range<CommonInputRange>);73static_assert(std::ranges::common_range<CommonInputRange>);74 75struct CommonRange {76  int x = 0;77  constexpr forward_iterator<int*> begin() {78    return forward_iterator<int*>(&x);79  }80  constexpr forward_iterator<int*> end() {81    return begin();82  }83};84static_assert(std::ranges::input_range<CommonRange>);85static_assert(std::ranges::common_range<CommonRange>);86 87struct NonCommonRange {88  int x = 0;89  constexpr forward_iterator<int*> begin() {90    return forward_iterator<int*>(&x);91  }92  constexpr sentinel_wrapper<forward_iterator<int*>> end() {93    return sentinel_wrapper<forward_iterator<int*>>(begin());94  }95};96static_assert(std::ranges::input_range<NonCommonRange>);97static_assert(!std::ranges::common_range<NonCommonRange>);98static_assert(std::derived_from<99    typename std::iterator_traits<std::ranges::iterator_t<NonCommonRange>>::iterator_category,100    std::input_iterator_tag>);101 102using ContainerT = int;103static_assert(!std::ranges::view<ContainerT>);104static_assert(HasTo<ContainerT, InputRange>);105static_assert(!HasTo<test_view<forward_iterator>, InputRange>);106 107// Note: it's not possible to check the `input_range` constraint because if it's not satisfied, the pipe adaptor108// overload hijacks the call (it takes unconstrained variadic arguments).109 110// Check the exact constraints for each one of the cases inside `ranges::to`.111 112struct Empty {};113 114struct Fallback {115  using value_type = int;116 117  CtrChoice ctr_choice = CtrChoice::Invalid;118  int x = 0;119 120  constexpr Fallback() : ctr_choice(CtrChoice::DefaultCtrAndInsert) {}121  constexpr Fallback(Empty) : ctr_choice(CtrChoice::DefaultCtrAndInsert) {}122 123  constexpr void push_back(value_type) {}124  constexpr value_type* begin() { return &x; }125  constexpr value_type* end() { return &x; }126  std::size_t size() const { return 0; }127};128 129struct CtrDirectOrFallback : Fallback {130  using Fallback::Fallback;131  constexpr CtrDirectOrFallback(InputRange&&, int = 0) { ctr_choice = CtrChoice::DirectCtr; }132};133 134struct CtrFromRangeTOrFallback : Fallback {135  using Fallback::Fallback;136  constexpr CtrFromRangeTOrFallback(std::from_range_t, InputRange&&, int = 0) { ctr_choice = CtrChoice::FromRangeT; }137};138 139struct CtrBeginEndPairOrFallback : Fallback {140  using Fallback::Fallback;141  template <class Iter>142  constexpr CtrBeginEndPairOrFallback(Iter, Iter, int = 0) { ctr_choice = CtrChoice::BeginEndPair; }143};144 145template <bool HasSize>146struct MaybeSizedRange {147  int x = 0;148  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&x); }149  constexpr forward_iterator<int*> end() { return begin(); }150 151  constexpr std::size_t size() const152  requires HasSize {153    return 0;154  }155};156static_assert(std::ranges::sized_range<MaybeSizedRange<true>>);157static_assert(!std::ranges::sized_range<MaybeSizedRange<false>>);158 159template <bool HasCapacity = true, bool CapacityReturnsSizeT = true,160          bool HasMaxSize = true, bool MaxSizeReturnsSizeT = true>161struct Reservable : Fallback {162  bool reserve_called = false;163 164  using Fallback::Fallback;165 166  constexpr std::size_t capacity() const167  requires (HasCapacity && CapacityReturnsSizeT) {168    return 0;169  }170  constexpr int capacity() const171  requires (HasCapacity && !CapacityReturnsSizeT) {172    return 0;173  }174 175  constexpr std::size_t max_size() const176  requires (HasMaxSize && MaxSizeReturnsSizeT) {177    return 0;178  }179  constexpr int max_size() const180  requires (HasMaxSize && !MaxSizeReturnsSizeT) {181    return 0;182  }183 184  constexpr void reserve(std::size_t) {185    reserve_called = true;186  }187};188LIBCPP_STATIC_ASSERT(std::ranges::__reservable_container<Reservable<>>);189 190constexpr void test_constraints() {191  { // Case 1 -- construct directly from the range.192    { // (range)193      auto result = std::ranges::to<CtrDirectOrFallback>(InputRange());194      assert(result.ctr_choice == CtrChoice::DirectCtr);195    }196 197    { // (range, arg)198      auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), 1);199      assert(result.ctr_choice == CtrChoice::DirectCtr);200    }201 202    { // (range, convertible-to-arg)203      auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), 1.0);204      assert(result.ctr_choice == CtrChoice::DirectCtr);205    }206 207    { // (range, BAD_arg)208      auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), Empty());209      assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);210    }211  }212 213  { // Case 2 -- construct using the `from_range_t` tagged constructor.214    { // (range)215      auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange());216      assert(result.ctr_choice == CtrChoice::FromRangeT);217    }218 219    { // (range, arg)220      auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), 1);221      assert(result.ctr_choice == CtrChoice::FromRangeT);222    }223 224    { // (range, convertible-to-arg)225      auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), 1.0);226      assert(result.ctr_choice == CtrChoice::FromRangeT);227    }228 229    { // (range, BAD_arg)230      auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), Empty());231      assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);232    }233  }234 235  { // Case 3 -- construct from a begin-end iterator pair.236    { // (range)237      auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange());238      assert(result.ctr_choice == CtrChoice::BeginEndPair);239    }240 241    { // (range, arg)242      auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), 1);243      assert(result.ctr_choice == CtrChoice::BeginEndPair);244    }245 246    { // (range, convertible-to-arg)247      auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), 1.0);248      assert(result.ctr_choice == CtrChoice::BeginEndPair);249    }250 251    { // (BAD_range) -- not a common range.252      auto result = std::ranges::to<CtrBeginEndPairOrFallback>(NonCommonRange());253      assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);254    }255 256    { // (BAD_range) -- iterator type not derived from `input_iterator_tag`.257      auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonInputRange());258      assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);259    }260 261    { // (range, BAD_arg)262      auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), Empty());263      assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);264    }265  }266 267  { // Case 4 -- default-construct (or construct from the extra arguments) and insert, reserving the size if possible.268    // Note: it's not possible to check the constraints on the default constructor using this approach because there is269    // nothing to fall back to -- the call will result in a hard error.270    // However, it's possible to check the constraints on reserving the capacity.271 272    { // All constraints satisfied.273      using C = Reservable<>;274      auto result = std::ranges::to<C>(MaybeSizedRange<true>());275      assert(result.reserve_called);276    }277 278    { // !sized_range279      using C = Reservable<>;280      auto result = std::ranges::to<C>(MaybeSizedRange<false>());281      assert(!result.reserve_called);282    }283 284    { // Missing `capacity`.285      using C = Reservable</*HasCapacity=*/false>;286      auto result = std::ranges::to<C>(MaybeSizedRange<true>());287      assert(!result.reserve_called);288    }289 290    { // `capacity` doesn't return `size_type`.291      using C = Reservable</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/false>;292      auto result = std::ranges::to<C>(MaybeSizedRange<true>());293      assert(!result.reserve_called);294    }295 296    { // Missing `max_size`.297      using C = Reservable</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/false>;298      auto result = std::ranges::to<C>(MaybeSizedRange<true>());299      assert(!result.reserve_called);300    }301 302    { // `max_size` doesn't return `size_type`.303      using C = Reservable<304        /*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/true, /*MaxSizeReturnsSizeT=*/false>;305      auto result = std::ranges::to<C>(MaybeSizedRange<true>());306      assert(!result.reserve_called);307    }308  }309}310 311constexpr void test_ctr_choice_order() {312  std::array in = {1, 2, 3, 4, 5};313  int arg1 = 42;314  char arg2 = 'a';315 316  { // Case 1 -- construct directly from the given range.317    {318      using C = Container<int, CtrChoice::DirectCtr>;319      std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);320 321      assert(result.ctr_choice == CtrChoice::DirectCtr);322      assert(std::ranges::equal(result, in));323      assert((in | std::ranges::to<C>()) == result);324      auto closure = std::ranges::to<C>();325      assert((in | closure) == result);326    }327 328    { // Extra arguments.329      using C = Container<int, CtrChoice::DirectCtr>;330      std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);331 332      assert(result.ctr_choice == CtrChoice::DirectCtr);333      assert(std::ranges::equal(result, in));334      assert(result.extra_arg1 == arg1);335      assert(result.extra_arg2 == arg2);336      assert((in | std::ranges::to<C>(arg1, arg2)) == result);337      auto closure = std::ranges::to<C>(arg1, arg2);338      assert((in | closure) == result);339    }340  }341 342  { // Case 2 -- construct using the `from_range_t` tag.343    {344      using C = Container<int, CtrChoice::FromRangeT>;345      std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);346 347      assert(result.ctr_choice == CtrChoice::FromRangeT);348      assert(std::ranges::equal(result, in));349      assert((in | std::ranges::to<C>()) == result);350      auto closure = std::ranges::to<C>();351      assert((in | closure) == result);352    }353 354    { // Extra arguments.355      using C = Container<int, CtrChoice::FromRangeT>;356      std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);357 358      assert(result.ctr_choice == CtrChoice::FromRangeT);359      assert(std::ranges::equal(result, in));360      assert(result.extra_arg1 == arg1);361      assert(result.extra_arg2 == arg2);362      assert((in | std::ranges::to<C>(arg1, arg2)) == result);363      auto closure = std::ranges::to<C>(arg1, arg2);364      assert((in | closure) == result);365    }366  }367 368  { // Case 3 -- construct from a begin-end pair.369    {370      using C = Container<int, CtrChoice::BeginEndPair>;371      std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);372 373      assert(result.ctr_choice == CtrChoice::BeginEndPair);374      assert(std::ranges::equal(result, in));375      assert((in | std::ranges::to<C>()) == result);376      auto closure = std::ranges::to<C>();377      assert((in | closure) == result);378    }379 380    { // Extra arguments.381      using C = Container<int, CtrChoice::BeginEndPair>;382      std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);383 384      assert(result.ctr_choice == CtrChoice::BeginEndPair);385      assert(std::ranges::equal(result, in));386      assert(result.extra_arg1 == arg1);387      assert(result.extra_arg2 == arg2);388      assert((in | std::ranges::to<C>(arg1, arg2)) == result);389      auto closure = std::ranges::to<C>(arg1, arg2);390      assert((in | closure) == result);391    }392  }393 394  { // Case 4 -- default-construct then insert elements.395    auto case_4 = [in, arg1, arg2]<auto InserterChoice, bool CanReserve>() {396      using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice, CanReserve>;397      {398        [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);399 400        assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);401        assert(result.inserter_choice == InserterChoice);402        assert(std::ranges::equal(result, in));403 404        if constexpr (CanReserve) {405          assert(result.called_reserve);406        } else {407          assert(!result.called_reserve);408        }409 410        assert((in | std::ranges::to<C>()) == result);411        [[maybe_unused]] auto closure = std::ranges::to<C>();412        assert((in | closure) == result);413      }414 415      { // Extra arguments416        [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);417 418        assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);419        assert(result.inserter_choice == InserterChoice);420        assert(std::ranges::equal(result, in));421        assert(result.extra_arg1 == arg1);422        assert(result.extra_arg2 == arg2);423 424        if constexpr (CanReserve) {425          assert(result.called_reserve);426        } else {427          assert(!result.called_reserve);428        }429 430        assert((in | std::ranges::to<C>(arg1, arg2)) == result);431        [[maybe_unused]] auto closure = std::ranges::to<C>(arg1, arg2);432        assert((in | closure) == result);433      }434    };435 436    case_4.operator()<InserterChoice::Insert, false>();437    case_4.operator()<InserterChoice::Insert, true>();438    case_4.operator()<InserterChoice::Emplace, false>();439    case_4.operator()<InserterChoice::Emplace, true>();440    case_4.operator()<InserterChoice::PushBack, false>();441    case_4.operator()<InserterChoice::PushBack, true>();442    case_4.operator()<InserterChoice::EmplaceBack, false>();443    case_4.operator()<InserterChoice::EmplaceBack, true>();444  }445}446 447template <CtrChoice Rank>448struct NotARange {449  using value_type = int;450 451  constexpr NotARange(std::ranges::input_range auto&&)452  requires (Rank >= CtrChoice::DirectCtr)453  {}454 455  constexpr NotARange(std::from_range_t, std::ranges::input_range auto&&)456  requires (Rank >= CtrChoice::FromRangeT)457  {}458 459  template <class Iter>460  constexpr NotARange(Iter, Iter)461  requires (Rank >= CtrChoice::BeginEndPair)462  {}463 464  constexpr NotARange()465  requires (Rank >= CtrChoice::DefaultCtrAndInsert)466  = default;467 468  constexpr void push_back(int) {}469};470 471static_assert(!std::ranges::range<NotARange<CtrChoice::DirectCtr>>);472 473constexpr void test_lwg_3785() {474  // Test LWG 3785 ("`ranges::to` is over-constrained on the destination type being a range") -- make sure it's possible475  // to convert the given input range to a non-range type.476  std::array in = {1, 2, 3, 4, 5};477 478  {479    using C = NotARange<CtrChoice::DirectCtr>;480    [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);481  }482 483  {484    using C = NotARange<CtrChoice::FromRangeT>;485    [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);486  }487 488  {489    using C = NotARange<CtrChoice::BeginEndPair>;490    [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);491  }492 493  {494    using C = NotARange<CtrChoice::DefaultCtrAndInsert>;495    [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);496  }497}498 499constexpr void test_recursive() {500  using C1 = Container<int, CtrChoice::DirectCtr>;501  using C2 = Container<C1, CtrChoice::FromRangeT>;502  using C3 = Container<C2, CtrChoice::BeginEndPair>;503  using C4 = Container<C3, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack>;504  using A1 = std::array<int, 4>;505  using A2 = std::array<A1, 3>;506  using A3 = std::array<A2, 2>;507  using A4 = std::array<A3, 2>;508 509  A4 in = {};510  { // Fill the nested array with incremental values.511    int x = 0;512    for (auto& a3 : in) {513      for (auto& a2 : a3) {514        for (auto& a1 : a2) {515          for (int& el : a1) {516            el = x++;517          }518        }519      }520    }521  }522 523  std::same_as<C4> decltype(auto) result = std::ranges::to<C4>(in);524 525  assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);526 527  int expected_value = 0;528  for (auto& c3 : result) {529    assert(c3.ctr_choice == CtrChoice::BeginEndPair);530 531    for (auto& c2 : c3) {532      assert(c2.ctr_choice == CtrChoice::FromRangeT);533 534      for (auto& c1 : c2) {535        assert(c1.ctr_choice == CtrChoice::DirectCtr);536 537        for (int el : c1) {538          assert(el == expected_value);539          ++expected_value;540        }541      }542    }543  }544 545  assert((in | std::ranges::to<C4>()) == result);546 547  // LWG3984: ranges::to's recursion branch may be ill-formed548  auto in_owning_view = std::views::all(std::move(in));549  static_assert(!std::ranges::viewable_range<decltype((in_owning_view))>);550  assert(std::ranges::to<C4>(in_owning_view) == result);551}552 553constexpr bool test() {554  test_constraints();555  test_ctr_choice_order();556  test_lwg_3785();557  test_recursive();558 559  return true;560}561 562int main(int, char**) {563  test();564  static_assert(test());565 566  return 0;567}568