[libcxx-commits] [libcxx] [libc++] Make sure that __desugars_to isn't tripped up by reference_wrapper and cv-refs (PR #132092)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Thu Mar 20 12:04:16 PDT 2025


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

>From 72990d44832ffe0de57b71912f6088cab2d5fa29 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 19 Mar 2025 16:14:24 -0400
Subject: [PATCH 1/5] [libc++] Make sure that __desugars_to isn't tripped up by
 reference_wrapper and cv-refs

Previously, any cv-ref qualification on an operation would cause
__desugars_to to report false, which would lead to unnecessary
pessimizations. The same holds for reference_wrapper.

In practice, cv-ref qualifications on the operation itself are not
relevant to determining whether an operation desugars to something
else or not.

Fixes #129312
---
 .../include/__functional/reference_wrapper.h  |  6 +++
 libcxx/include/__type_traits/desugars_to.h    | 18 +++++++++
 .../type_traits/desugars_to.compile.pass.cpp  | 40 +++++++++++++++++++
 .../refwrap/desugars_to.compile.pass.cpp      | 23 +++++++++++
 4 files changed, 87 insertions(+)
 create mode 100644 libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp

diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index d6cd6428f22db..3678b5553c444 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -15,6 +15,7 @@
 #include <__config>
 #include <__functional/weak_result_type.h>
 #include <__memory/addressof.h>
+#include <__type_traits/desugars_to.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
@@ -149,6 +150,11 @@ void ref(const _Tp&&) = delete;
 template <class _Tp>
 void cref(const _Tp&&) = delete;
 
+// Let desugars-to pass through std::reference_wrapper
+template <class _CanonicalTag, class _Operation, class... _Args>
+inline const bool __desugars_to_v<_CanonicalTag, reference_wrapper<_Operation>, _Args...> =
+    __desugars_to_v<_CanonicalTag, _Operation, _Args...>;
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H
diff --git a/libcxx/include/__type_traits/desugars_to.h b/libcxx/include/__type_traits/desugars_to.h
index 452c70bfbad66..ed8b5ab8d318a 100644
--- a/libcxx/include/__type_traits/desugars_to.h
+++ b/libcxx/include/__type_traits/desugars_to.h
@@ -52,6 +52,24 @@ struct __totally_ordered_less_tag {};
 template <class _CanonicalTag, class _Operation, class... _Args>
 inline const bool __desugars_to_v = false;
 
+// For the purpose of determining whether something desugars to something else,
+// we disregard the cv-refs of the operation itself.
+template <class _CanonicalTag, class _Operation, class... _Args>
+inline const bool __desugars_to_v<_CanonicalTag, _Operation const, _Args...> =
+    __desugars_to_v<_CanonicalTag, _Operation, _Args...>;
+template <class _CanonicalTag, class _Operation, class... _Args>
+inline const bool __desugars_to_v<_CanonicalTag, _Operation volatile, _Args...> =
+    __desugars_to_v<_CanonicalTag, _Operation, _Args...>;
+template <class _CanonicalTag, class _Operation, class... _Args>
+inline const bool __desugars_to_v<_CanonicalTag, _Operation const volatile, _Args...> =
+    __desugars_to_v<_CanonicalTag, _Operation, _Args...>;
+template <class _CanonicalTag, class _Operation, class... _Args>
+inline const bool __desugars_to_v<_CanonicalTag, _Operation&, _Args...> =
+    __desugars_to_v<_CanonicalTag, _Operation, _Args...>;
+template <class _CanonicalTag, class _Operation, class... _Args>
+inline const bool __desugars_to_v<_CanonicalTag, _Operation&&, _Args...> =
+    __desugars_to_v<_CanonicalTag, _Operation, _Args...>;
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___TYPE_TRAITS_DESUGARS_TO_H
diff --git a/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp b/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
new file mode 100644
index 0000000000000..780d9372d8387
--- /dev/null
+++ b/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__type_traits/desugars_to.h>
+
+struct Tag {};
+struct Operation {};
+template <>
+bool const std::__desugars_to_v<Tag, Operation> = true;
+
+void tests() {
+  // Make sure that __desugars_to is false by default
+  {
+    struct Foo {};
+    static_assert(!std::__desugars_to_v<Tag, Foo>, "");
+  }
+
+  // Make sure that __desugars_to bypasses cv and ref qualifiers on the operation
+  {
+    static_assert(std::__desugars_to_v<Tag, Operation>, ""); // no quals
+    static_assert(std::__desugars_to_v<Tag, Operation const>, "");
+    static_assert(std::__desugars_to_v<Tag, Operation volatile>, "");
+    static_assert(std::__desugars_to_v<Tag, Operation const volatile>, "");
+
+    static_assert(std::__desugars_to_v<Tag, Operation&>, "");
+    static_assert(std::__desugars_to_v<Tag, Operation const&>, "");
+    static_assert(std::__desugars_to_v<Tag, Operation volatile&>, "");
+    static_assert(std::__desugars_to_v<Tag, Operation const volatile&>, "");
+
+    static_assert(std::__desugars_to_v<Tag, Operation&&>, "");
+    static_assert(std::__desugars_to_v<Tag, Operation const&&>, "");
+    static_assert(std::__desugars_to_v<Tag, Operation volatile&&>, "");
+    static_assert(std::__desugars_to_v<Tag, Operation const volatile&&>, "");
+  }
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
new file mode 100644
index 0000000000000..30e5654c0824f
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <functional>
+
+// reference_wrapper
+
+// Ensure that std::reference_wrapper does not inhibit optimizations based on the
+// std::__desugars_to internal helper.
+
+#include <functional>
+
+static_assert(std::__desugars_to_v<std::__equal_tag, std::equal_to<void>, int, int>,
+              "something is wrong with the test");
+
+// make sure we pass through reference_wrapper
+static_assert(std::__desugars_to_v<std::__equal_tag, std::reference_wrapper<std::equal_to<void> >, int, int>, "");
+static_assert(std::__desugars_to_v<std::__equal_tag, std::reference_wrapper<std::equal_to<void> const>, int, int>, "");

>From 918e2696cdb14807fa5c6b921f4130eda77905d9 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 20 Mar 2025 13:36:36 -0400
Subject: [PATCH 2/5] Decouple test from std::equal_to

---
 .../refwrap/desugars_to.compile.pass.cpp             | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
index 30e5654c0824f..c98d688b41084 100644
--- a/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
@@ -15,9 +15,13 @@
 
 #include <functional>
 
-static_assert(std::__desugars_to_v<std::__equal_tag, std::equal_to<void>, int, int>,
-              "something is wrong with the test");
+struct Operation {};
+struct Tag {};
+template <>
+bool const std::__desugars_to_v<Tag, Operation> = true;
+
+static_assert(std::__desugars_to_v<Tag, Operation>, "something is wrong with the test");
 
 // make sure we pass through reference_wrapper
-static_assert(std::__desugars_to_v<std::__equal_tag, std::reference_wrapper<std::equal_to<void> >, int, int>, "");
-static_assert(std::__desugars_to_v<std::__equal_tag, std::reference_wrapper<std::equal_to<void> const>, int, int>, "");
+static_assert(std::__desugars_to_v<Tag, std::reference_wrapper<Operation> >, "");
+static_assert(std::__desugars_to_v<Tag, std::reference_wrapper<Operation const> >, "");

>From 691272c6dbbd71703ead367f9d7e1a59e3073c31 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 20 Mar 2025 13:37:47 -0400
Subject: [PATCH 3/5] Fix modules issues

---
 .../function.objects/refwrap/desugars_to.compile.pass.cpp        | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
index c98d688b41084..96e3e76aac685 100644
--- a/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
@@ -14,6 +14,7 @@
 // std::__desugars_to internal helper.
 
 #include <functional>
+#include <__type_traits/desugars_to.h>
 
 struct Operation {};
 struct Tag {};

>From ce363a7639f2784c24e924c54f865509f0212c2b Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 20 Mar 2025 14:06:18 -0400
Subject: [PATCH 4/5] Frozen C++03 headers

---
 libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp     | 2 ++
 .../function.objects/refwrap/desugars_to.compile.pass.cpp       | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp b/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
index 780d9372d8387..6759ebc6de948 100644
--- a/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
+++ b/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// UNSUPPORTED: FROZEN-CXX03-HEADERS-FIXME
+
 #include <__type_traits/desugars_to.h>
 
 struct Tag {};
diff --git a/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
index 96e3e76aac685..d56d5dc180dfc 100644
--- a/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/refwrap/desugars_to.compile.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// UNSUPPORTED: FROZEN-CXX03-HEADERS-FIXME
+
 // <functional>
 
 // reference_wrapper

>From 560f7d8d2f66def090fcce8157a49205de115d2f Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 20 Mar 2025 15:04:01 -0400
Subject: [PATCH 5/5] Simplify some uses of __desugars_to with remove_cvref

---
 libcxx/include/__algorithm/sort.h        | 4 +---
 libcxx/include/__algorithm/stable_sort.h | 4 +---
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 8dd0721f2c65f..4332b62544b40 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -34,7 +34,6 @@
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_copyable.h>
-#include <__type_traits/remove_cvref.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
 #include <climits>
@@ -52,8 +51,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Compare, class _Iter, class _Tp = typename iterator_traits<_Iter>::value_type>
 inline const bool __use_branchless_sort =
     __libcpp_is_contiguous_iterator<_Iter>::value && __is_cheap_to_copy<_Tp> && is_arithmetic<_Tp>::value &&
-    (__desugars_to_v<__less_tag, __remove_cvref_t<_Compare>, _Tp, _Tp> ||
-     __desugars_to_v<__greater_tag, __remove_cvref_t<_Compare>, _Tp, _Tp>);
+    (__desugars_to_v<__less_tag, _Compare, _Tp, _Tp> || __desugars_to_v<__greater_tag, _Compare, _Tp, _Tp>);
 
 namespace __detail {
 
diff --git a/libcxx/include/__algorithm/stable_sort.h b/libcxx/include/__algorithm/stable_sort.h
index 76d9e5557008f..c7f9780e3f627 100644
--- a/libcxx/include/__algorithm/stable_sort.h
+++ b/libcxx/include/__algorithm/stable_sort.h
@@ -29,7 +29,6 @@
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
-#include <__type_traits/remove_cvref.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
 
@@ -246,8 +245,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort(
   }
 
 #if _LIBCPP_STD_VER >= 17
-  constexpr auto __default_comp =
-      __desugars_to_v<__totally_ordered_less_tag, __remove_cvref_t<_Compare>, value_type, value_type >;
+  constexpr auto __default_comp = __desugars_to_v<__totally_ordered_less_tag, _Compare, value_type, value_type >;
   constexpr auto __integral_value =
       is_integral_v<value_type > && is_same_v< value_type&, __iter_reference<_RandomAccessIterator>>;
   constexpr auto __allowed_radix_sort = __default_comp && __integral_value;



More information about the libcxx-commits mailing list