On 30/05/19 09:06 +0300, Antony Polukhin wrote:
чт, 9 мая 2019 г. в 00:10, Jonathan Wakely <jwak...@redhat.com>:
On 06/05/19 14:19 +0300, Antony Polukhin wrote:
>@@ -924,14 +984,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _Tp>
> struct is_default_constructible
> : public __is_default_constructible_safe<_Tp>::type
>- { };
>+ {
>+ static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
>+ "template argument must be a complete class or an unbounded array");
>+ };
>
> /// is_constructible
This "///" comment is for Doxygen, and should remain with the
is_constructible trait, not __is_constructible_impl.
Fixed that issue along with some other misplaced Doxygen comments.
Rebased the patch and removed some trailing white-spaces.
Thanks! Rebasing this was on my TODO list for today.
--
Best regards,
Antony Polukhin
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index b209460..c5f0672 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,27 @@
+2019-05-29 Antony Polukhin <antosh...@gmail.com>
+
+ PR libstdc++/71579
+ * include/std/type_traits: Add static_asserts to make sure that
+ type traits are not misused with an incomplete type.
This changelog could be more explicit. It doesn't have to list every
changed trait, but maybe something like:
* include/std/type_traits: Add static_asserts to make sure that
type traits are not misused with an incomplete type. Create
new base characteristics without assertions that can be reused
in other traits.
+ * testsuite/20_util/__is_complete_or_unbounded/memoization.cc:
+ New test.
+ * testsuite/20_util/__is_complete_or_unbounded/memoization_neg.cc:
+ Likewise.
+ * testsuite/20_util/__is_complete_or_unbounded/value.cc: Likewise.
I think I'd prefer not to have the double underscores in the directory
name here.
@@ -876,19 +935,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Tp>
struct is_nothrow_destructible
: public __is_nt_destructible_safe<_Tp>::type
+ {
+ static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
+ "template argument must be a complete class or an unbounded array");
+ };
+
+ template<typename _Tp, typename... _Args>
N.B. This 'template' keyword should only be indented two spaces,
however ...
+ struct __is_constructible_impl
+ : public __bool_constant<__is_constructible(_Tp, _Args...)>
{ };
I thought this could be an alias template instead of a class template:
template<typename _Tp, typename... _Args>
using __is_constructible_impl
= __bool_constant<__is_constructible(_Tp, _Args...)>;
But it causes failures in the 20_util/variable_templates_for_traits.cc
test. It looks like it instantiates some things too eagerly if it's an
alias template.
/// is_constructible
template<typename _Tp, typename... _Args>
struct is_constructible
- : public __bool_constant<__is_constructible(_Tp, _Args...)>
+ : public __is_constructible_impl<_Tp, _Args...>
+ {
+ static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
+ "template argument must be a complete class or an unbounded array");
+ };
+
+ template<typename _Tp>
+ struct __is_default_constructible_impl
+ : public __is_constructible_impl<_Tp>::type
{ };
Do we need this new __is_default_constructible_impl type?
Could we just use __is_constructible_impl<_Tp> instead, to avoid an
extra step (and extra template instantiations)?
@@ -946,12 +1030,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: public __is_nt_default_constructible_atom<_Tp>
{ };
+ template<typename _Tp>
Indentation again :-)
+ struct __is_nothrow_default_constructible_impl
+ : public __and_<__is_default_constructible_impl<_Tp>,
+ __is_nt_default_constructible_impl<_Tp>>
+ { };
This one can be an alias template:
template<typename _Tp>
using __is_nothrow_default_constructible_impl
= typename __and_<__is_constructible_impl<_Tp>,
__is_nt_default_constructible_impl<_Tp>>::type;
+ template<typename _Tp, typename _Up>
+ struct __is_nothrow_assignable_impl
+ : public __and_<__bool_constant<__is_assignable(_Tp, _Up)>,
+ __is_nt_assignable_impl<_Tp, _Up>>
+ { };
I wanted this one to be an alias template:
template<typename _Tp, typename _Up>
using __is_nothrow_assignable_impl
= __and_<__bool_constant<__is_assignable(_Tp, _Up)>,
__is_nt_assignable_impl<_Tp, _Up>>;
But that also causes the same test to fail. I think it would work if
we moved the __is_assignable intrinsic into a new __is_assignable_impl
class template, but then we'd be adding a new class template just to
get rid of another one. That doesn't seem sensible.
I've attached a relative diff, to be applied on top of yours, with my
suggested tweaks. Do you see any issues with it?
(If you're happy with those tweaks I can go ahead and apply this,
there's no need for an updated patch from you).
commit 732d9c1c9634900437f560538305a6ffde5d6e8d
Author: Jonathan Wakely <jwak...@redhat.com>
Date: Thu May 30 17:30:45 2019 +0100
simplify
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index b481106b30e..78a113af415 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -940,9 +940,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
"template argument must be a complete class or an unbounded array");
};
- template<typename _Tp, typename... _Args>
+ template<typename _Tp, typename... _Args>
struct __is_constructible_impl
- : public __bool_constant<__is_constructible(_Tp, _Args...)>
+ : public __bool_constant<__is_constructible(_Tp, _Args...)>
{ };
/// is_constructible
@@ -954,15 +954,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
"template argument must be a complete class or an unbounded array");
};
- template<typename _Tp>
- struct __is_default_constructible_impl
- : public __is_constructible_impl<_Tp>::type
- { };
-
/// is_default_constructible
template<typename _Tp>
struct is_default_constructible
- : public __is_default_constructible_impl<_Tp>::type
+ : public __is_constructible_impl<_Tp>::type
{
static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
"template argument must be a complete class or an unbounded array");
@@ -1030,16 +1025,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: public __is_nt_default_constructible_atom<_Tp>
{ };
- template<typename _Tp>
- struct __is_nothrow_default_constructible_impl
- : public __and_<__is_default_constructible_impl<_Tp>,
- __is_nt_default_constructible_impl<_Tp>>
- { };
+ template<typename _Tp>
+ using __is_nothrow_default_constructible_impl
+ = __and_<__is_constructible_impl<_Tp>,
+ __is_nt_default_constructible_impl<_Tp>>;
/// is_nothrow_default_constructible
template<typename _Tp>
struct is_nothrow_default_constructible
- : public __is_nothrow_default_constructible_impl<_Tp>
+ : public __is_nothrow_default_constructible_impl<_Tp>::type
{
static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
"template argument must be a complete class or an unbounded array");
@@ -1070,7 +1064,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// is_nothrow_constructible
template<typename _Tp, typename... _Args>
struct is_nothrow_constructible
- : public __is_nothrow_constructible_impl<_Tp, _Args...>
+ : public __is_nothrow_constructible_impl<_Tp, _Args...>::type
{
static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
"template argument must be a complete class or an unbounded array");
@@ -1091,7 +1085,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// is_nothrow_copy_constructible
template<typename _Tp>
struct is_nothrow_copy_constructible
- : public __is_nothrow_copy_constructible_impl<_Tp>
+ : public __is_nothrow_copy_constructible_impl<_Tp>::type
{
static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
"template argument must be a complete class or an unbounded array");
@@ -1112,7 +1106,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// is_nothrow_move_constructible
template<typename _Tp>
struct is_nothrow_move_constructible
- : public __is_nothrow_move_constructible_impl<_Tp>
+ : public __is_nothrow_move_constructible_impl<_Tp>::type
{
static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
"template argument must be a complete class or an unbounded array");
@@ -1121,7 +1115,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// is_assignable
template<typename _Tp, typename _Up>
struct is_assignable
- : public __bool_constant<__is_assignable(_Tp, _Up)>
+ : public __bool_constant<__is_assignable(_Tp, _Up)>
{
static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
"template argument must be a complete class or an unbounded array");
@@ -1142,7 +1136,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// is_copy_assignable
template<typename _Tp>
struct is_copy_assignable
- : public __is_copy_assignable_impl<_Tp>
+ : public __is_copy_assignable_impl<_Tp>::type
{
static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
"template argument must be a complete class or an unbounded array");
@@ -1163,7 +1157,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// is_move_assignable
template<typename _Tp>
struct is_move_assignable
- : public __is_move_assignable_impl<_Tp>
+ : public __is_move_assignable_impl<_Tp>::type
{
static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
"template argument must be a complete class or an unbounded array");
@@ -1174,7 +1168,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: public integral_constant<bool, noexcept(declval<_Tp>() = declval<_Up>())>
{ };
- template<typename _Tp, typename _Up>
+ template<typename _Tp, typename _Up>
struct __is_nothrow_assignable_impl
: public __and_<__bool_constant<__is_assignable(_Tp, _Up)>,
__is_nt_assignable_impl<_Tp, _Up>>
@@ -1275,7 +1269,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template <typename _Tp>
struct __is_implicitly_default_constructible
- : public __and_<__is_default_constructible_impl<_Tp>,
+ : public __and_<__is_constructible_impl<_Tp>,
__is_implicitly_default_constructible_safe<_Tp>>
{ };
@@ -1492,7 +1486,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: public __is_convertible_helper<_From, _To>::type
{ };
- template<typename _From, typename _To,
+ template<typename _From, typename _To,
bool = __or_<is_void<_From>, is_function<_To>,
is_array<_To>>::value>
struct __is_nt_convertible_helper