Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++][ranges] LWG4001: iota_view should provide empty #79687

Merged

Conversation

H-G-Hristov
Copy link
Contributor

@H-G-Hristov H-G-Hristov marked this pull request as ready for review January 27, 2024 11:21
@H-G-Hristov H-G-Hristov requested a review from a team as a code owner January 27, 2024 11:21
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jan 27, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Jan 27, 2024

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

Implements: https://wg21.link/LWG4001


Full diff: https://github.com/llvm/llvm-project/pull/79687.diff

3 Files Affected:

  • (modified) libcxx/docs/Status/Cxx2cIssues.csv (+1-1)
  • (modified) libcxx/include/__ranges/iota_view.h (+2)
  • (added) libcxx/test/std/ranges/range.factories/range.iota.view/empty.pass.cpp (+136)
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index b69b0948325412..6401c7c6d8fc7d 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -38,7 +38,7 @@
 "`3974 <https://wg21.link/LWG3974>`__","``mdspan::operator[]`` should not copy ``OtherIndexTypes``","Kona November 2023","","",""
 "`3987 <https://wg21.link/LWG3987>`__","Including ``<flat_foo>`` doesn't provide ``std::begin``/``end``","Kona November 2023","","","|flat_containers|"
 "`3990 <https://wg21.link/LWG3990>`__","Program-defined specializations of ``std::tuple`` and ``std::variant`` can't be properly supported","Kona November 2023","","",""
-"`4001 <https://wg21.link/LWG4001>`__","``iota_view`` should provide ``empty``","Kona November 2023","","","|ranges|"
+"`4001 <https://wg21.link/LWG4001>`__","``iota_view`` should provide ``empty``","Kona November 2023","|Complete|","19.0","|ranges|"
 "","","","","",""
 "`3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Yet Adopted","|Complete|","16.0",""
 "","","","","",""
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index c8314dd848b447..6c5923c7ef013c 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -345,6 +345,8 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     return __iterator{__bound_sentinel_};
   }
 
+  _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const { return __value_ == __bound_sentinel_; }
+
   _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires(same_as<_Start, _BoundSentinel> && __advanceable<_Start>) ||
             (integral<_Start> && integral<_BoundSentinel>) || sized_sentinel_for<_BoundSentinel, _Start>
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/empty.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/empty.pass.cpp
new file mode 100644
index 00000000000000..e7adffaf63b4c9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/empty.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr bool empty() const;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "types.h"
+
+template <typename R>
+concept HasFreeEmpty = requires(R r) { std::ranges::empty(r); };
+
+template <typename R>
+concept HasMemberEmpty = requires(R r) {
+  { r.empty() } -> std::same_as<bool>;
+};
+
+constexpr void test_empty_iota() {
+  std::vector<int> ev;
+
+  // Both parameters are non-const
+  {
+    auto iv = std::views::iota(std::ranges::begin(ev), std::ranges::end(ev));
+
+    static_assert(HasFreeEmpty<decltype(iv)>);
+    static_assert(HasMemberEmpty<decltype(iv)>);
+
+    assert(iv.empty());
+  }
+  // Left paramter is const
+  {
+    auto iv = std::views::iota(std::ranges::begin(std::as_const(ev)), std::ranges::end(ev));
+
+    static_assert(HasFreeEmpty<decltype(iv)>);
+    static_assert(HasMemberEmpty<decltype(iv)>);
+
+    assert(iv.empty());
+  }
+  // Right paramter is const
+  {
+    auto iv = std::views::iota(std::ranges::begin(ev), std::ranges::end(std::as_const(ev)));
+
+    static_assert(HasFreeEmpty<decltype(iv)>);
+    static_assert(HasMemberEmpty<decltype(iv)>);
+
+    assert(iv.empty());
+  }
+  // Both parameters are const
+  {
+    auto iv = std::views::iota(std::ranges::begin(std::as_const(ev)), std::ranges::end(std::as_const(ev)));
+
+    static_assert(HasFreeEmpty<decltype(iv)>);
+    static_assert(HasMemberEmpty<decltype(iv)>);
+
+    assert(iv.empty());
+  }
+
+  std::vector<char> v{'b', 'a', 'b', 'a', 'z', 'm', 't'};
+  auto fv = v | std::views::filter([](auto val) { return val == '0'; });
+
+  {
+    auto iv = std::views::iota(std::ranges::begin(fv), std::ranges::end(fv));
+
+    static_assert(HasFreeEmpty<decltype(iv)>);
+    static_assert(HasMemberEmpty<decltype(iv)>);
+
+    assert(iv.empty());
+  }
+}
+
+constexpr void test_nonempty_iota() {
+  // Default ctr
+  {
+    std::ranges::iota_view<Int42<DefaultTo42>> iv;
+
+    static_assert(HasFreeEmpty<decltype(iv)>);
+    static_assert(HasMemberEmpty<decltype(iv)>);
+
+    assert(!iv.empty());
+  }
+  // Value pass
+  {
+    std::ranges::iota_view<SomeInt> iv(SomeInt(94));
+
+    static_assert(HasFreeEmpty<decltype(iv)>);
+    static_assert(HasMemberEmpty<decltype(iv)>);
+
+    assert(!iv.empty());
+  }
+
+  {
+    std::vector<char> v;
+    auto it = std::back_inserter(v);
+    auto iv = std::views::iota(it);
+
+    static_assert(HasFreeEmpty<decltype(iv)>);
+    static_assert(HasMemberEmpty<decltype(iv)>);
+
+    assert(!iv.empty());
+  }
+  {
+    std::vector<char> v{'b', 'a', 'b', 'a', 'z', 'm', 't'};
+    auto it = std::back_inserter(v);
+    auto iv = std::views::iota(it);
+
+    static_assert(HasFreeEmpty<decltype(iv)>);
+    static_assert(HasMemberEmpty<decltype(iv)>);
+
+    assert(!iv.empty());
+  }
+}
+
+constexpr bool test() {
+  test_empty_iota();
+  test_nonempty_iota();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

Copy link
Contributor

@hawkinsw hawkinsw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope these little suggestions help! Thanks for doing the work!

@ldionne ldionne added the ranges Issues related to `<ranges>` label Mar 7, 2024
@var-const var-const self-assigned this Mar 8, 2024
Copy link
Member

@var-const var-const left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about the slow review! Almost LGTM with just a couple of nits.

{
auto iv = std::views::iota(std::ranges::begin(ev), std::ranges::end(ev));

static_assert(HasFreeEmpty<decltype(iv)>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: normally, the ranges tests follow a pattern where constraints are tested in a separate block before testing the actual function -- can you please do the same? It's mostly for consistency, but IMO it's also easier to make sure we're testing all the constraints when the tests are all next to each other and not spread out in the file.

};

constexpr void test_empty_iota() {
std::vector<int> ev;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: what do ev and iv stand for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally I prefer long descriptive names but the libc++ tests use short names, so I try to be consistent:
ev -> empty vector vs v -> non-empty vector.
iv -> iota_view

I can change these.

}
// Right parameter is const
{
auto iv = std::views::iota(std::ranges::begin(ev), std::ranges::end(std::as_const(ev)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: does using as_const really matter to test iv.empty()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it was good to be detailed. Should I remove those?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/LWG4001-iota_view_should_provide_empty branch from 76c72a5 to 02c4e71 Compare April 2, 2024 13:39
@ldionne ldionne added this to the LLVM 19.X Release milestone Jul 9, 2024
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments about the tests but this overall LGTM.

#include "types.h"

template <typename R>
concept HasEmpty = requires(R r) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
concept HasEmpty = requires(R r) {
concept HasEmpty = requires(R const r) {

Otherwise you don't test anywhere that the method is marked as const.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 95 to 112
// Left parameter is const
{
auto iv = std::views::iota(std::ranges::begin(std::as_const(ev)), std::ranges::end(ev));

assert(iv.empty());
}
// Right parameter is const
{
auto iv = std::views::iota(std::ranges::begin(ev), std::ranges::end(std::as_const(ev)));

assert(iv.empty());
}
// Both parameters are const
{
auto iv = std::views::iota(std::ranges::begin(std::as_const(ev)), std::ranges::end(std::as_const(ev)));

assert(iv.empty());
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand the purpose of these tests. We're interested in checking that empty() can be called on a const iota_view, not that we can call it on a mutable iota_view constructed from const iterators?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

@H-G-Hristov
Copy link
Contributor Author

Minor comments about the tests but this overall LGTM.

Thanks! I think I addressed the comments.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Can be merged once CI is green. Thanks!

@H-G-Hristov
Copy link
Contributor Author

Thank you! The CI is green finally!

@Zingam Zingam merged commit 60b6f43 into llvm:main Jul 17, 2024
58 checks passed
@H-G-Hristov H-G-Hristov deleted the hgh/libcxx/LWG4001-iota_view_should_provide_empty branch July 19, 2024 15:39
sgundapa pushed a commit to sgundapa/upstream_effort that referenced this pull request Jul 23, 2024
yuxuanchen1997 pushed a commit that referenced this pull request Jul 25, 2024
Summary:
Implements: https://wg21.link/LWG4001
- https://eel.is/c++draft/range.iota.view

---------

Co-authored-by: Zingam <[email protected]>
Co-authored-by: Will Hawkins <[email protected]>

Test Plan: 

Reviewers: 

Subscribers: 

Tasks: 

Tags: 


Differential Revision: https://phabricator.intern.facebook.com/D60251523
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. ranges Issues related to `<ranges>`
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants