Commit 7ac20567 by Jonathan Wakely Committed by Jonathan Wakely

PR libstdc++/85965 delay static assertions until types are complete

The static assertions added for PR libstdc++/48101 were at class scope
and so were evaluated too eagerly, when it might not be possible to
determine whether the function objects are invocable with the key types.
The problematic cases are where the key type is not known to be
convertible to the argument type(s) of the function object until later,
after a type has been completed. Specifically, if the key type is a
pointer to a derived class and the function object's argument type is a
pointer to a base class, then the derived-to-base conversion is only
valid once the derived type is complete.

By moving the static assertions to the destructor they will only be
evaluated when the destructor is instantiated, at which point whether
the key type can be passed to the function object should be knowable.
The ideal place to do the checks would be only when the function objects
are actually invoked, but that would mean adding the checks in numerous
places, so the destructor is used instead.

The tests need to be adjusted because the "required from here" line is
now the location of the destructor, not the point of instantiation in
the test file. For the map and multimap tests which check two
specializations, the dg-error matching the assertion text matches both
cases. Also check the diagnostic output for the template arguments, to
ensure both specializations trigger the assertion.

	PR libstdc++/85965
	* include/bits/hashtable.h (_Hashtable): Move static assertions to
	destructor so they are not evaluated until the _Key type is complete.
	* include/bits/stl_tree.h (_Rb_tree): Likewise.
	* testsuite/23_containers/set/85965.cc: New test.
	* testsuite/23_containers/unordered_set/85965.cc: New test.
	* testsuite/23_containers/map/48101_neg.cc: Replace "here" errors
	with regexp matching the corresponding _Rb_tree specialization.
	* testsuite/23_containers/multimap/48101_neg.cc: Likewise.
	* testsuite/23_containers/multiset/48101_neg.cc: Remove "here" error.
	* testsuite/23_containers/set/48101_neg.cc: Likewise.
	* testsuite/23_containers/unordered_map/48101_neg.cc: Likewise.
	* testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise.
	* testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise.
	* testsuite/23_containers/unordered_set/48101_neg.cc: Likewise.

From-SVN: r269949
parent 0da83a16
2019-03-26 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/85965
* include/bits/hashtable.h (_Hashtable): Move static assertions to
destructor so they are not evaluated until the _Key type is complete.
* include/bits/stl_tree.h (_Rb_tree): Likewise.
* testsuite/23_containers/set/85965.cc: New test.
* testsuite/23_containers/unordered_set/85965.cc: New test.
* testsuite/23_containers/map/48101_neg.cc: Replace "here" errors
with regexp matching the corresponding _Rb_tree specialization.
* testsuite/23_containers/multimap/48101_neg.cc: Likewise.
* testsuite/23_containers/multiset/48101_neg.cc: Remove "here" error.
* testsuite/23_containers/set/48101_neg.cc: Likewise.
* testsuite/23_containers/unordered_map/48101_neg.cc: Likewise.
* testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise.
* testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise.
* testsuite/23_containers/unordered_set/48101_neg.cc: Likewise.
2019-03-26 Ville Voutilainen <ville.voutilainen@gmail.com> 2019-03-26 Ville Voutilainen <ville.voutilainen@gmail.com>
PR libstdc++/89825 PR libstdc++/89825
......
...@@ -192,11 +192,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -192,11 +192,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static_assert(is_same<typename _Alloc::value_type, _Value>{}, static_assert(is_same<typename _Alloc::value_type, _Value>{},
"unordered container must have the same value_type as its allocator"); "unordered container must have the same value_type as its allocator");
#endif #endif
static_assert(__is_invocable<const _H1&, const _Key&>{},
"hash function must be invocable with an argument of key type");
static_assert(__is_invocable<const _Equal&, const _Key&, const _Key&>{},
"key equality predicate must be invocable with two arguments of "
"key type");
using __traits_type = _Traits; using __traits_type = _Traits;
using __hash_cached = typename __traits_type::__hash_cached; using __hash_cached = typename __traits_type::__hash_cached;
...@@ -1356,6 +1351,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1356,6 +1351,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ {
clear(); clear();
_M_deallocate_buckets(); _M_deallocate_buckets();
static_assert(__is_invocable<const _H1&, const _Key&>{},
"hash function must be invocable with an argument of key type");
static_assert(__is_invocable<const _Equal&, const _Key&, const _Key&>{},
"key equality predicate must be invocable with two arguments of "
"key type");
} }
template<typename _Key, typename _Value, template<typename _Key, typename _Value,
......
...@@ -440,17 +440,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -440,17 +440,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits; typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
#if __cplusplus >= 201103L
static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
"comparison object must be invocable with two arguments of key type");
# if __cplusplus >= 201703L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2542. Missing const requirements for associative containers
static_assert(is_invocable_v<const _Compare&, const _Key&, const _Key&>,
"comparison object must be invocable as const");
# endif // C++17
#endif // C++11
protected: protected:
typedef _Rb_tree_node_base* _Base_ptr; typedef _Rb_tree_node_base* _Base_ptr;
typedef const _Rb_tree_node_base* _Const_Base_ptr; typedef const _Rb_tree_node_base* _Const_Base_ptr;
...@@ -985,7 +974,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -985,7 +974,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#endif #endif
~_Rb_tree() _GLIBCXX_NOEXCEPT ~_Rb_tree() _GLIBCXX_NOEXCEPT
{ _M_erase(_M_begin()); } {
_M_erase(_M_begin());
#if __cplusplus >= 201103L
static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
"comparison object must be invocable "
"with two arguments of key type");
# if __cplusplus >= 201703L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2542. Missing const requirements for associative containers
static_assert(is_invocable_v<const _Compare&, const _Key&, const _Key&>,
"comparison object must be invocable as const");
# endif // C++17
#endif // C++11
}
_Rb_tree& _Rb_tree&
operator=(const _Rb_tree& __x); operator=(const _Rb_tree& __x);
......
...@@ -23,8 +23,10 @@ ...@@ -23,8 +23,10 @@
void void
test01() test01()
{ {
std::map<int, int, std::less<int*>> c; // { dg-error "here" } std::map<int, int, std::less<int*>> c;
std::map<int, int, std::allocator<int>> c2; // { dg-error "here" } std::map<int, int, std::allocator<int>> c2;
} }
// { dg-error "_Compare = std::less<int.>" "" { target *-*-* } 0 }
// { dg-error "_Compare = std::allocator<int>" "" { target *-*-* } 0 }
// { dg-error "comparison object must be invocable" "" { target *-*-* } 0 } // { dg-error "comparison object must be invocable" "" { target *-*-* } 0 }
...@@ -23,8 +23,10 @@ ...@@ -23,8 +23,10 @@
void void
test01() test01()
{ {
std::multimap<int, int, std::less<int*>> c; // { dg-error "here" } std::multimap<int, int, std::less<int*>> c;
std::multimap<int, int, std::allocator<int>> c2; // { dg-error "here" } std::multimap<int, int, std::allocator<int>> c2;
} }
// { dg-error "_Compare = std::less<int.>" "" { target *-*-* } 0 }
// { dg-error "_Compare = std::allocator<int>" "" { target *-*-* } 0 }
// { dg-error "comparison object must be invocable" "" { target *-*-* } 0 } // { dg-error "comparison object must be invocable" "" { target *-*-* } 0 }
...@@ -23,7 +23,7 @@ void ...@@ -23,7 +23,7 @@ void
test01() test01()
{ {
std::multiset<const int> c; // { dg-error "here" } std::multiset<const int> c; // { dg-error "here" }
std::multiset<int, std::less<long*>> c2; // { dg-error "here" } std::multiset<int, std::less<long*>> c2;
} }
// { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 } // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
......
...@@ -23,7 +23,7 @@ void ...@@ -23,7 +23,7 @@ void
test01() test01()
{ {
std::set<const int> c; // { dg-error "here" } std::set<const int> c; // { dg-error "here" }
std::set<int, std::less<long*>> c2; // { dg-error "here" } std::set<int, std::less<long*>> c2;
} }
// { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 } // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
......
// Copyright (C) 2019 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 copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-do compile { target c++11 } }
#include <set>
struct Base { };
struct Derived; // derives from Base, but incomplete at this point
struct Foo
{
// PR libstdc++/85965
std::set<Derived*, std::less<Base*>> s;
};
...@@ -23,7 +23,7 @@ void ...@@ -23,7 +23,7 @@ void
test01() test01()
{ {
using namespace std; using namespace std;
unordered_map<int, int, equal_to<int>, hash<int>> c2; // { dg-error "here" } unordered_map<int, int, equal_to<int>, hash<int>> c2;
} }
// { dg-error "hash function must be invocable" "" { target *-*-* } 0 } // { dg-error "hash function must be invocable" "" { target *-*-* } 0 }
......
...@@ -23,7 +23,7 @@ void ...@@ -23,7 +23,7 @@ void
test01() test01()
{ {
using namespace std; using namespace std;
unordered_multimap<int, int, equal_to<int>, hash<int>> c2; // { dg-error "here" } unordered_multimap<int, int, equal_to<int>, hash<int>> c2;
} }
// { dg-error "hash function must be invocable" "" { target *-*-* } 0 } // { dg-error "hash function must be invocable" "" { target *-*-* } 0 }
......
...@@ -24,7 +24,7 @@ test01() ...@@ -24,7 +24,7 @@ test01()
{ {
using namespace std; using namespace std;
unordered_multiset<const int, hash<int>> c; // { dg-error "here" } unordered_multiset<const int, hash<int>> c; // { dg-error "here" }
unordered_multiset<int, equal_to<int>, hash<int>> c2; // { dg-error "here" } unordered_multiset<int, equal_to<int>, hash<int>> c2;
} }
// { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 } // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
......
...@@ -24,7 +24,7 @@ test01() ...@@ -24,7 +24,7 @@ test01()
{ {
using namespace std; using namespace std;
unordered_set<const int, hash<int>> c; // { dg-error "here" } unordered_set<const int, hash<int>> c; // { dg-error "here" }
unordered_set<int, equal_to<int>, hash<int>> c2; // { dg-error "here" } unordered_set<int, equal_to<int>, hash<int>> c2;
} }
// { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 } // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
......
// Copyright (C) 2019 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 copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-do compile { target c++11 } }
#include <unordered_set>
struct Base { };
struct Derived; // derives from Base, but incomplete at this point
struct Foo
{
// PR libstdc++/85965
std::unordered_set<Derived*, std::equal_to<Base*>, std::hash<Base*>> u;
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment