On 14 November 2016 at 00:49, Ville Voutilainen <ville.voutilai...@gmail.com> wrote: > On 14 November 2016 at 00:48, Ville Voutilainen > <ville.voutilai...@gmail.com> wrote: >> Tested on Linux-x64. > > > Ah, negative tests incomplete, sending a follow-up soon...
Here: 2016-11-13 Ville Voutilainen <ville.voutilai...@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/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.
diff --git a/libstdc++-v3/include/bits/functional_hash.h b/libstdc++-v3/include/bits/functional_hash.h index def35f5..dc09683 100644 --- a/libstdc++-v3/include/bits/functional_hash.h +++ b/libstdc++-v3/include/bits/functional_hash.h @@ -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 diff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h index 996c9ea..f9ec60f 100644 --- a/libstdc++-v3/include/bits/unique_ptr.h +++ b/libstdc++-v3/include/bits/unique_ptr.h @@ -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 diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index f272876..69aa54d 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -990,7 +990,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>; diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 314f5f2..004d667 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -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...>; diff --git a/libstdc++-v3/testsuite/20_util/optional/hash.cc b/libstdc++-v3/testsuite/20_util/optional/hash.cc new file mode 100644 index 0000000..294a617 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/hash.cc @@ -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)); +} diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc index 9d24826..e9655f1 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc @@ -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 } } diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc index a0f77c0..3e6f41b 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc @@ -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> diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/hash/1.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/hash/1.cc index 6fc4fdc..ae73a43 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/hash/1.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/hash/1.cc @@ -21,6 +21,29 @@ #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 index 0000000..74f97ed --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/variant/hash.cc @@ -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)); +}