[libcxx-commits] [libcxx] [libc++] Verify forward_list self-merging is a no-op (PR #129985)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Mar 19 09:27:07 PDT 2025


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/129985

>From e813bed4aa8cf67fbfc265090a1b5610f3935abe Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Wed, 5 Mar 2025 23:14:39 -0500
Subject: [PATCH 1/3] Verify forward_list self-merging is a no-op

---
 libcxx/docs/Status/Cxx23Issues.csv                       | 2 +-
 .../forwardlist/forwardlist.ops/merge_lvalue.pass.cpp    | 8 ++++++++
 .../forwardlist.ops/merge_lvalue_pred.pass.cpp           | 9 ++++++++-
 .../forwardlist/forwardlist.ops/merge_rvalue.pass.cpp    | 8 ++++++++
 .../forwardlist.ops/merge_rvalue_pred.pass.cpp           | 8 ++++++++
 5 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 9ae6322b7fdbe..c0245035f60ae 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -137,7 +137,7 @@
 "`LWG3593 <https://wg21.link/LWG3593>`__","Several iterators' ``base() const &`` and ``lazy_split_view::outer-iterator::value_type::end()`` missing ``noexcept``","2021-10 (Virtual)","","",""
 "`LWG3595 <https://wg21.link/LWG3595>`__","Exposition-only classes proxy and postfix-proxy for ``common_iterator`` should be fully ``constexpr``","2021-10 (Virtual)","|Complete|","14",""
 "","","","","",""
-"`LWG3088 <https://wg21.link/LWG3088>`__","``forward_list::merge`` behaviour unclear when passed ``*this``","2022-02 (Virtual)","","",""
+"`LWG3088 <https://wg21.link/LWG3088>`__","``forward_list::merge`` behaviour unclear when passed ``*this``","2022-02 (Virtual)","|Complete|","21",""
 "`LWG3471 <https://wg21.link/LWG3471>`__","``polymorphic_allocator::allocate`` does not satisfy ``Cpp17Allocator`` requirements","2022-02 (Virtual)","","",""
 "`LWG3525 <https://wg21.link/LWG3525>`__","``uses_allocator_construction_args`` fails to handle types convertible to ``pair``","2022-02 (Virtual)","","",""
 "`LWG3598 <https://wg21.link/LWG3598>`__","``system_category().default_error_condition(0)`` is underspecified","2022-02 (Virtual)","","",""
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp
index 6e73b2fd73726..541a5417db4e5 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp
@@ -9,6 +9,7 @@
 // <forward_list>
 
 // void merge(forward_list& x);
+// If (addressof(x) == this) does nothing; otherwise ...
 
 #include <forward_list>
 #include <iterator>
@@ -109,5 +110,12 @@ int main(int, char**) {
   }
 #endif
 
+  { // Make sure self-merging does nothing.
+    int a[] = {1, 2, 3, 4, 5};
+    std::forward_list<int> c(a, a + sizeof(a) / sizeof(a[0]));
+    c.merge(c);
+    assert((c == std::forward_list<int>(a, a + sizeof(a) / sizeof(a[0]))));
+  }
+
   return 0;
 }
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp
index fddf9f9dc0f46..22acb2836076b 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp
@@ -9,6 +9,7 @@
 // <forward_list>
 
 // template <class Compare> void merge(forward_list& x, Compare comp);
+// If (addressof(x) == this) does nothing; otherwise ...
 
 #include <forward_list>
 #include <iterator>
@@ -91,7 +92,6 @@ int main(int, char**) {
     C c3(std::begin(t3), std::end(t3));
     assert(c1 == c3);
   }
-
 #if TEST_STD_VER >= 11
   { // Test with a different allocator.
     typedef int T;
@@ -110,5 +110,12 @@ int main(int, char**) {
   }
 #endif
 
+  { // Make sure self-merging does nothing.
+    int a[] = {5, 4, 3, 2, 1};
+    std::forward_list<int> c(a, a + sizeof(a) / sizeof(a[0]));
+    c.merge(c, std::greater<int>());
+    assert((c == std::forward_list<int>(a, a + sizeof(a) / sizeof(a[0]))));
+  }
+
   return 0;
 }
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp
index d5084eccd98da..995762e8a04e3 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp
@@ -11,6 +11,7 @@
 // <forward_list>
 
 // void merge(forward_list&& x);
+// If (addressof(x) == this) does nothing; otherwise ...
 
 #include <forward_list>
 #include <functional>
@@ -102,5 +103,12 @@ int main(int, char**) {
     assert(c1 == c3);
   }
 
+  { // Make sure self-merging does nothing.
+    int a[] = {1, 2, 3, 4, 5};
+    std::forward_list<int> c(a, a + sizeof(a) / sizeof(a[0]));
+    c.merge(std::move(c));
+    assert((c == std::forward_list<int>(a, a + sizeof(a) / sizeof(a[0]))));
+  }
+
   return 0;
 }
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp
index 235707c65370d..0ec466cefce2b 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp
@@ -11,6 +11,7 @@
 // <forward_list>
 
 // template <class Compare> void merge(forward_list&& x, Compare comp);
+// If (addressof(x) == this) does nothing; otherwise ...
 
 #include <forward_list>
 #include <functional>
@@ -103,5 +104,12 @@ int main(int, char**) {
     assert(c1 == c3);
   }
 
+  { // Make sure self-merging does nothing.
+    int a[] = {5, 4, 3, 2, 1};
+    std::forward_list<int> c(a, a + sizeof(a) / sizeof(a[0]));
+    c.merge(std::move(c), std::greater<int>());
+    assert((c == std::forward_list<int>(a, a + sizeof(a) / sizeof(a[0]))));
+  }
+
   return 0;
 }

>From f05b9377f527fa78db283262e5102e6dadf0099a Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Thu, 6 Mar 2025 13:42:58 -0500
Subject: [PATCH 2/3] Fix reviewer comments

---
 libcxx/docs/Status/Cxx23Issues.csv                          | 2 +-
 .../forwardlist/forwardlist.ops/merge_lvalue.pass.cpp       | 5 ++---
 .../forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp  | 6 +++---
 .../forwardlist/forwardlist.ops/merge_rvalue.pass.cpp       | 5 ++---
 .../forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp  | 5 ++---
 5 files changed, 10 insertions(+), 13 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index c0245035f60ae..8cfc25099c034 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -137,7 +137,7 @@
 "`LWG3593 <https://wg21.link/LWG3593>`__","Several iterators' ``base() const &`` and ``lazy_split_view::outer-iterator::value_type::end()`` missing ``noexcept``","2021-10 (Virtual)","","",""
 "`LWG3595 <https://wg21.link/LWG3595>`__","Exposition-only classes proxy and postfix-proxy for ``common_iterator`` should be fully ``constexpr``","2021-10 (Virtual)","|Complete|","14",""
 "","","","","",""
-"`LWG3088 <https://wg21.link/LWG3088>`__","``forward_list::merge`` behaviour unclear when passed ``*this``","2022-02 (Virtual)","|Complete|","21",""
+"`LWG3088 <https://wg21.link/LWG3088>`__","``forward_list::merge`` behaviour unclear when passed ``*this``","2022-02 (Virtual)","|Complete|","Yes",""
 "`LWG3471 <https://wg21.link/LWG3471>`__","``polymorphic_allocator::allocate`` does not satisfy ``Cpp17Allocator`` requirements","2022-02 (Virtual)","","",""
 "`LWG3525 <https://wg21.link/LWG3525>`__","``uses_allocator_construction_args`` fails to handle types convertible to ``pair``","2022-02 (Virtual)","","",""
 "`LWG3598 <https://wg21.link/LWG3598>`__","``system_category().default_error_condition(0)`` is underspecified","2022-02 (Virtual)","","",""
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp
index 541a5417db4e5..d7c06721da71b 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp
@@ -9,7 +9,6 @@
 // <forward_list>
 
 // void merge(forward_list& x);
-// If (addressof(x) == this) does nothing; otherwise ...
 
 #include <forward_list>
 #include <iterator>
@@ -112,9 +111,9 @@ int main(int, char**) {
 
   { // Make sure self-merging does nothing.
     int a[] = {1, 2, 3, 4, 5};
-    std::forward_list<int> c(a, a + sizeof(a) / sizeof(a[0]));
+    std::forward_list<int> c(std::begin(a), std::end(a));
     c.merge(c);
-    assert((c == std::forward_list<int>(a, a + sizeof(a) / sizeof(a[0]))));
+    assert(c == std::forward_list<int>(std::begin(a), std::end(a)));
   }
 
   return 0;
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp
index 22acb2836076b..f740969ea207e 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp
@@ -9,7 +9,6 @@
 // <forward_list>
 
 // template <class Compare> void merge(forward_list& x, Compare comp);
-// If (addressof(x) == this) does nothing; otherwise ...
 
 #include <forward_list>
 #include <iterator>
@@ -92,6 +91,7 @@ int main(int, char**) {
     C c3(std::begin(t3), std::end(t3));
     assert(c1 == c3);
   }
+
 #if TEST_STD_VER >= 11
   { // Test with a different allocator.
     typedef int T;
@@ -112,9 +112,9 @@ int main(int, char**) {
 
   { // Make sure self-merging does nothing.
     int a[] = {5, 4, 3, 2, 1};
-    std::forward_list<int> c(a, a + sizeof(a) / sizeof(a[0]));
+    std::forward_list<int> c(std::begin(a), std::end(a));
     c.merge(c, std::greater<int>());
-    assert((c == std::forward_list<int>(a, a + sizeof(a) / sizeof(a[0]))));
+    assert(c == std::forward_list<int>(std::begin(a), std::end(a)));
   }
 
   return 0;
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp
index 995762e8a04e3..2dc2fa3303c71 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp
@@ -11,7 +11,6 @@
 // <forward_list>
 
 // void merge(forward_list&& x);
-// If (addressof(x) == this) does nothing; otherwise ...
 
 #include <forward_list>
 #include <functional>
@@ -105,9 +104,9 @@ int main(int, char**) {
 
   { // Make sure self-merging does nothing.
     int a[] = {1, 2, 3, 4, 5};
-    std::forward_list<int> c(a, a + sizeof(a) / sizeof(a[0]));
+    std::forward_list<int> c(std::begin(a), std::end(a));
     c.merge(std::move(c));
-    assert((c == std::forward_list<int>(a, a + sizeof(a) / sizeof(a[0]))));
+    assert(c == std::forward_list<int>(std::begin(a), std::end(a)));
   }
 
   return 0;
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp
index 0ec466cefce2b..8dee81750a989 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp
@@ -11,7 +11,6 @@
 // <forward_list>
 
 // template <class Compare> void merge(forward_list&& x, Compare comp);
-// If (addressof(x) == this) does nothing; otherwise ...
 
 #include <forward_list>
 #include <functional>
@@ -106,9 +105,9 @@ int main(int, char**) {
 
   { // Make sure self-merging does nothing.
     int a[] = {5, 4, 3, 2, 1};
-    std::forward_list<int> c(a, a + sizeof(a) / sizeof(a[0]));
+    std::forward_list<int> c(std::begin(a), std::end(a));
     c.merge(std::move(c), std::greater<int>());
-    assert((c == std::forward_list<int>(a, a + sizeof(a) / sizeof(a[0]))));
+    assert(c == std::forward_list<int>(std::begin(a), std::end(a)));
   }
 
   return 0;

>From 5576cf0cd20c216d4f2a82bcea47f6f64e2897d5 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 19 Mar 2025 12:26:57 -0400
Subject: [PATCH 3/3] Apply suggestions from code review

---
 .../sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp | 2 +-
 .../forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp      | 2 +-
 .../sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp | 2 +-
 .../forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp      | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp
index d7c06721da71b..9a162789569d3 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp
@@ -109,7 +109,7 @@ int main(int, char**) {
   }
 #endif
 
-  { // Make sure self-merging does nothing.
+  { // LWG3088: Make sure self-merging does nothing.
     int a[] = {1, 2, 3, 4, 5};
     std::forward_list<int> c(std::begin(a), std::end(a));
     c.merge(c);
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp
index f740969ea207e..4e1814044808c 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp
@@ -110,7 +110,7 @@ int main(int, char**) {
   }
 #endif
 
-  { // Make sure self-merging does nothing.
+  { // LWG3088: Make sure self-merging does nothing.
     int a[] = {5, 4, 3, 2, 1};
     std::forward_list<int> c(std::begin(a), std::end(a));
     c.merge(c, std::greater<int>());
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp
index 2dc2fa3303c71..acfa014fe2546 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp
@@ -102,7 +102,7 @@ int main(int, char**) {
     assert(c1 == c3);
   }
 
-  { // Make sure self-merging does nothing.
+  { // LWG3088: Make sure self-merging does nothing.
     int a[] = {1, 2, 3, 4, 5};
     std::forward_list<int> c(std::begin(a), std::end(a));
     c.merge(std::move(c));
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp
index 8dee81750a989..41b56ce7a2884 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp
@@ -103,7 +103,7 @@ int main(int, char**) {
     assert(c1 == c3);
   }
 
-  { // Make sure self-merging does nothing.
+  { // LWG3088: Make sure self-merging does nothing.
     int a[] = {5, 4, 3, 2, 1};
     std::forward_list<int> c(std::begin(a), std::end(a));
     c.merge(std::move(c), std::greater<int>());



More information about the libcxx-commits mailing list