[libcxx-commits] [libcxx] [libc++][ranges] Implement LWG4053 and LWG4054 (PR #88612)

via libcxx-commits libcxx-commits at lists.llvm.org
Sun Apr 28 16:41:44 PDT 2024


https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/88612

>From 4f1a78bf028bf20345bd3d7c238dd522b1505241 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sat, 13 Apr 2024 17:52:17 +0800
Subject: [PATCH 1/5] [libc++][ranges] Implement LWG4053 'Unary call to
 std::views::repeat does not decay the argument'

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/docs/Status/Cxx2cIssues.csv                        | 2 +-
 libcxx/include/__ranges/repeat_view.h                     | 4 ++--
 .../range.repeat.view/views_repeat.pass.cpp               | 8 ++++++++
 3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 008f7418ab9c05..0c4034a1b412c7 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -59,7 +59,7 @@
 "`4038 <https://wg21.link/LWG4038>`__","``std::text_encoding::aliases_view`` should have constexpr iterators","Tokyo March 2024","","",""
 "`4043 <https://wg21.link/LWG4043>`__","""ASCII"" is not a registered character encoding","Tokyo March 2024","|Nothing To Do|","",""
 "`4045 <https://wg21.link/LWG4045>`__","``tuple`` can create dangling references from ``tuple-like``","Tokyo March 2024","","",""
-"`4053 <https://wg21.link/LWG4053>`__","Unary call to ``std::views::repeat`` does not decay the argument","Tokyo March 2024","","","|ranges|"
+"`4053 <https://wg21.link/LWG4053>`__","Unary call to ``std::views::repeat`` does not decay the argument","Tokyo March 2024","|Complete|","19.0","|ranges|"
 "`4054 <https://wg21.link/LWG4054>`__","Repeating a ``repeat_view`` should repeat the view","Tokyo March 2024","","","|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/repeat_view.h b/libcxx/include/__ranges/repeat_view.h
index 5caea757a39314..2544cf496c838a 100644
--- a/libcxx/include/__ranges/repeat_view.h
+++ b/libcxx/include/__ranges/repeat_view.h
@@ -127,8 +127,8 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS repeat_view : public view_interface<r
   _LIBCPP_NO_UNIQUE_ADDRESS _Bound __bound_ = _Bound();
 };
 
-template <class _Tp, class _Bound>
-repeat_view(_Tp, _Bound) -> repeat_view<_Tp, _Bound>;
+template <class _Tp, class _Bound = unreachable_sentinel_t>
+repeat_view(_Tp, _Bound = _Bound()) -> repeat_view<_Tp, _Bound>;
 
 // [range.repeat.iterator]
 template <move_constructible _Tp, semiregular _Bound>
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
index 9cbe505621b989..9077f9a0c607cd 100644
--- a/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
@@ -60,6 +60,14 @@ static_assert(!std::is_invocable_v<decltype(std::views::repeat), NonCopyable>);
 // Tp is move_constructible
 static_assert(std::is_invocable_v<decltype(std::views::repeat), MoveOnly>);
 
+// LWG4053: Unary call to std::views::repeat does not decay the argument
+using RPV = std::ranges::repeat_view<const char*>;
+
+static_assert(std::same_as<decltype(std::views::repeat("foo", std::unreachable_sentinel)), RPV>); // OK
+static_assert(std::same_as<decltype(std::views::repeat(+"foo", std::unreachable_sentinel)), RPV>); // OK
+static_assert(std::same_as<decltype(std::views::repeat("foo")), RPV>); // OK since LWG4053
+static_assert(std::same_as<decltype(std::views::repeat(+"foo")), RPV>); // OK
+
 constexpr bool test() {
   assert(*std::views::repeat(33).begin() == 33);
   assert(*std::views::repeat(33, 10).begin() == 33);

>From 28464ecfc39b344bcd4c4c569bee9c320fa899b3 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sun, 14 Apr 2024 10:40:42 +0800
Subject: [PATCH 2/5] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../range.factories/range.repeat.view/views_repeat.pass.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
index 9077f9a0c607cd..703ea9d7053f8f 100644
--- a/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
@@ -63,10 +63,10 @@ static_assert(std::is_invocable_v<decltype(std::views::repeat), MoveOnly>);
 // LWG4053: Unary call to std::views::repeat does not decay the argument
 using RPV = std::ranges::repeat_view<const char*>;
 
-static_assert(std::same_as<decltype(std::views::repeat("foo", std::unreachable_sentinel)), RPV>); // OK
+static_assert(std::same_as<decltype(std::views::repeat("foo", std::unreachable_sentinel)), RPV>);  // OK
 static_assert(std::same_as<decltype(std::views::repeat(+"foo", std::unreachable_sentinel)), RPV>); // OK
-static_assert(std::same_as<decltype(std::views::repeat("foo")), RPV>); // OK since LWG4053
-static_assert(std::same_as<decltype(std::views::repeat(+"foo")), RPV>); // OK
+static_assert(std::same_as<decltype(std::views::repeat("foo")), RPV>);                             // OK since LWG4053
+static_assert(std::same_as<decltype(std::views::repeat(+"foo")), RPV>);                            // OK
 
 constexpr bool test() {
   assert(*std::views::repeat(33).begin() == 33);

>From 5e428e9e971e24ed6dfc2342bf00840b020f6677 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 15 Apr 2024 22:05:24 +0800
Subject: [PATCH 3/5] Add CTAD test

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../range.repeat.view/ctad.compile.pass.cpp               | 3 +++
 .../range.repeat.view/views_repeat.pass.cpp               | 8 --------
 2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp
index 454bac6e03e518..73802dbd7984ab 100644
--- a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp
@@ -24,4 +24,7 @@ static_assert(std::same_as<decltype(std::ranges::repeat_view(std::declval<Empty&
 static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1)), std::ranges::repeat_view<int, int>>);
 static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1U)), std::ranges::repeat_view<int, unsigned>>);
 static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1UL)), std::ranges::repeat_view<int, unsigned long>>);
+
+// LWG4053 and LWG4054 "Repeating a repeat_view should repeat the view"
+static_assert(std::same_as<decltype(std::ranges::repeat_view(std::ranges::repeat_view(1))), std::ranges::repeat_view<std::ranges::repeat_view<int>>>);
 // clang-format on
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
index 703ea9d7053f8f..9cbe505621b989 100644
--- a/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
@@ -60,14 +60,6 @@ static_assert(!std::is_invocable_v<decltype(std::views::repeat), NonCopyable>);
 // Tp is move_constructible
 static_assert(std::is_invocable_v<decltype(std::views::repeat), MoveOnly>);
 
-// LWG4053: Unary call to std::views::repeat does not decay the argument
-using RPV = std::ranges::repeat_view<const char*>;
-
-static_assert(std::same_as<decltype(std::views::repeat("foo", std::unreachable_sentinel)), RPV>);  // OK
-static_assert(std::same_as<decltype(std::views::repeat(+"foo", std::unreachable_sentinel)), RPV>); // OK
-static_assert(std::same_as<decltype(std::views::repeat("foo")), RPV>);                             // OK since LWG4053
-static_assert(std::same_as<decltype(std::views::repeat(+"foo")), RPV>);                            // OK
-
 constexpr bool test() {
   assert(*std::views::repeat(33).begin() == 33);
   assert(*std::views::repeat(33, 10).begin() == 33);

>From 04452dd9f15e9d9df44d677edafff46e0e88918b Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 15 Apr 2024 23:50:48 +0800
Subject: [PATCH 4/5] [libc++][ranges] Implement LWG4054 'Repeating a
 repeat_view should repeat the view'

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/docs/Status/Cxx2cIssues.csv                   |  2 +-
 libcxx/include/__ranges/repeat_view.h                |  7 ++++---
 .../range.repeat.view/ctad.compile.pass.cpp          |  2 +-
 .../range.repeat.view/views_repeat.pass.cpp          | 12 ++++++++++++
 4 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 0c4034a1b412c7..cb9c6b165bd00d 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -60,7 +60,7 @@
 "`4043 <https://wg21.link/LWG4043>`__","""ASCII"" is not a registered character encoding","Tokyo March 2024","|Nothing To Do|","",""
 "`4045 <https://wg21.link/LWG4045>`__","``tuple`` can create dangling references from ``tuple-like``","Tokyo March 2024","","",""
 "`4053 <https://wg21.link/LWG4053>`__","Unary call to ``std::views::repeat`` does not decay the argument","Tokyo March 2024","|Complete|","19.0","|ranges|"
-"`4054 <https://wg21.link/LWG4054>`__","Repeating a ``repeat_view`` should repeat the view","Tokyo March 2024","","","|ranges|"
+"`4054 <https://wg21.link/LWG4054>`__","Repeating a ``repeat_view`` should repeat the view","Tokyo March 2024","|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",""
 "XXXX","","The sys_info range should be affected by save","Not Yet Adopted","|Complete|","19.0"
diff --git a/libcxx/include/__ranges/repeat_view.h b/libcxx/include/__ranges/repeat_view.h
index 2544cf496c838a..6f1241adb341e0 100644
--- a/libcxx/include/__ranges/repeat_view.h
+++ b/libcxx/include/__ranges/repeat_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/view_interface.h>
+#include <__type_traits/decay.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/make_unsigned.h>
 #include <__type_traits/remove_cv.h>
@@ -230,9 +231,9 @@ namespace __repeat {
 struct __fn {
   template <class _Tp>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Tp&& __value)
-    noexcept(noexcept(ranges::repeat_view(std::forward<_Tp>(__value))))
-    -> decltype(      ranges::repeat_view(std::forward<_Tp>(__value)))
-    { return          ranges::repeat_view(std::forward<_Tp>(__value)); }
+    noexcept(noexcept(ranges::repeat_view<decay_t<_Tp>>(std::forward<_Tp>(__value))))
+    -> decltype(      ranges::repeat_view<decay_t<_Tp>>(std::forward<_Tp>(__value)))
+    { return          ranges::repeat_view<decay_t<_Tp>>(std::forward<_Tp>(__value)); }
 
   template <class _Tp, class _Bound>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Tp&& __value, _Bound&& __bound_sentinel)
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp
index 73802dbd7984ab..44560dd7a11408 100644
--- a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp
@@ -26,5 +26,5 @@ static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1U)), std::rang
 static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1UL)), std::ranges::repeat_view<int, unsigned long>>);
 
 // LWG4053 and LWG4054 "Repeating a repeat_view should repeat the view"
-static_assert(std::same_as<decltype(std::ranges::repeat_view(std::ranges::repeat_view(1))), std::ranges::repeat_view<std::ranges::repeat_view<int>>>);
+static_assert(std::same_as<decltype(std::views::repeat(std::views::repeat(1))), std::ranges::repeat_view<std::ranges::repeat_view<int>>>);
 // clang-format on
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
index 9cbe505621b989..5d913fa197f5a9 100644
--- a/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
@@ -60,6 +60,18 @@ static_assert(!std::is_invocable_v<decltype(std::views::repeat), NonCopyable>);
 // Tp is move_constructible
 static_assert(std::is_invocable_v<decltype(std::views::repeat), MoveOnly>);
 
+// Test LWG4054 "Repeating a repeat_view should repeat the view"
+static_assert(std::is_same_v<decltype(std::views::repeat(std::views::repeat(42))),
+                             std::ranges::repeat_view<std::ranges::repeat_view<int>>>);
+
+// These cases are from LWG4053, but they are actually covered by the resolution of LWG4054,
+// and the resolution of LWG4053 only affects CTAD.
+using RPV = std::ranges::repeat_view<const char*>;
+static_assert(std::same_as<decltype(std::views::repeat("foo", std::unreachable_sentinel)), RPV>);  // OK
+static_assert(std::same_as<decltype(std::views::repeat(+"foo", std::unreachable_sentinel)), RPV>); // OK
+static_assert(std::same_as<decltype(std::views::repeat("foo")), RPV>);                             // OK since LWG4054
+static_assert(std::same_as<decltype(std::views::repeat(+"foo")), RPV>);                            // OK
+
 constexpr bool test() {
   assert(*std::views::repeat(33).begin() == 33);
   assert(*std::views::repeat(33, 10).begin() == 33);

>From ea5e01bb0e497dfbc9fa05442995777f472db9e1 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 16 Apr 2024 20:00:04 +0800
Subject: [PATCH 5/5] Address review comments

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../range.repeat.view/ctad.compile.pass.cpp                | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp
index 44560dd7a11408..97b74289619dd7 100644
--- a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp
@@ -25,6 +25,9 @@ static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1)), std::range
 static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1U)), std::ranges::repeat_view<int, unsigned>>);
 static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1UL)), std::ranges::repeat_view<int, unsigned long>>);
 
-// LWG4053 and LWG4054 "Repeating a repeat_view should repeat the view"
-static_assert(std::same_as<decltype(std::views::repeat(std::views::repeat(1))), std::ranges::repeat_view<std::ranges::repeat_view<int>>>);
+using RPV = std::ranges::repeat_view<const char*>;
+static_assert(std::same_as<decltype(std::ranges::repeat_view("foo", std::unreachable_sentinel)), RPV>);  // OK
+static_assert(std::same_as<decltype(std::ranges::repeat_view(+"foo", std::unreachable_sentinel)), RPV>); // OK
+static_assert(std::same_as<decltype(std::ranges::repeat_view("foo")), RPV>);                             // OK since LWG4053
+static_assert(std::same_as<decltype(std::ranges::repeat_view(+"foo")), RPV>);                            // OK
 // clang-format on



More information about the libcxx-commits mailing list