Skip to content

Conversation

philnik777
Copy link
Contributor

#140407 accidentally enabled hash for cv-qualified types. This patch disables these specializations again.

@philnik777 philnik777 requested a review from a team as a code owner August 28, 2025 08:38
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Aug 28, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 28, 2025

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

Changes

#140407 accidentally enabled hash for cv-qualified types. This patch disables these specializations again.


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

7 Files Affected:

  • (modified) libcxx/include/CMakeLists.txt (+1)
  • (modified) libcxx/include/__functional/hash.h (+10-4)
  • (added) libcxx/include/__type_traits/is_unqualified.h (+25)
  • (modified) libcxx/include/module.modulemap.in (+1)
  • (modified) libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp (+5)
  • (modified) libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp (+5)
  • (modified) libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp (+5)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 6fd16419f0c49..1f91b4828d4d3 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -872,6 +872,7 @@ set(files
   __type_traits/is_trivially_relocatable.h
   __type_traits/is_unbounded_array.h
   __type_traits/is_union.h
+  __type_traits/is_unqualified.h
   __type_traits/is_unsigned.h
   __type_traits/is_valid_expansion.h
   __type_traits/is_void.h
diff --git a/libcxx/include/__functional/hash.h b/libcxx/include/__functional/hash.h
index 489a6f00b8a3d..d95b2c0884984 100644
--- a/libcxx/include/__functional/hash.h
+++ b/libcxx/include/__functional/hash.h
@@ -21,6 +21,7 @@
 #include <__type_traits/is_enum.h>
 #include <__type_traits/is_floating_point.h>
 #include <__type_traits/is_integral.h>
+#include <__type_traits/is_unqualified.h>
 #include <__type_traits/underlying_type.h>
 #include <__utility/pair.h>
 #include <__utility/swap.h>
@@ -355,7 +356,8 @@ struct __hash_impl {
 };
 
 template <class _Tp>
-struct __hash_impl<_Tp, __enable_if_t<is_enum<_Tp>::value> > : __unary_function<_Tp, size_t> {
+struct __hash_impl<_Tp, __enable_if_t<is_enum<_Tp>::value && __is_unqualified_v<_Tp>> >
+    : __unary_function<_Tp, size_t> {
   _LIBCPP_HIDE_FROM_ABI size_t operator()(_Tp __v) const _NOEXCEPT {
     using type = __underlying_type_t<_Tp>;
     return hash<type>()(static_cast<type>(__v));
@@ -363,17 +365,21 @@ struct __hash_impl<_Tp, __enable_if_t<is_enum<_Tp>::value> > : __unary_function<
 };
 
 template <class _Tp>
-struct __hash_impl<_Tp, __enable_if_t<is_integral<_Tp>::value && (sizeof(_Tp) <= sizeof(size_t))> >
+struct __hash_impl<
+    _Tp,
+    __enable_if_t<is_integral<_Tp>::value && __is_unqualified_v<_Tp> && (sizeof(_Tp) <= sizeof(size_t))> >
     : __unary_function<_Tp, size_t> {
   _LIBCPP_HIDE_FROM_ABI size_t operator()(_Tp __v) const _NOEXCEPT { return static_cast<size_t>(__v); }
 };
 
 template <class _Tp>
-struct __hash_impl<_Tp, __enable_if_t<is_integral<_Tp>::value && (sizeof(_Tp) > sizeof(size_t))> >
+struct __hash_impl<_Tp,
+                   __enable_if_t<is_integral<_Tp>::value && __is_unqualified_v<_Tp> && (sizeof(_Tp) > sizeof(size_t))> >
     : __scalar_hash<_Tp> {};
 
 template <class _Tp>
-struct __hash_impl<_Tp, __enable_if_t<is_floating_point<_Tp>::value> > : __scalar_hash<_Tp> {
+struct __hash_impl<_Tp, __enable_if_t<is_floating_point<_Tp>::value && __is_unqualified_v<_Tp> > >
+    : __scalar_hash<_Tp> {
   _LIBCPP_HIDE_FROM_ABI size_t operator()(_Tp __v) const _NOEXCEPT {
     // -0.0 and 0.0 should return same hash
     if (__v == 0.0f)
diff --git a/libcxx/include/__type_traits/is_unqualified.h b/libcxx/include/__type_traits/is_unqualified.h
new file mode 100644
index 0000000000000..7970b3611601f
--- /dev/null
+++ b/libcxx/include/__type_traits/is_unqualified.h
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___TYPE_TRAITS_IS_UNQUALIFIED_H
+#define _LIBCPP___TYPE_TRAITS_IS_UNQUALIFIED_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+inline const bool __is_unqualified_v = __is_same(_Tp, __remove_cvref(_Tp));
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_IS_UNQUALIFIED_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index ee18d04c78d0e..c50c4dd73d4bb 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -335,6 +335,7 @@ module std_core [system] {
       header "__type_traits/is_union.h"
       export std_core.type_traits.integral_constant
     }
+    module is_unqualified { header "__type_traits/is_unqualified.h" }
     module is_unsigned {
       header "__type_traits/is_unsigned.h"
       export std_core.type_traits.integral_constant
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
index fd1fbe5f113ba..b796960975ea1 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
@@ -19,6 +19,7 @@
 #include <limits>
 #include <type_traits>
 
+#include "poisoned_hash_helper.h"
 #include "test_macros.h"
 
 enum class Colors { red, orange, yellow, green, blue, indigo, violet };
@@ -33,6 +34,10 @@ template <class T>
 void
 test()
 {
+    test_hash_disabled<const T>();
+    test_hash_disabled<volatile T>();
+    test_hash_disabled<const volatile T>();
+
     typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
index b8f85e193dc8f..bf3e38e619519 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
@@ -25,12 +25,17 @@
 #include <limits>
 #include <cmath>
 
+#include "poisoned_hash_helper.h"
 #include "test_macros.h"
 
 template <class T>
 void
 test()
 {
+    test_hash_disabled<const T>();
+    test_hash_disabled<volatile T>();
+    test_hash_disabled<const volatile T>();
+
     typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
index 14af7093c1e36..cfa6b324ba323 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
@@ -24,12 +24,17 @@
 #include <limits>
 #include <type_traits>
 
+#include "poisoned_hash_helper.h"
 #include "test_macros.h"
 
 template <class T>
 void
 test()
 {
+    test_hash_disabled<const T>();
+    test_hash_disabled<volatile T>();
+    test_hash_disabled<const volatile T>();
+
     typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");

Copy link

github-actions bot commented Aug 28, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff origin/main HEAD --extensions cpp,h -- libcxx/include/__type_traits/is_unqualified.h libcxx/include/__functional/hash.h libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp

⚠️
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing origin/main to the base branch/commit you want to compare against.
⚠️

View the diff from clang-format here.
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
index 05a19329f..e854bc363 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
@@ -38,12 +38,12 @@ void
 test()
 {
 #if TEST_STD_VER >= 11
-    test_hash_disabled<const T>();
-    test_hash_disabled<volatile T>();
-    test_hash_disabled<const volatile T>();
+  test_hash_disabled<const T>();
+  test_hash_disabled<volatile T>();
+  test_hash_disabled<const volatile T>();
 #endif
 
-    typedef std::hash<T> H;
+  typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
index f6c31d068..b96fbbb68 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
@@ -36,12 +36,12 @@ void
 test()
 {
 #if TEST_STD_VER >= 11
-    test_hash_disabled<const T>();
-    test_hash_disabled<volatile T>();
-    test_hash_disabled<const volatile T>();
+  test_hash_disabled<const T>();
+  test_hash_disabled<volatile T>();
+  test_hash_disabled<const volatile T>();
 #endif
 
-    typedef std::hash<T> H;
+  typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
index c798b3aa7..7fb9a953d 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
@@ -35,12 +35,12 @@ void
 test()
 {
 #if TEST_STD_VER >= 11
-    test_hash_disabled<const T>();
-    test_hash_disabled<volatile T>();
-    test_hash_disabled<const volatile T>();
+  test_hash_disabled<const T>();
+  test_hash_disabled<volatile T>();
+  test_hash_disabled<const volatile T>();
 #endif
 
-    typedef std::hash<T> H;
+  typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");

@philnik777 philnik777 changed the title [libc++] Disabled cv-qualified arithmetic hash specializations [libc++] Disable cv-qualified arithmetic hash specializations Aug 28, 2025
@philnik777 philnik777 added this to the LLVM 21.x Release milestone Aug 28, 2025
@github-project-automation github-project-automation bot moved this to Needs Triage in LLVM Release Status Aug 28, 2025
@philnik777 philnik777 force-pushed the disable_qualified_hashes branch from 88f028b to a8083ac Compare August 28, 2025 11:42
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, thanks for the fix!

@github-project-automation github-project-automation bot moved this from Needs Triage to Needs Merge in LLVM Release Status Aug 28, 2025
@philnik777 philnik777 force-pushed the disable_qualified_hashes branch from a8083ac to 565cf2a Compare August 29, 2025 08:29
@philnik777 philnik777 merged commit 8a65c4f into llvm:main Aug 29, 2025
70 of 74 checks passed
@github-project-automation github-project-automation bot moved this from Needs Merge to Done in LLVM Release Status Aug 29, 2025
@philnik777 philnik777 deleted the disable_qualified_hashes branch August 29, 2025 16:13
@philnik777
Copy link
Contributor Author

/cherry-pick 8a65c4f

@llvmbot
Copy link
Member

llvmbot commented Aug 29, 2025

/pull-request #156054

tru pushed a commit to llvmbot/llvm-project that referenced this pull request Sep 3, 2025
…55786)

llvm#140407 accidentally enabled `hash` for cv-qualified types. This patch
disables these specializations again.

(cherry picked from commit 8a65c4f)
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.
Projects
Development

Successfully merging this pull request may close these issues.

3 participants