Implement P0513R0, Poisoning the Hash.
authorVille Voutilainen <ville.voutilainen@gmail.com>
Mon, 14 Nov 2016 21:22:53 +0000 (23:22 +0200)
committerVille Voutilainen <ville@gcc.gnu.org>
Mon, 14 Nov 2016 21:22:53 +0000 (23:22 +0200)
* include/bits/functional_hash.h (__poison_hash): New.
* include/bits/unique_ptr.h
(hash<unique_ptr<_Tp, _Dp>>): Derive from __poison_hash.
* include/std/optional (hash<optional<_Tp>>): Likewise.
* include/std/variant (hash<variant<_Types...>>): Likewise.
* testsuite/20_util/default_delete/48631_neg.cc: Adjust.
* testsuite/20_util/default_delete/void_neg.cc: Likewise.
* testsuite/20_util/optional/hash.cc: New.
* testsuite/20_util/unique_ptr/assign/48635_neg.cc: Adjust.
* testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc: Adjust.
* testsuite/20_util/unique_ptr/hash/1.cc: Add tests for
poisoned fancy pointer hashes.
* testsuite/20_util/variant/hash.cc: New.

From-SVN: r242402

12 files changed:
libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/functional_hash.h
libstdc++-v3/include/bits/unique_ptr.h
libstdc++-v3/include/std/optional
libstdc++-v3/include/std/variant
libstdc++-v3/testsuite/20_util/default_delete/48631_neg.cc
libstdc++-v3/testsuite/20_util/default_delete/void_neg.cc
libstdc++-v3/testsuite/20_util/optional/hash.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc
libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc
libstdc++-v3/testsuite/20_util/unique_ptr/hash/1.cc
libstdc++-v3/testsuite/20_util/variant/hash.cc [new file with mode: 0644]

index fd309713686a711f1225dc2e7414fe2aae6469eb..d3da57cc9a45b8b637be599d2c57f02f70d9c596 100644 (file)
@@ -1,3 +1,20 @@
+2016-11-14  Ville Voutilainen  <ville.voutilainen@gmail.com>
+
+       Implement P0513R0, Poisoning the Hash.
+       * include/bits/functional_hash.h (__poison_hash): New.
+       * include/bits/unique_ptr.h
+       (hash<unique_ptr<_Tp, _Dp>>): Derive from __poison_hash.
+       * include/std/optional (hash<optional<_Tp>>): Likewise.
+       * include/std/variant (hash<variant<_Types...>>): Likewise.
+       * testsuite/20_util/default_delete/48631_neg.cc: Adjust.
+       * testsuite/20_util/default_delete/void_neg.cc: Likewise.
+       * testsuite/20_util/optional/hash.cc: New.
+       * testsuite/20_util/unique_ptr/assign/48635_neg.cc: Adjust.
+       * testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc: Adjust.
+       * testsuite/20_util/unique_ptr/hash/1.cc: Add tests for
+       poisoned fancy pointer hashes.
+       * testsuite/20_util/variant/hash.cc: New.
+
 2016-11-14  Ville Voutilainen  <ville.voutilainen@gmail.com>
 
        Implement P0504R0 (Revisiting in-place tag types for
index def35f589e4385735ff25977531fbec99b0506d9..dc096831869213e556a7dcfeed3b320525674979 100644 (file)
@@ -57,6 +57,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct hash;
 
+  template<typename _Tp, typename = void>
+    struct __poison_hash
+    {
+    private:
+      // Private rather than deleted to be non-trivially-copyable.
+      __poison_hash(__poison_hash&&);
+      ~__poison_hash();
+    };
+
+  template<typename _Tp>
+    struct __poison_hash<_Tp, __void_t<decltype(hash<_Tp>()(declval<_Tp>()))>>
+    {
+    };
+
   // Helper struct for SFINAE-poisoning non-enum types.
   template<typename _Tp, bool = is_enum<_Tp>::value>
     struct __hash_enum
index 996c9eac3c3d5de15e0d03225a55cabb29256ea1..f9ec60f769bbd2801f989d0943dcfec3ed841e66 100644 (file)
@@ -36,6 +36,7 @@
 #include <utility>
 #include <tuple>
 #include <bits/stl_function.h>
+#include <bits/functional_hash.h>
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -763,7 +764,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::hash specialization for unique_ptr.
   template<typename _Tp, typename _Dp>
     struct hash<unique_ptr<_Tp, _Dp>>
-    : public __hash_base<size_t, unique_ptr<_Tp, _Dp>>
+    : public __hash_base<size_t, unique_ptr<_Tp, _Dp>>,
+    private __poison_hash<typename unique_ptr<_Tp, _Dp>::pointer>
     {
       size_t
       operator()(const unique_ptr<_Tp, _Dp>& __u) const noexcept
index 35b6932fc984451f94a82bd7426a5843701ab2ef..ac73ea75bae0099aaa1c1120a6237ad39732efa9 100644 (file)
@@ -943,7 +943,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Hash.
   template<typename _Tp>
-    struct hash<optional<_Tp>>
+    struct hash<optional<_Tp>> : private __poison_hash<remove_const_t<_Tp>>
     {
       using result_type = size_t;
       using argument_type = optional<_Tp>;
index 0d5fa56670b909f4352a61a917d4bd7994db3e3a..6f3f67c578097445acd17c61ba93e29aebf9bdd5 100644 (file)
@@ -41,6 +41,7 @@
 #include <bits/functexcept.h>
 #include <bits/move.h>
 #include <bits/uses_allocator.h>
+#include <bits/functional_hash.h>
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -1337,6 +1338,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename... _Types>
     struct hash<variant<_Types...>>
+    : private __poison_hash<remove_const_t<_Types>>...
     {
       using result_type = size_t;
       using argument_type = variant<_Types...>;
index 23b3f8b6c46406ea121da76836adec225bd203eb..2f99b25758bc404ca93fb76606d853c341e6d49a 100644 (file)
@@ -26,4 +26,4 @@ struct D : B { };
 D d;
 std::default_delete<B[]> db;
 typedef decltype(db(&d)) type; // { dg-error "no match" }
-// { dg-error "no type" "" { target *-*-* } 107 }
+// { dg-error "no type" "" { target *-*-* } 108 }
index c9688bcda7b5e95015447807911ae1b8ae2e0029..74aa32f7cb0c4ed3270a189585797835a0227bd6 100644 (file)
@@ -25,5 +25,5 @@ void test01()
 {
   std::default_delete<void> d;
   d(nullptr);   // { dg-error "here" }
-  // { dg-error "incomplete" "" { target *-*-* } 73 }
+  // { dg-error "incomplete" "" { target *-*-* } 74 }
 }
diff --git a/libstdc++-v3/testsuite/20_util/optional/hash.cc b/libstdc++-v3/testsuite/20_util/optional/hash.cc
new file mode 100644 (file)
index 0000000..294a617
--- /dev/null
@@ -0,0 +1,38 @@
+// { dg-options "-std=gnu++17" }
+
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a moved_to of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <optional>
+#include <testsuite_hooks.h>
+
+class S{}; // No hash specialization
+
+template<class T>
+auto f(int) -> decltype(std::hash<std::optional<T>>(), std::true_type());
+
+template<class T>
+auto f(...) -> decltype(std::false_type());
+
+static_assert(!decltype(f<S>(0))::value, "");
+
+int main()
+{
+  int x = 42;
+  std::optional<int> x2 = 42;
+  VERIFY(std::hash<int>()(x) == std::hash<std::optional<int>>()(x2));
+}
index 9d248265be97950c0116c397cacb4b7531e414dc..e9655f137505c4df83e001c02e8076f9a21ec735 100644 (file)
@@ -42,10 +42,10 @@ void f()
   std::unique_ptr<int, D&> ud(nullptr, d);
   ub = std::move(ud); // { dg-error "no match" }
   ub2 = ud; // { dg-error "no match" }
-// { dg-error "no type" "" { target *-*-* } 288 }
+// { dg-error "no type" "" { target *-*-* } 289 }
 
   std::unique_ptr<int[], B&> uba(nullptr, b);
   std::unique_ptr<int[], D&> uda(nullptr, d);
   uba = std::move(uda); // { dg-error "no match" }
-// { dg-error "no type" "" { target *-*-* } 539 }
+// { dg-error "no type" "" { target *-*-* } 540 }
 }
index a0f77c0c00979abbf82ceccabad66cc1f33fac5d..3e6f41bd8da9867c6581281173329ff621c795d5 100644 (file)
@@ -39,7 +39,7 @@ test07()
   std::unique_ptr<const A[]> cA3(p); // { dg-error "no matching function" }
   std::unique_ptr<volatile A[]> vA3(p); // { dg-error "no matching function" }
   std::unique_ptr<const volatile A[]> cvA3(p); // { dg-error "no matching function" }
-  // { dg-error "no type" "" { target *-*-* } 447 }
+  // { dg-error "no type" "" { target *-*-* } 448 }
 }
 
 template<typename T>
index 6fc4fdc809d3275f3abd1ad4b0c25db173bd3da7..ae73a43cfb3162ebab505a769b4228b24a70338e 100644 (file)
 
 #include <memory>
 #include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+// User-defined pointer type that throws if a null pointer is dereferenced.
+template<typename T>
+struct Pointer : __gnu_test::PointerBase<Pointer<T>, T>
+{
+};
+
+template<typename T>
+struct PointerDeleter : std::default_delete<T>
+{
+  typedef Pointer<T> pointer;
+  void operator()(pointer) const;
+};
+
+template<class T>
+auto f(int) -> decltype(std::hash<std::unique_ptr<T,
+                       PointerDeleter<T>>>(), std::true_type());
+
+template<class T>
+auto f(...) -> decltype(std::false_type());
+
+static_assert(!decltype(f<Pointer<int>>(0))::value, "");
 
 void test01()
 {
diff --git a/libstdc++-v3/testsuite/20_util/variant/hash.cc b/libstdc++-v3/testsuite/20_util/variant/hash.cc
new file mode 100644 (file)
index 0000000..74f97ed
--- /dev/null
@@ -0,0 +1,38 @@
+// { dg-options "-std=gnu++17" }
+
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a moved_to of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <variant>
+#include <testsuite_hooks.h>
+
+class S{}; // No hash specialization
+
+template<class T>
+auto f(int) -> decltype(std::hash<std::variant<T>>(), std::true_type());
+
+template<class T>
+auto f(...) -> decltype(std::false_type());
+
+static_assert(!decltype(f<S>(0))::value, "");
+
+int main()
+{
+  int x = 42;
+  std::variant<int> x2 = 42;
+  VERIFY(std::hash<int>()(x) == std::hash<std::variant<int>>()(x2));
+}