On Wed, 26 Mar 2025, Tomasz Kamiński wrote:

> As pointed out by Hewill Kang (reporter) in the issue, checking if iterator
> of the incoming range satisfies __cpp17_input_iterator, may still lead
> to hard errors inside of insert_range for iterators that satisfies
> that concept, but specialize iterator_traits without iterator_category
> typedef (std::common_iterator specialize iterator_traits without
> iterator_category in some cases).
> 
> To address that we instead check if the iterator_traits<It>::iterator_category
> is present and denote at least input_iterator_tag, using existing 
> __has_input_iter_cat.

LGTM as well, sorry for not catching this.  I forgot that
__cpp17_input_iterator doesn't actually look at the type's
iterator_category since IIUC its purpose is to infer C++17
iterator-ness from a C++20 iterator.

> 
>       PR libstdc++/119415
> 
> libstdc++-v3/ChangeLog:
> 
>       * include/std/flat_set (_Flat_set_impl:insert_range):
>       Replace __detail::__cpp17_input_iterator with __has_input_iter_cat.
>       * testsuite/23_containers/flat_multiset/1.cc: New tests
>       * testsuite/23_containers/flat_set/1.cc: New tests
> ---
> Tested on x86_64-linux. OK for trunk?
>  libstdc++-v3/include/std/flat_set             |  2 +-
>  .../23_containers/flat_multiset/1.cc          | 57 +++++++++++++++++--
>  .../testsuite/23_containers/flat_set/1.cc     | 57 +++++++++++++++++--
>  3 files changed, 103 insertions(+), 13 deletions(-)
> 
> diff --git a/libstdc++-v3/include/std/flat_set 
> b/libstdc++-v3/include/std/flat_set
> index bab56742ddd..a7b0b8aef15 100644
> --- a/libstdc++-v3/include/std/flat_set
> +++ b/libstdc++-v3/include/std/flat_set
> @@ -481,7 +481,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         if constexpr (requires { _M_cont.insert_range(_M_cont.end(), __rg); })
>           __it = _M_cont.insert_range(_M_cont.end(), __rg);
>         else if constexpr (ranges::common_range<_Rg>
> -                          && 
> __detail::__cpp17_input_iterator<ranges::iterator_t<_Rg>>)
> +                          && __has_input_iter_cat<ranges::iterator_t<_Rg>>)
>           __it = _M_cont.insert(_M_cont.end(), ranges::begin(__rg), 
> ranges::end(__rg));
>         else
>           {
> diff --git a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc 
> b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
> index cc31164315a..dc3cecd9720 100644
> --- a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
> +++ b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
> @@ -152,19 +152,64 @@ struct NoInsertRange : std::vector<T>
>    void insert_range(typename std::vector<T>::const_iterator, R&&) = delete;
>  };
>  
> +struct NoCatIterator {
> +  using difference_type = int;
> +  using value_type = int;
> +
> +  NoCatIterator() : v(0) {}
> +  NoCatIterator(int x) : v(x) {}
> +
> +  int operator*() const
> +  { return v; }
> +
> +  NoCatIterator& operator++()
> +  {
> +    ++v;
> +    return *this;
> +  }
> +
> +  NoCatIterator operator++(int)
> +  {
> +    ++v;
> +    return NoCatIterator(v-1);
> +  }
> +
> +  bool operator==(const NoCatIterator& rhs) const
> +  { return v == rhs.v; }
> +
> +private:
> +  int v;
> +};
> +
> +template<>
> +struct std::iterator_traits<NoCatIterator> {
> +  using difference_type = int;
> +  using value_type = int;
> +  using iterator_concept = std::input_iterator_tag;
> +  // no iterator_category, happens also for common_iterator
> +};
> +
>  void test07()
>  {
> +  std::flat_multiset<int> s;
> +  std::flat_multiset<int, std::less<int>, NoInsertRange<int>> s2;
> +
> +  auto r = std::ranges::subrange<NoCatIterator>(1, 6);
> +  s.insert_range(r);
> +  VERIFY( std::ranges::equal(s, (int[]){1, 2, 3, 4, 5}) );
> +  s2.insert_range(r);
> +  VERIFY( std::ranges::equal(s2, (int[]){1, 2, 3, 4, 5}) );
> +
>  #ifdef __SIZEOF_INT128__
>    // PR libstdc++/119415 - flat_foo::insert_range cannot handle common ranges
>    // on c++20 only iterators
> -  auto r = std::views::iota(__int128(1), __int128(6));
> -
> -  std::flat_multiset<int> s;
> -  s.insert_range(r);
> +  auto r2 = std::views::iota(__int128(1), __int128(6));
> +  s.clear();
> +  s.insert_range(r2);
>    VERIFY( std::ranges::equal(s, (int[]){1, 2, 3, 4, 5}) );
>  
> -  std::flat_multiset<int, std::less<int>, NoInsertRange<int>> s2;
> -  s2.insert_range(r);
> +  s2.clear();
> +  s2.insert_range(r2);
>    VERIFY( std::ranges::equal(s2, (int[]){1, 2, 3, 4, 5}) );
>  #endif
>  }
> diff --git a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc 
> b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
> index 16881d788fc..90f5855859f 100644
> --- a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
> +++ b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
> @@ -167,19 +167,64 @@ struct NoInsertRange : std::vector<T>
>    void insert_range(typename std::vector<T>::const_iterator, R&&) = delete;
>  };
>  
> +struct NoCatIterator {
> +  using difference_type = int;
> +  using value_type = int;
> +
> +  NoCatIterator() : v(0) {}
> +  NoCatIterator(int x) : v(x) {}
> +
> +  int operator*() const
> +  { return v; }
> +
> +  NoCatIterator& operator++()
> +  {
> +    ++v;
> +    return *this;
> +  }
> +
> +  NoCatIterator operator++(int)
> +  {
> +    ++v;
> +    return NoCatIterator(v-1);
> +  }
> +
> +  bool operator==(const NoCatIterator& rhs) const
> +  { return v == rhs.v; }
> +
> +private:
> +  int v;
> +};
> +
> +template<>
> +struct std::iterator_traits<NoCatIterator> {
> +  using difference_type = int;
> +  using value_type = int;
> +  using iterator_concept = std::input_iterator_tag;
> +  // no iterator_category, happens also for common_iterator
> +};
> +
>  void test07()
>  {
> +  std::flat_set<int> s;
> +  std::flat_set<int, std::less<int>, NoInsertRange<int>> s2;
> +
> +  auto r = std::ranges::subrange<NoCatIterator>(1, 6);
> +  s.insert_range(r);
> +  VERIFY( std::ranges::equal(s, (int[]){1, 2, 3, 4, 5}) );
> +  s2.insert_range(r);
> +  VERIFY( std::ranges::equal(s2, (int[]){1, 2, 3, 4, 5}) );
> +
>  #ifdef __SIZEOF_INT128__
>    // PR libstdc++/119415 - flat_foo::insert_range cannot handle common ranges
>    // on c++20 only iterators
> -  auto r = std::views::iota(__int128(1), __int128(6));
> -
> -  std::flat_set<int> s;
> -  s.insert_range(r);
> +  auto r2 = std::views::iota(__int128(1), __int128(6));
> +  s.clear();
> +  s.insert_range(r2);
>    VERIFY( std::ranges::equal(s, (int[]){1, 2, 3, 4, 5}) );
>  
> -  std::flat_set<int, std::less<int>, NoInsertRange<int>> s2;
> -  s2.insert_range(r);
> +  s2.clear();
> +  s2.insert_range(r2);
>    VERIFY( std::ranges::equal(s2, (int[]){1, 2, 3, 4, 5}) );
>  #endif
>  }
> -- 
> 2.48.1
> 
> 

Reply via email to