[clang] [libc++] Prevent calling the projection more than three times (PR #66315)

Jocelyn Castellano via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 15 13:32:19 PDT 2023


https://github.com/pandaninjas updated https://github.com/llvm/llvm-project/pull/66315

>From ead65bfcb70be46788bc9e88c891e7ae7f91b8d7 Mon Sep 17 00:00:00 2001
From: PandaNinjas <admin at malwarefight.wip.la>
Date: Wed, 13 Sep 2023 17:38:17 -0700
Subject: [PATCH 01/13] [libc++] Prevent calling the projection more than three
 times

---
 libcxx/include/__algorithm/ranges_clamp.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h
index 9613f7f37720a6c..ca46675eb4b3041 100644
--- a/libcxx/include/__algorithm/ranges_clamp.h
+++ b/libcxx/include/__algorithm/ranges_clamp.h
@@ -37,9 +37,10 @@ struct __fn {
     _LIBCPP_ASSERT_UNCATEGORIZED(!bool(std::invoke(__comp, std::invoke(__proj, __high), std::invoke(__proj, __low))),
                                  "Bad bounds passed to std::ranges::clamp");
 
-    if (std::invoke(__comp, std::invoke(__proj, __value), std::invoke(__proj, __low)))
+    auto &projection = std::invoke(__proj, __value);
+    if (std::invoke(__comp, projection, std::invoke(__proj, __low)))
       return __low;
-    else if (std::invoke(__comp, std::invoke(__proj, __high), std::invoke(__proj, __value)))
+    else if (std::invoke(__comp, std::invoke(__proj, __high), projection))
       return __high;
     else
       return __value;

>From c18d60870ac342a95a5528396a8e0c7b91717cbb Mon Sep 17 00:00:00 2001
From: PandaNinjas <admin at malwarefight.wip.la>
Date: Wed, 13 Sep 2023 18:56:44 -0700
Subject: [PATCH 02/13] [libc++] Run clang-format on file

---
 libcxx/include/__algorithm/ranges_clamp.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h
index ca46675eb4b3041..3469a6419b2270f 100644
--- a/libcxx/include/__algorithm/ranges_clamp.h
+++ b/libcxx/include/__algorithm/ranges_clamp.h
@@ -37,7 +37,7 @@ struct __fn {
     _LIBCPP_ASSERT_UNCATEGORIZED(!bool(std::invoke(__comp, std::invoke(__proj, __high), std::invoke(__proj, __low))),
                                  "Bad bounds passed to std::ranges::clamp");
 
-    auto &projection = std::invoke(__proj, __value);
+    auto& projection = std::invoke(__proj, __value);
     if (std::invoke(__comp, projection, std::invoke(__proj, __low)))
       return __low;
     else if (std::invoke(__comp, std::invoke(__proj, __high), projection))

>From b40e791f0e9fedbb19936851e1e71decf00331fa Mon Sep 17 00:00:00 2001
From: Jocelyn Castellano <admin at malwarefight.wip.la>
Date: Wed, 13 Sep 2023 19:11:20 -0700
Subject: [PATCH 03/13] [libcxx] CamelCase projection and make variable name
 more descriptive

---
 libcxx/include/__algorithm/ranges_clamp.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h
index 3469a6419b2270f..3adb5fa828e1ee5 100644
--- a/libcxx/include/__algorithm/ranges_clamp.h
+++ b/libcxx/include/__algorithm/ranges_clamp.h
@@ -37,7 +37,7 @@ struct __fn {
     _LIBCPP_ASSERT_UNCATEGORIZED(!bool(std::invoke(__comp, std::invoke(__proj, __high), std::invoke(__proj, __low))),
                                  "Bad bounds passed to std::ranges::clamp");
 
-    auto& projection = std::invoke(__proj, __value);
+    auto& ValueProjection = std::invoke(__proj, __value);
     if (std::invoke(__comp, projection, std::invoke(__proj, __low)))
       return __low;
     else if (std::invoke(__comp, std::invoke(__proj, __high), projection))

>From a8907624defa4cc4f47520a2d93a8bd042816aa2 Mon Sep 17 00:00:00 2001
From: Jocelyn Castellano <admin at malwarefight.wip.la>
Date: Wed, 13 Sep 2023 19:11:47 -0700
Subject: [PATCH 04/13] [libcxx] properly change variable name

---
 libcxx/include/__algorithm/ranges_clamp.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h
index 3adb5fa828e1ee5..3d7a224b3649a3f 100644
--- a/libcxx/include/__algorithm/ranges_clamp.h
+++ b/libcxx/include/__algorithm/ranges_clamp.h
@@ -38,9 +38,9 @@ struct __fn {
                                  "Bad bounds passed to std::ranges::clamp");
 
     auto& ValueProjection = std::invoke(__proj, __value);
-    if (std::invoke(__comp, projection, std::invoke(__proj, __low)))
+    if (std::invoke(__comp, ValueProjection, std::invoke(__proj, __low)))
       return __low;
-    else if (std::invoke(__comp, std::invoke(__proj, __high), projection))
+    else if (std::invoke(__comp, std::invoke(__proj, __high), ValueProjection))
       return __high;
     else
       return __value;

>From 15d3b2b79fbd61f97b0312e0913cede36b5b202d Mon Sep 17 00:00:00 2001
From: Jocelyn Castellano <admin at malwarefight.wip.la>
Date: Thu, 14 Sep 2023 10:37:34 -0700
Subject: [PATCH 05/13] Apply suggestions from code review

---
 libcxx/include/__algorithm/ranges_clamp.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h
index 3d7a224b3649a3f..a97538e4c0e3f65 100644
--- a/libcxx/include/__algorithm/ranges_clamp.h
+++ b/libcxx/include/__algorithm/ranges_clamp.h
@@ -37,10 +37,10 @@ struct __fn {
     _LIBCPP_ASSERT_UNCATEGORIZED(!bool(std::invoke(__comp, std::invoke(__proj, __high), std::invoke(__proj, __low))),
                                  "Bad bounds passed to std::ranges::clamp");
 
-    auto& ValueProjection = std::invoke(__proj, __value);
-    if (std::invoke(__comp, ValueProjection, std::invoke(__proj, __low)))
+    auto&& __projected = std::invoke(__proj, __value);
+    if (std::invoke(__comp, std::forward(__projected), std::invoke(__proj, __low)))
       return __low;
-    else if (std::invoke(__comp, std::invoke(__proj, __high), ValueProjection))
+    else if (std::invoke(__comp, std::invoke(__proj, __high), std::forward(__projected)))
       return __high;
     else
       return __value;

>From 67a6c91976c76fe1d399a81af8f45ecb549c62ec Mon Sep 17 00:00:00 2001
From: Jocelyn Castellano <admin at malwarefight.wip.la>
Date: Thu, 14 Sep 2023 11:09:55 -0700
Subject: [PATCH 06/13] [libc++] add test to ensure projection is not called
 more than 3 times

---
 ..._not_called_more_than_three_times.pass.cpp | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp

diff --git a/libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp b/libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp
new file mode 100644
index 000000000000000..8d0713cd573fb34
--- /dev/null
+++ b/libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp
@@ -0,0 +1,20 @@
+#include <algorithm>
+#include <assert_macros.h>
+
+int Projection(const int value)
+{
+    static int counter = 0;
+
+    if (counter++ == 3)
+    {
+        TEST_FAIL("projection called more than 3 times")
+    }
+
+    return value;
+}
+
+int main()
+{
+    TEST_DOES_NOT_THROW(std::ranges::clamp(3, 2, 4, std::ranges::less{}, &Projection));
+    return 0;
+}

>From cf56669a8ae02b1cb0bf35ef7a37146615778008 Mon Sep 17 00:00:00 2001
From: PandaNinjas <admin at malwarefight.wip.la>
Date: Thu, 14 Sep 2023 11:10:41 -0700
Subject: [PATCH 07/13] [libc++] run clang-format on files

---
 ..._not_called_more_than_three_times.pass.cpp | 21 ++++++++-----------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp b/libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp
index 8d0713cd573fb34..fd47de4eb390534 100644
--- a/libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp
@@ -1,20 +1,17 @@
 #include <algorithm>
 #include <assert_macros.h>
 
-int Projection(const int value)
-{
-    static int counter = 0;
+int Projection(const int value) {
+  static int counter = 0;
 
-    if (counter++ == 3)
-    {
-        TEST_FAIL("projection called more than 3 times")
-    }
+  if (counter++ == 3) {
+    TEST_FAIL("projection called more than 3 times")
+  }
 
-    return value;
+  return value;
 }
 
-int main()
-{
-    TEST_DOES_NOT_THROW(std::ranges::clamp(3, 2, 4, std::ranges::less{}, &Projection));
-    return 0;
+int main() {
+  std::ranges::clamp(3, 2, 4, std::ranges::less{}, &Projection);
+  return 0;
 }

>From 04debcbd954222ce20ad3558f7b9bde42401ce75 Mon Sep 17 00:00:00 2001
From: Jocelyn Castellano <admin at malwarefight.wip.la>
Date: Thu, 14 Sep 2023 17:48:53 -0700
Subject: [PATCH 08/13] [libc++] use decltype in std::forward

---
 libcxx/include/__algorithm/ranges_clamp.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h
index a97538e4c0e3f65..182aed4fc53cbed 100644
--- a/libcxx/include/__algorithm/ranges_clamp.h
+++ b/libcxx/include/__algorithm/ranges_clamp.h
@@ -38,9 +38,9 @@ struct __fn {
                                  "Bad bounds passed to std::ranges::clamp");
 
     auto&& __projected = std::invoke(__proj, __value);
-    if (std::invoke(__comp, std::forward(__projected), std::invoke(__proj, __low)))
+    if (std::invoke(__comp, std::forward<decltype(__projected)>(__projected), std::invoke(__proj, __low)))
       return __low;
-    else if (std::invoke(__comp, std::invoke(__proj, __high), std::forward(__projected)))
+    else if (std::invoke(__comp, std::invoke(__proj, __high), std::forward<decltype(__projected)>(__projected))
       return __high;
     else
       return __value;

>From a377e11f8c502ca1a6557bf90d48efdec409e2a0 Mon Sep 17 00:00:00 2001
From: PandaNinjas <admin at malwarefight.wip.la>
Date: Fri, 15 Sep 2023 10:56:31 -0700
Subject: [PATCH 09/13] Apply testing changes from code review

---
 .../alg.sorting/alg.clamp/ranges.clamp.pass.cpp |  9 ++++++++-
 ...on_not_called_more_than_three_times.pass.cpp | 17 -----------------
 2 files changed, 8 insertions(+), 18 deletions(-)
 delete mode 100644 libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp

diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
index 036552f75de4eb4..189d8cc4d886e8e 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
@@ -108,7 +108,14 @@ constexpr bool test() {
     auto prvalue_proj = [](const CheckDoubleMove& x) -> CheckDoubleMove { return x; };
     assert(&std::ranges::clamp(val, low, high, moving_comp, prvalue_proj) == &val);
   }
-
+  { // Make sure we don't call the projection more than three times per [alg.clamp], see #64717
+    auto projection_function = [](const int value) -> int {
+      static int counter = 0;
+      assert(counter++ != 3);
+      return value;
+    };
+    std::ranges::clamp(3, 2, 4, std::ranges::less{}, &projection_function);
+  }
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp b/libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp
deleted file mode 100644
index fd47de4eb390534..000000000000000
--- a/libcxx/test/std/algorithms/ranges_projection_not_called_more_than_three_times.pass.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <algorithm>
-#include <assert_macros.h>
-
-int Projection(const int value) {
-  static int counter = 0;
-
-  if (counter++ == 3) {
-    TEST_FAIL("projection called more than 3 times")
-  }
-
-  return value;
-}
-
-int main() {
-  std::ranges::clamp(3, 2, 4, std::ranges::less{}, &Projection);
-  return 0;
-}

>From 4e1857f82ffb001300d7e7e4fdd2421bf9db545a Mon Sep 17 00:00:00 2001
From: PandaNinjas <admin at malwarefight.wip.la>
Date: Fri, 15 Sep 2023 11:21:49 -0700
Subject: [PATCH 10/13] [libcxx] Fix typo in ranges_clamp.h

---
 libcxx/include/__algorithm/ranges_clamp.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h
index 182aed4fc53cbed..e6c86207254a19f 100644
--- a/libcxx/include/__algorithm/ranges_clamp.h
+++ b/libcxx/include/__algorithm/ranges_clamp.h
@@ -40,7 +40,7 @@ struct __fn {
     auto&& __projected = std::invoke(__proj, __value);
     if (std::invoke(__comp, std::forward<decltype(__projected)>(__projected), std::invoke(__proj, __low)))
       return __low;
-    else if (std::invoke(__comp, std::invoke(__proj, __high), std::forward<decltype(__projected)>(__projected))
+    else if (std::invoke(__comp, std::invoke(__proj, __high), std::forward<decltype(__projected)>(__projected)))
       return __high;
     else
       return __value;

>From d2adb7371df960f819d40fdac1a1b28af33c180d Mon Sep 17 00:00:00 2001
From: PandaNinjas <admin at malwarefight.wip.la>
Date: Fri, 15 Sep 2023 11:45:48 -0700
Subject: [PATCH 11/13] [libcxx] fix tests, apply changes from code review

---
 libcxx/include/__algorithm/ranges_clamp.h                   | 6 +++---
 .../algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp  | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h
index e6c86207254a19f..e3bac65856fe984 100644
--- a/libcxx/include/__algorithm/ranges_clamp.h
+++ b/libcxx/include/__algorithm/ranges_clamp.h
@@ -37,10 +37,10 @@ struct __fn {
     _LIBCPP_ASSERT_UNCATEGORIZED(!bool(std::invoke(__comp, std::invoke(__proj, __high), std::invoke(__proj, __low))),
                                  "Bad bounds passed to std::ranges::clamp");
 
-    auto&& __projected = std::invoke(__proj, __value);
-    if (std::invoke(__comp, std::forward<decltype(__projected)>(__projected), std::invoke(__proj, __low)))
+    auto&& __projected = std::forward<decltype(__projected)>(std::invoke(__proj, __value));
+    if (std::invoke(__comp, __projected), std::invoke(__proj, __low)))
       return __low;
-    else if (std::invoke(__comp, std::invoke(__proj, __high), std::forward<decltype(__projected)>(__projected)))
+    else if (std::invoke(__comp, std::invoke(__proj, __high), __projected))
       return __high;
     else
       return __value;
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
index 189d8cc4d886e8e..2ff685f67ac0ce3 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
@@ -109,12 +109,12 @@ constexpr bool test() {
     assert(&std::ranges::clamp(val, low, high, moving_comp, prvalue_proj) == &val);
   }
   { // Make sure we don't call the projection more than three times per [alg.clamp], see #64717
-    auto projection_function = [](const int value) -> int {
-      static int counter = 0;
+    int counter = 0;
+    auto projection_function = [counter](const int value) -> int {
       assert(counter++ != 3);
       return value;
     };
-    std::ranges::clamp(3, 2, 4, std::ranges::less{}, &projection_function);
+    std::ranges::clamp(3, 2, 4, std::ranges::less{}, projection_function);
   }
   return true;
 }

>From 403ac820e7a89e12820b021ce643c713e8de17a9 Mon Sep 17 00:00:00 2001
From: PandaNinjas <admin at malwarefight.wip.la>
Date: Fri, 15 Sep 2023 12:54:17 -0700
Subject: [PATCH 12/13] Apply testing changes from code review

---
 .../alg.clamp/ranges.clamp.pass.cpp           | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
index 2ff685f67ac0ce3..8470af2620af3a1 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
@@ -116,6 +116,25 @@ constexpr bool test() {
     };
     std::ranges::clamp(3, 2, 4, std::ranges::less{}, projection_function);
   }
+  {
+    struct Foo {
+      std::string s;
+    };
+
+    // taking by value is important here
+    auto comparator = [](std::string a, std::string b) {
+      return std::atoi(a.c_str()) < std::atoi(b.c_str());
+    };
+
+    auto projection = [](Foo const& foo) {
+      return foo.s;
+    };
+
+    Foo foo{"12"};
+    Foo high{"10"};
+    Foo low{"1"};
+    assert(std::ranges::clamp(foo, low, high, comparator, projection).s == "10");
+  }
   return true;
 }
 

>From 1f686fc8883af3af6d66216555bc85dc9931ea02 Mon Sep 17 00:00:00 2001
From: Jocelyn Castellano <admin at malwarefight.wip.la>
Date: Fri, 15 Sep 2023 13:32:11 -0700
Subject: [PATCH 13/13] Apply suggestions from code review

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__algorithm/ranges_clamp.h                     | 4 ++--
 .../algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp    | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h
index e3bac65856fe984..9017354667c80e3 100644
--- a/libcxx/include/__algorithm/ranges_clamp.h
+++ b/libcxx/include/__algorithm/ranges_clamp.h
@@ -37,8 +37,8 @@ struct __fn {
     _LIBCPP_ASSERT_UNCATEGORIZED(!bool(std::invoke(__comp, std::invoke(__proj, __high), std::invoke(__proj, __low))),
                                  "Bad bounds passed to std::ranges::clamp");
 
-    auto&& __projected = std::forward<decltype(__projected)>(std::invoke(__proj, __value));
-    if (std::invoke(__comp, __projected), std::invoke(__proj, __low)))
+    auto&& __projected = std::invoke(__proj, __value);
+    if (std::invoke(__comp, __projected, std::invoke(__proj, __low)))
       return __low;
     else if (std::invoke(__comp, std::invoke(__proj, __high), __projected))
       return __high;
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
index 8470af2620af3a1..63f8281a2821fee 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp
@@ -110,7 +110,7 @@ constexpr bool test() {
   }
   { // Make sure we don't call the projection more than three times per [alg.clamp], see #64717
     int counter = 0;
-    auto projection_function = [counter](const int value) -> int {
+    auto projection_function = [&counter](const int value) -> int {
       assert(counter++ != 3);
       return value;
     };



More information about the cfe-commits mailing list