Message ID | CAKqmYPZ8o8TX5EOT=4Lxy5_uC4BtexLMtm3nmYGNsFybQ3Mb7g@mail.gmail.com |
---|---|
State | New |
Headers | show |
Series | PR libstdc++/71579 assert that type traits are not misused with an incomplete type | expand |
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. >- template<typename _Tp, typename... _Args> >- struct is_constructible >+ template<typename _Tp, typename... _Args> >+ struct __is_constructible_impl > : public __bool_constant<__is_constructible(_Tp, _Args...)> > { }; > >+ template<typename _Tp, typename... _Args> >+ struct is_constructible >+ : 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, bool = __is_referenceable<_Tp>::value> > struct __is_copy_constructible_impl; > The rest looks reasonable, but I'll finish reviewing it ASAP, when I have more time.
чт, 9 мая 2019 г. в 00:10, Jonathan Wakely <jwakely@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.
On 30/05/19 09:06 +0300, Antony Polukhin wrote: >чт, 9 мая 2019 г. в 00:10, Jonathan Wakely <jwakely@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 <antoshkka@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).
On Thu, May 30, 2019, 19:38 Jonathan Wakely <jwakely@redhat.com> wrote: <...> > 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). > Looks good. Please apply! >
On 31/05/19 08:58 +0300, Antony Polukhin wrote: >On Thu, May 30, 2019, 19:38 Jonathan Wakely <jwakely@redhat.com> wrote: ><...> > >> 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). >> > >Looks good. Please apply! Here's what I've tested and committed to trunk. I decided to add a more detailed changelog too.
On 31/05/19 11:35 +0100, Jonathan Wakely wrote: >On 31/05/19 08:58 +0300, Antony Polukhin wrote: >>On Thu, May 30, 2019, 19:38 Jonathan Wakely <jwakely@redhat.com> wrote: >><...> >> >>>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). >>> >> >>Looks good. Please apply! > >Here's what I've tested and committed to trunk. > >I decided to add a more detailed changelog too. I'm removing some of these assertions again, because they are either reundant or wrong. Tested x86_64-linux, committed to trunk.
чт, 6 июн. 2019 г. в 15:19, Jonathan Wakely <jwakely@redhat.com>: > I'm removing some of these assertions again, because they are either > reundant or wrong. Thanks for cleaning up! In attachment there is an additional patch for type traits hardening. Things that still remain unasserted are type traits with variadic template arguments. I have to came up with a proper solution for providing a useful and lightweight diagnostics.
On Thu, 20 Jun 2019 at 20:49, Antony Polukhin <antoshkka@gmail.com> wrote: > > чт, 6 июн. 2019 г. в 15:19, Jonathan Wakely <jwakely@redhat.com>: > > I'm removing some of these assertions again, because they are either > > reundant or wrong. > > Thanks for cleaning up! > > > In attachment there is an additional patch for type traits hardening. > > Things that still remain unasserted are type traits with variadic > template arguments. I have to came up with a proper solution for > providing a useful and lightweight diagnostics. I see a public __bool_constant<__is_trivially_assignable(_Tp, _Up)> in this patch, followed by a trait-body that static_asserts. In such cases, I think we want to a) be really careful about duplicating compiler diagnostics with library ones b) look at the compiler diagnostics, and if they are lacking, improve them. ...because that's what Jonathan's cleanup was really about. In the test modifications of __is_trivially_assignable, this looks bloody suspicious: +// { dg-prune-output "invalid use of incomplete type" } +// { dg-prune-output "must be a complete" } No. Don't merge. We are not replacing diagnostics A with diagnostics B, we are ignoring existing diagnostics and adding more. Which is exactly what Jonathan's cleanup avoided.
чт, 20 июн. 2019 г. в 20:57, Ville Voutilainen <ville.voutilainen@gmail.com>: > > On Thu, 20 Jun 2019 at 20:49, Antony Polukhin <antoshkka@gmail.com> wrote: > > > > чт, 6 июн. 2019 г. в 15:19, Jonathan Wakely <jwakely@redhat.com>: > > > I'm removing some of these assertions again, because they are either > > > reundant or wrong. > > > > Thanks for cleaning up! > > > > > > In attachment there is an additional patch for type traits hardening. > > > > Things that still remain unasserted are type traits with variadic > > template arguments. I have to came up with a proper solution for > > providing a useful and lightweight diagnostics. > > I see a > public __bool_constant<__is_trivially_assignable(_Tp, _Up)> > in this patch, followed by a trait-body that static_asserts. In such > cases, I think we want > to > a) be really careful about duplicating compiler diagnostics with library ones > b) look at the compiler diagnostics, and if they are lacking, improve them. > > ...because that's what Jonathan's cleanup was really about. > In the test modifications of __is_trivially_assignable, this looks bloody > suspicious: > > +// { dg-prune-output "invalid use of incomplete type" } > +// { dg-prune-output "must be a complete" } > > No. Don't merge. We are not replacing diagnostics A with diagnostics > B, we are ignoring existing > diagnostics and adding more. Which is exactly what Jonathan's cleanup avoided. Thanks for the review and clarifications. I'll fix the patch.
Fixed patch for type traits hardening Changelog 2020-08-12 Antony Polukhin <antoshkka@gmail.com> PR libstdc/71579 * include/std/type_traits (invoke_result, is_nothrow_invocable_r) Add static_asserts to make sure that the argument of the type trait is not misused with incomplete types. (is_swappable_with, is_nothrow_swappable_with): Add static_asserts to make sure that the first and second arguments of the type trait are not misused with incomplete types. * testsuite/20_util/invoke_result/incomplete_neg.cc: New test. * testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc: New test. * testsuite/20_util/is_nothrow_swappable/incomplete_neg.cc: New test. * testsuite/20_util/is_nothrow_swappable_with/incomplete_neg.cc: New test. * testsuite/20_util/is_swappable_with/incomplete_neg.cc: New test.
On 12/08/20 11:19 +0300, Antony Polukhin via Libstdc++ wrote: >Fixed patch for type traits hardening > >Changelog > >2020-08-12 Antony Polukhin <antoshkka@gmail.com> > > PR libstdc/71579 > * include/std/type_traits (invoke_result, is_nothrow_invocable_r) > Add static_asserts to make sure that the argument of the type > trait is not misused with incomplete types. > (is_swappable_with, is_nothrow_swappable_with): Add static_asserts > to make sure that the first and second arguments of the type trait > are not misused with incomplete types. > * testsuite/20_util/invoke_result/incomplete_neg.cc: New test. > * testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc: New test. > * testsuite/20_util/is_nothrow_swappable/incomplete_neg.cc: New test. > * testsuite/20_util/is_nothrow_swappable_with/incomplete_neg.cc: New > test. > * testsuite/20_util/is_swappable_with/incomplete_neg.cc: New test. Thanks, pushed to master. Do we also want to check (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>{}) && ...) for invoke_result and the is_invocable traits? We can use a fold expression there, because those traits are not defined before C++17.
ср, 19 авг. 2020 г. в 14:29, Jonathan Wakely <jwakely@redhat.com>: <...> > Do we also want to check > (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>{}) && ...) > for invoke_result and the is_invocable traits? > > We can use a fold expression there, because those traits are not > defined before C++17. Good idea. I'll try.
ср, 19 авг. 2020 г. в 14:29, Jonathan Wakely <jwakely@redhat.com>: <...> > Do we also want to check > (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>{}) && ...) > for invoke_result and the is_invocable traits? Done. Changelog: 2020-08-20 Antony Polukhin <antoshkka@gmail.com> PR libstdc/71579 * include/std/type_traits (invoke_result, is_invocable, is_invocable_r) (is_nothrow_invocable, is_nothrow_invocable_r): Add static_asserts to make sure that the arguments of the type traits are not misused with incomplete types.` * testsuite/20_util/invoke_result/incomplete_args_neg.cc: New test. * testsuite/20_util/is_invocable/incomplete_args_neg.cc: New test. * testsuite/20_util/is_invocable/incomplete_neg.cc: New test. * testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc: New test. * testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc: Check for error on incomplete response type usage in trait.
Looks like the last patch was not applied. Do I have to change something in it?
On 24/09/20 10:15 +0300, Antony Polukhin via Libstdc++ wrote: >Looks like the last patch was not applied. Do I have to change something in >it? No, it just hasn't been reviewed yet.
On 20/08/20 18:31 +0300, Antony Polukhin via Libstdc++ wrote: >ср, 19 авг. 2020 г. в 14:29, Jonathan Wakely <jwakely@redhat.com>: ><...> >> Do we also want to check >> (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>{}) && ...) >> for invoke_result and the is_invocable traits? > >Done. > >Changelog: > >2020-08-20 Antony Polukhin <antoshkka@gmail.com> > > PR libstdc/71579 > * include/std/type_traits (invoke_result, is_invocable, is_invocable_r) > (is_nothrow_invocable, is_nothrow_invocable_r): Add static_asserts > to make sure that the arguments of the type traits are not misused > with incomplete types.` > * testsuite/20_util/invoke_result/incomplete_args_neg.cc: New test. > * testsuite/20_util/is_invocable/incomplete_args_neg.cc: New test. > * testsuite/20_util/is_invocable/incomplete_neg.cc: New test. > * testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc: New test. > * testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc: Check for > error on incomplete response type usage in trait. Committed with some tweaks to the static assert messages to say: "each argument type must be a complete class or an unbounded array" Thanks!
Final bits for libstdc/71579 std::common_type assertions attempt to give a proper 'required from here' hint for user code, do not bring many changes to the implementation and check all the template parameters for completeness. In some cases the type could be checked for completeness more than once. This seems to be unsolvable due to the fact that std::common_type could be specialized by the user, so we have to call std::common_type recursively, potentially repeating the check for the first type. std::common_reference assertions make sure that we detect incomplete types even if the user specialized the std::basic_common_reference. Changelog: 2020-11-12 Antony Polukhin <antoshkka@gmail.com> PR libstdc/71579 * include/std/type_traits (is_convertible, is_nothrow_convertible) (common_type, common_reference): Add static_asserts to make sure that the arguments of the type traits are not misused with incomplete types. * testsuite/20_util/common_reference/incomplete_basic_common_neg.cc: New test. * testsuite/20_util/common_reference/incomplete_neg.cc: New test. * testsuite/20_util/common_type/incomplete_neg.cc: New test. * testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc: Remove SFINAE tests on incomplete types. * testsuite/20_util/is_convertible/incomplete_neg.cc: New test. * testsuite/20_util/is_nothrow_convertible/incomplete_neg.cc: New test. -- Best regards, Antony Polukhin diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index 34e068b..00fa7f5 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -1406,12 +1406,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _From, typename _To> struct is_convertible : public __is_convertible_helper<_From, _To>::type - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_From>{}), + "first template argument must be a complete class or an unbounded array"); + static_assert(std::__is_complete_or_unbounded(__type_identity<_To>{}), + "second template argument must be a complete class or an unbounded array"); + }; // helper trait for unique_ptr<T[]>, shared_ptr<T[]>, and span<T, N> template<typename _ToElementType, typename _FromElementType> using __is_array_convertible - = is_convertible<_FromElementType(*)[], _ToElementType(*)[]>; + = typename __is_convertible_helper< + _FromElementType(*)[], _ToElementType(*)[]>::type; template<typename _From, typename _To, bool = __or_<is_void<_From>, is_function<_To>, @@ -1454,7 +1460,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _From, typename _To> struct is_nothrow_convertible : public __is_nt_convertible_helper<_From, _To>::type - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_From>{}), + "first template argument must be a complete class or an unbounded array"); + static_assert(std::__is_complete_or_unbounded(__type_identity<_To>{}), + "second template argument must be a complete class or an unbounded array"); + }; /// is_nothrow_convertible_v template<typename _From, typename _To> @@ -2239,7 +2250,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp1, typename _Tp2> struct common_type<_Tp1, _Tp2> : public __common_type_impl<_Tp1, _Tp2>::type - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp1>{}), + "each argument type must be a complete class or an unbounded array"); + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp2>{}), + "each argument type must be a complete class or an unbounded array"); + }; template<typename...> struct __common_type_pack @@ -2253,7 +2269,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct common_type<_Tp1, _Tp2, _Rp...> : public __common_type_fold<common_type<_Tp1, _Tp2>, __common_type_pack<_Rp...>> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp1>{}), + "first argument type must be a complete class or an unbounded array"); + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp2>{}), + "second argument type must be a complete class or an unbounded array"); +#ifdef __cpp_fold_expressions + static_assert((std::__is_complete_or_unbounded( + __type_identity<_Rp>{}) && ...), + "each argument type must be a complete class or an unbounded array"); +#endif + }; // Let C denote the same type, if any, as common_type_t<T1, T2>. // If there is such a type C, type shall denote the same type, if any, @@ -3315,9 +3341,10 @@ template <typename _From, typename _To> // If A and B are both rvalue reference types, ... template<typename _Xp, typename _Yp> - struct __common_ref_impl<_Xp&&, _Yp&&, - _Require<is_convertible<_Xp&&, __common_ref_C<_Xp, _Yp>>, - is_convertible<_Yp&&, __common_ref_C<_Xp, _Yp>>>> + struct __common_ref_impl<_Xp&&, _Yp&&, _Require< + typename __is_convertible_helper<_Xp&&, __common_ref_C<_Xp, _Yp>>::type, + typename __is_convertible_helper<_Yp&&, __common_ref_C<_Xp, _Yp>>::type + >> { using type = __common_ref_C<_Xp, _Yp>; }; // let D be COMMON-REF(const X&, Y&) @@ -3326,8 +3353,9 @@ template <typename _From, typename _To> // If A is an rvalue reference and B is an lvalue reference, ... template<typename _Xp, typename _Yp> - struct __common_ref_impl<_Xp&&, _Yp&, - _Require<is_convertible<_Xp&&, __common_ref_D<_Xp, _Yp>>>> + struct __common_ref_impl<_Xp&&, _Yp&, _Require< + typename __is_convertible_helper<_Xp&&, __common_ref_D<_Xp, _Yp>>::type + >> { using type = __common_ref_D<_Xp, _Yp>; }; // If A is an lvalue reference and B is an rvalue reference, ... @@ -3374,7 +3402,12 @@ template <typename _From, typename _To> // If sizeof...(T) is one ... template<typename _Tp0> struct common_reference<_Tp0> - { using type = _Tp0; }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp0>{}), + "each argument type must be a complete class or an unbounded array"); + + using type = _Tp0; + }; template<typename _Tp1, typename _Tp2, int _Bullet = 1, typename = void> struct __common_reference_impl @@ -3385,7 +3418,12 @@ template <typename _From, typename _To> template<typename _Tp1, typename _Tp2> struct common_reference<_Tp1, _Tp2> : __common_reference_impl<_Tp1, _Tp2> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp1>{}), + "each argument type must be a complete class or an unbounded array"); + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp2>{}), + "each argument type must be a complete class or an unbounded array"); + }; // If T1 and T2 are reference types and COMMON-REF(T1, T2) is well-formed, ... template<typename _Tp1, typename _Tp2> diff --git a/libstdc++-v3/testsuite/20_util/common_reference/incomplete_basic_common_neg.cc b/libstdc++-v3/testsuite/20_util/common_reference/incomplete_basic_common_neg.cc new file mode 100644 index 0000000..9724ad0 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/common_reference/incomplete_basic_common_neg.cc @@ -0,0 +1,38 @@ +// { dg-do compile { target c++20 } } + +// Copyright (C) 2020 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; + +template <> +struct std::basic_common_reference<X, X> { + using type = int; +}; + +void test01() +{ + std::common_reference<X>(); // { dg-error "required from here" } + std::common_reference<X, X>(); // { dg-error "required from here" } + std::common_reference<X, X, X>(); // { dg-error "required from here" } + std::common_reference<X, X, X, X>(); // { dg-error "required from here" } + std::common_reference<X, X, X, X, X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/common_reference/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/common_reference/incomplete_neg.cc new file mode 100644 index 0000000..c151d4a --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/common_reference/incomplete_neg.cc @@ -0,0 +1,58 @@ +// { dg-do compile { target c++20 } } + +// Copyright (C) 2020 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; +class Y {}; + +void test01() +{ + std::common_reference<X>(); // { dg-error "required from here" } + std::common_reference<X, int>(); // { dg-error "required from here" } + std::common_reference<X, int, int>(); // { dg-error "required from here" } + std::common_reference<X, int, int, int>(); // { dg-error "required from here" } + std::common_reference<X, int, int, int, int>(); // { dg-error "required from here" } + + std::common_reference<int, X>(); // { dg-error "required from here" } + std::common_reference<int, X, int>(); // { dg-error "required from here" } + std::common_reference<int, X, int, int>(); // { dg-error "required from here" } + std::common_reference<int, X, int, int, int>(); // { dg-error "required from here" } + + std::common_reference<int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, X, int>(); // { dg-error "required from here" } + std::common_reference<int, int, X, int, int>(); // { dg-error "required from here" } + + std::common_reference<int, int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, int, X, int>(); // { dg-error "required from here" } + + std::common_reference<int, int, int, int, X>(); // { dg-error "required from here" } + + std::common_reference<X, int, int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, X, int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, X, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, int, X, X>(); // { dg-error "required from here" } + + std::common_reference<Y, int, int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, Y, int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, Y, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, int, Y, X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/common_type/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/common_type/incomplete_neg.cc new file mode 100644 index 0000000..6c2641d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/common_type/incomplete_neg.cc @@ -0,0 +1,59 @@ +// { dg-do compile { target c++17 } } + +// Copyright (C) 2020 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; +class Y {}; + +// Some of the following asserts require __cpp_fold_expressions to trigger +void test01() +{ + std::common_type<X>(); // { dg-error "required from here" } + std::common_type<X, int>(); // { dg-error "required from here" } + std::common_type<X, int, int>(); // { dg-error "required from here" } + std::common_type<X, int, int, int>(); // { dg-error "required from here" } + std::common_type<X, int, int, int, int>(); // { dg-error "required from here" } + + std::common_type<int, X>(); // { dg-error "required from here" } + std::common_type<int, X, int>(); // { dg-error "required from here" } + std::common_type<int, X, int, int>(); // { dg-error "required from here" } + std::common_type<int, X, int, int, int>(); // { dg-error "required from here" } + + std::common_type<int, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, X, int>(); // { dg-error "required from here" } + std::common_type<int, int, X, int, int>(); // { dg-error "required from here" } + + std::common_type<int, int, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, int, X, int>(); // { dg-error "required from here" } + + std::common_type<int, int, int, int, X>(); // { dg-error "required from here" } + + std::common_type<X, int, int, int, X>(); // { dg-error "required from here" } + std::common_type<int, X, int, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, X, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, int, X, X>(); // { dg-error "required from here" } + + std::common_type<Y, int, int, int, X>(); // { dg-error "required from here" } + std::common_type<int, Y, int, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, Y, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, int, Y, X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc b/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc index 04eef50..a52f186 100644 --- a/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc +++ b/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc @@ -89,7 +89,7 @@ enum class ScEn; enum UnscEn : int; -struct Ukn; + union U { @@ -240,7 +240,7 @@ static_assert(is_type<std::common_type<decltype(nullptr), int (B::*)() const>, static_assert(is_type<std::common_type<decltype(nullptr), const int B::*>, const int B::*>(), ""); static_assert(is_type<std::common_type<Abstract&, Abstract&>, Abstract>(), ""); -static_assert(is_type<std::common_type<Ukn&, Ukn&>, Ukn>(), ""); + static_assert(is_type<std::common_type<ImplicitTo<B&>, B&>, B>(), ""); static_assert(is_type<std::common_type<ImplicitTo<B&>&, B&&>, B>(), ""); static_assert(is_type<std::common_type<UConv1, const Abstract*&>, @@ -254,11 +254,6 @@ static_assert(is_type<std::common_type<const Abstract&&, const Abstract&&>, Abstract>(), ""); static_assert(is_type<std::common_type<volatile Abstract&&, volatile Abstract&&>, Abstract>(), ""); -static_assert(is_type<std::common_type<Ukn&&, Ukn&&>, Ukn>(), ""); -static_assert(is_type<std::common_type<const Ukn&&, const Ukn&&>, - Ukn>(), ""); -static_assert(is_type<std::common_type<volatile Ukn&&, volatile Ukn&&>, - Ukn>(), ""); static_assert(is_type<std::common_type<X1, X2>, RX12>(), ""); static_assert(is_type<std::common_type<const X1, X2>, RX12>(), ""); @@ -280,13 +275,13 @@ static_assert(!has_type<std::common_type<U, U2>>(), ""); static_assert(!has_type<std::common_type<PrivateImplicitTo<int>, int>>(), ""); static_assert(!has_type<std::common_type<const PrivateImplicitTo<int>, int>>(), ""); -static_assert(!has_type<std::common_type<int, Ukn>>(), ""); + static_assert(!has_type<std::common_type<int, Abstract>>(), ""); -static_assert(!has_type<std::common_type<Ukn, Abstract>>(), ""); + static_assert(!has_type<std::common_type<int, void>>(), ""); static_assert(!has_type<std::common_type<int, const volatile void>>(), ""); static_assert(!has_type<std::common_type<Abstract, void>>(), ""); -static_assert(!has_type<std::common_type<Ukn, void>>(), ""); + static_assert(!has_type<std::common_type<int[4], void>>(), ""); static_assert(!has_type<std::common_type<ScEn, void>>(), ""); static_assert(!has_type<std::common_type<UnscEn, void>>(), ""); diff --git a/libstdc++-v3/testsuite/20_util/is_convertible/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_convertible/incomplete_neg.cc new file mode 100644 index 0000000..7fe7f8b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_convertible/incomplete_neg.cc @@ -0,0 +1,31 @@ +// { dg-do compile { target c++11 } } + +// Copyright (C) 2020 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; + +void test01() +{ + std::is_convertible<X, int>(); // { dg-error "required from here" } + std::is_convertible<int, X>(); // { dg-error "required from here" } + std::is_convertible<X, X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/incomplete_neg.cc new file mode 100644 index 0000000..9c1d2f4 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/incomplete_neg.cc @@ -0,0 +1,31 @@ +// { dg-do compile { target c++20 } } + +// Copyright (C) 2020 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; + +void test01() +{ + std::is_nothrow_convertible<X, int>(); // { dg-error "required from here" } + std::is_nothrow_convertible<int, X>(); // { dg-error "required from here" } + std::is_nothrow_convertible<X, X>(); // { dg-error "required from here" } +}
On Thu, Nov 12, 2020, 21:55 Antony Polukhin <antoshkka@gmail.com> wrote: > Final bits for libstdc/71579 > Gentle reminder on last patch >
Rebased the patch on current master. Please review. Changelog stays the same: New std::common_type assertions attempt to give a proper 'required from here' hint for user code, do not bring many changes to the implementation and check all the template parameters for completeness. In some cases the type could be checked for completeness more than once. This seems to be unsolvable due to the fact that std::common_type could be specialized by the user, so we have to call std::common_type recursively, potentially repeating the check for the first type. std::common_reference assertions make sure that we detect incomplete types even if the user specialized the std::basic_common_reference. 2020-11-12 Antony Polukhin <antoshkka@gmail.com> PR libstdc/71579 * include/std/type_traits (is_convertible, is_nothrow_convertible) (common_type, common_reference): Add static_asserts to make sure that the arguments of the type traits are not misused with incomplete types. * testsuite/20_util/common_reference/incomplete_basic_common_neg.cc: New test. * testsuite/20_util/common_reference/incomplete_neg.cc: New test. * testsuite/20_util/common_type/incomplete_neg.cc: New test. * testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc: Remove SFINAE tests on incomplete types. * testsuite/20_util/is_convertible/incomplete_neg.cc: New test. * testsuite/20_util/is_nothrow_convertible/incomplete_neg.cc: New test. пт, 8 янв. 2021 г. в 20:28, Antony Polukhin <antoshkka@gmail.com>: > > > On Thu, Nov 12, 2020, 21:55 Antony Polukhin <antoshkka@gmail.com> wrote: >> >> Final bits for libstdc/71579 > > > Gentle reminder on last patch
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 411eff1..3a0cf44 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,27 @@ +2019-05-06 Antony Polukhin <antoshkka@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. + * 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. + * testsuite/20_util/is_abstract/incomplete_neg.cc: Likewise. + * testsuite/20_util/is_aggregate/incomplete_neg.cc: Likewise. + * testsuite/20_util/is_class/value.cc: Likewise. + * testsuite/20_util/is_function/value.cc: Likewise. + * testsuite/20_util/is_move_constructible/incomplete_neg.cc: Likewise. + * testsuite/20_util/is_nothrow_move_assignable/incomplete_neg.cc: + Likewise. + * testsuite/20_util/is_polymorphic/incomplete_neg.cc: Likewise. + * testsuite/20_util/is_reference/value.cc: Likewise. + * testsuite/20_util/is_unbounded_array/value.cc: Likewise. + * testsuite/20_util/is_union/value.cc: Likewise. + * testsuite/20_util/is_void/value.cc: Likewise. + * testsuite/util/testsuite_tr1.h: Add incomplete union type. + 2019-05-06 François Dumont <fdumont@gcc.gnu.org> * python/libstdcxx/v6/printers.py (add_one_template_type_printer): diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index 1d14c75..6fdb534 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -91,6 +91,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<bool, typename, typename> struct conditional; + template <typename _Type> + struct __type_identity { + using type = _Type; + }; + template<typename...> struct __or_; @@ -177,6 +182,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif // C++17 + // Forward declarations + template<typename> + struct is_reference; + template<typename> + struct is_function; + template<typename> + struct is_void; + template<typename> + struct __is_array_unknown_bounds; + + // Helper functions that return false_type for incomplete classes, + // incomplete unions and arrays of known bound from those. + + template <typename _T, size_t = sizeof(_T)> + constexpr true_type __is_complete_or_unbounded(__type_identity<_T>) + { return {}; } + + template <typename _TypeIdentity, + typename _NestedType = typename _TypeIdentity::type> + constexpr typename __or_< + is_reference<_NestedType>, + is_function<_NestedType>, + is_void<_NestedType>, + __is_array_unknown_bounds<_NestedType> + >::type __is_complete_or_unbounded(_TypeIdentity) + { return {}; } + // For several sfinae-friendly trait implementations we transport both the // result information (as the member type) and the failure information (no // member type). This is very similar to std::enable_if, but we cannot use @@ -399,9 +431,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : public true_type { }; template<typename> - struct is_function; - - template<typename> struct __is_member_object_pointer_helper : public false_type { }; @@ -671,44 +700,65 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct is_trivial : public integral_constant<bool, __is_trivial(_Tp)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; // is_trivially_copyable template<typename _Tp> struct is_trivially_copyable : public integral_constant<bool, __is_trivially_copyable(_Tp)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_standard_layout template<typename _Tp> struct is_standard_layout : public integral_constant<bool, __is_standard_layout(_Tp)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_pod // Could use is_standard_layout && is_trivial instead of the builtin. template<typename _Tp> struct is_pod : public integral_constant<bool, __is_pod(_Tp)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_literal_type template<typename _Tp> struct is_literal_type : public integral_constant<bool, __is_literal_type(_Tp)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_empty template<typename _Tp> struct is_empty : public integral_constant<bool, __is_empty(_Tp)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_polymorphic template<typename _Tp> struct is_polymorphic : public integral_constant<bool, __is_polymorphic(_Tp)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; #if __cplusplus >= 201402L #define __cpp_lib_is_final 201402L @@ -716,14 +766,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct is_final : public integral_constant<bool, __is_final(_Tp)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; #endif /// is_abstract template<typename _Tp> struct is_abstract : public integral_constant<bool, __is_abstract(_Tp)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; template<typename _Tp, bool = is_arithmetic<_Tp>::value> @@ -828,7 +884,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct is_destructible : public __is_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"); + }; // is_nothrow_destructible requires that is_destructible is // satisfied as well. We realize that by mimicing the @@ -876,7 +935,10 @@ _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"); + }; struct __do_is_default_constructible_impl { @@ -889,10 +951,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_default_constructible_impl - : public __do_is_default_constructible_impl - { - typedef decltype(__test<_Tp>(0)) type; - }; + : public decltype(__do_is_default_constructible_impl::__test<_Tp>(0)) + { }; template<typename _Tp> struct __is_default_constructible_atom @@ -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 - template<typename _Tp, typename... _Args> - struct is_constructible + template<typename _Tp, typename... _Args> + struct __is_constructible_impl : public __bool_constant<__is_constructible(_Tp, _Args...)> { }; + template<typename _Tp, typename... _Args> + struct is_constructible + : 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, bool = __is_referenceable<_Tp>::value> struct __is_copy_constructible_impl; @@ -941,14 +1012,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_copy_constructible_impl<_Tp, true> - : public is_constructible<_Tp, const _Tp&> + : public __is_constructible_impl<_Tp, const _Tp&> { }; /// is_copy_constructible template<typename _Tp> struct is_copy_constructible : public __is_copy_constructible_impl<_Tp> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; template<typename _Tp, bool = __is_referenceable<_Tp>::value> struct __is_move_constructible_impl; @@ -959,14 +1033,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_move_constructible_impl<_Tp, true> - : public is_constructible<_Tp, _Tp&&> + : public __is_constructible_impl<_Tp, _Tp&&> { }; /// is_move_constructible template<typename _Tp> struct is_move_constructible : public __is_move_constructible_impl<_Tp> - { }; + { + 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_nt_default_constructible_atom @@ -989,12 +1066,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { }; /// is_nothrow_default_constructible - template<typename _Tp> - struct is_nothrow_default_constructible - : public __and_<is_default_constructible<_Tp>, + template<typename _Tp> + struct __is_nothrow_default_constructible_impl + : public __and_<__is_default_constructible_safe<_Tp>, __is_nt_default_constructible_impl<_Tp>> { }; + template<typename _Tp> + struct is_nothrow_default_constructible + : public __is_nothrow_default_constructible_impl<_Tp> + { + 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> struct __is_nt_constructible_impl : public integral_constant<bool, noexcept(_Tp(declval<_Args>()...))> @@ -1008,16 +1093,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_nt_constructible_impl<_Tp> - : public is_nothrow_default_constructible<_Tp> + : public __is_nothrow_default_constructible_impl<_Tp> { }; /// is_nothrow_constructible template<typename _Tp, typename... _Args> - struct is_nothrow_constructible - : public __and_<is_constructible<_Tp, _Args...>, + struct __is_nothrow_constructible_impl + : public __and_<__is_constructible_impl<_Tp, _Args...>, __is_nt_constructible_impl<_Tp, _Args...>> { }; + template<typename _Tp, typename... _Args> + struct is_nothrow_constructible + : public __is_nothrow_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, bool = __is_referenceable<_Tp>::value> struct __is_nothrow_copy_constructible_impl; @@ -1027,14 +1120,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_nothrow_copy_constructible_impl<_Tp, true> - : public is_nothrow_constructible<_Tp, const _Tp&> + : public __is_nothrow_constructible_impl<_Tp, const _Tp&> { }; /// is_nothrow_copy_constructible template<typename _Tp> struct is_nothrow_copy_constructible : public __is_nothrow_copy_constructible_impl<_Tp> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; template<typename _Tp, bool = __is_referenceable<_Tp>::value> struct __is_nothrow_move_constructible_impl; @@ -1045,20 +1141,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_nothrow_move_constructible_impl<_Tp, true> - : public is_nothrow_constructible<_Tp, _Tp&&> + : public __is_nothrow_constructible_impl<_Tp, _Tp&&> { }; /// is_nothrow_move_constructible template<typename _Tp> struct is_nothrow_move_constructible : public __is_nothrow_move_constructible_impl<_Tp> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_assignable template<typename _Tp, typename _Up> struct is_assignable : 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"); + }; template<typename _Tp, bool = __is_referenceable<_Tp>::value> struct __is_copy_assignable_impl; @@ -1069,14 +1171,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_copy_assignable_impl<_Tp, true> - : public is_assignable<_Tp&, const _Tp&> + : public __bool_constant<__is_assignable(_Tp&, const _Tp&)> { }; /// is_copy_assignable template<typename _Tp> struct is_copy_assignable : public __is_copy_assignable_impl<_Tp> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; template<typename _Tp, bool = __is_referenceable<_Tp>::value> struct __is_move_assignable_impl; @@ -1087,14 +1192,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_move_assignable_impl<_Tp, true> - : public is_assignable<_Tp&, _Tp&&> + : public __bool_constant<__is_assignable(_Tp&, _Tp&&)> { }; /// is_move_assignable template<typename _Tp> struct is_move_assignable : public __is_move_assignable_impl<_Tp> - { }; + { + 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 _Up> struct __is_nt_assignable_impl @@ -1102,12 +1210,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { }; /// is_nothrow_assignable - template<typename _Tp, typename _Up> - struct is_nothrow_assignable - : public __and_<is_assignable<_Tp, _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>> { }; + template<typename _Tp, typename _Up> + struct is_nothrow_assignable + : public __is_nothrow_assignable_impl<_Tp, _Up> + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; + template<typename _Tp, bool = __is_referenceable<_Tp>::value> struct __is_nt_copy_assignable_impl; @@ -1117,14 +1233,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_nt_copy_assignable_impl<_Tp, true> - : public is_nothrow_assignable<_Tp&, const _Tp&> + : public __is_nothrow_assignable_impl<_Tp&, const _Tp&> { }; /// is_nothrow_copy_assignable template<typename _Tp> struct is_nothrow_copy_assignable : public __is_nt_copy_assignable_impl<_Tp> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; template<typename _Tp, bool = __is_referenceable<_Tp>::value> struct __is_nt_move_assignable_impl; @@ -1135,26 +1254,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_nt_move_assignable_impl<_Tp, true> - : public is_nothrow_assignable<_Tp&, _Tp&&> + : public __is_nothrow_assignable_impl<_Tp&, _Tp&&> { }; /// is_nothrow_move_assignable template<typename _Tp> struct is_nothrow_move_assignable : public __is_nt_move_assignable_impl<_Tp> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_trivially_constructible template<typename _Tp, typename... _Args> struct is_trivially_constructible : public __bool_constant<__is_trivially_constructible(_Tp, _Args...)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_trivially_default_constructible template<typename _Tp> struct is_trivially_default_constructible - : public is_trivially_constructible<_Tp>::type - { }; + : public __bool_constant<__is_trivially_constructible(_Tp)> + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; struct __do_is_implicitly_default_constructible_impl { @@ -1182,7 +1310,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template <typename _Tp> struct __is_implicitly_default_constructible - : public __and_<is_default_constructible<_Tp>, + : public __and_<__is_default_constructible_safe<_Tp>, __is_implicitly_default_constructible_safe<_Tp>> { }; @@ -1197,7 +1325,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_trivially_copy_constructible_impl<_Tp, true> - : public __and_<is_copy_constructible<_Tp>, + : public __and_<__is_copy_constructible_impl<_Tp>, integral_constant<bool, __is_trivially_constructible(_Tp, const _Tp&)>> { }; @@ -1205,7 +1333,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct is_trivially_copy_constructible : public __is_trivially_copy_constructible_impl<_Tp> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_trivially_move_constructible @@ -1218,7 +1349,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct __is_trivially_move_constructible_impl<_Tp, true> - : public __and_<is_move_constructible<_Tp>, + : public __and_<__is_move_constructible_impl<_Tp>, integral_constant<bool, __is_trivially_constructible(_Tp, _Tp&&)>> { }; @@ -1226,13 +1357,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct is_trivially_move_constructible : public __is_trivially_move_constructible_impl<_Tp> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_trivially_assignable template<typename _Tp, typename _Up> struct is_trivially_assignable : public __bool_constant<__is_trivially_assignable(_Tp, _Up)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_trivially_copy_assignable @@ -1251,7 +1388,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct is_trivially_copy_assignable : public __is_trivially_copy_assignable_impl<_Tp> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_trivially_move_assignable @@ -1270,21 +1410,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct is_trivially_move_assignable : public __is_trivially_move_assignable_impl<_Tp> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_trivially_destructible template<typename _Tp> struct is_trivially_destructible - : public __and_<is_destructible<_Tp>, + : public __and_<__is_destructible_safe<_Tp>, __bool_constant<__has_trivial_destructor(_Tp)>> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// has_virtual_destructor template<typename _Tp> struct has_virtual_destructor : public integral_constant<bool, __has_virtual_destructor(_Tp)> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; // type property queries. @@ -1292,7 +1441,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// alignment_of template<typename _Tp> struct alignment_of - : public integral_constant<std::size_t, alignof(_Tp)> { }; + : public integral_constant<std::size_t, alignof(_Tp)> + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// rank template<typename> @@ -2596,13 +2749,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct is_swappable : public __is_swappable_impl<_Tp>::type - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_nothrow_swappable template<typename _Tp> struct is_nothrow_swappable : public __is_nothrow_swappable_impl<_Tp>::type - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; #if __cplusplus >= 201402L /// is_swappable_v @@ -2793,20 +2952,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Fn, typename... _ArgTypes> struct is_invocable : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>::type - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}), + "_Fn must be a complete class or an unbounded array"); + }; /// std::is_invocable_r template<typename _Ret, typename _Fn, typename... _ArgTypes> struct is_invocable_r : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, _Ret>::type - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}), + "_Fn must be a complete class or an unbounded array"); + }; /// std::is_nothrow_invocable template<typename _Fn, typename... _ArgTypes> struct is_nothrow_invocable : __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>, - __call_is_nothrow_<_Fn, _ArgTypes...>>::type - { }; + __call_is_nothrow_<_Fn, _ArgTypes...>>::type + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}), + "_Fn must be a complete class or an unbounded array"); + }; template<typename _Result, typename _Ret, typename = void> struct __is_nt_invocable_impl : false_type { }; @@ -3013,7 +3181,10 @@ template <typename _From, typename _To> : bool_constant<__has_unique_object_representations( remove_cv_t<remove_all_extents_t<_Tp>> )> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; template<typename _Tp> inline constexpr bool has_unique_object_representations_v @@ -3025,7 +3196,11 @@ template <typename _From, typename _To> /// is_aggregate template<typename _Tp> struct is_aggregate - : bool_constant<__is_aggregate(remove_cv_t<_Tp>)> { }; + : bool_constant<__is_aggregate(remove_cv_t<_Tp>)> + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), + "template argument must be a complete class or an unbounded array"); + }; /// is_aggregate_v template<typename _Tp> @@ -3084,7 +3259,7 @@ template <typename _From, typename _To> : public __is_array_known_bounds<_Tp> { }; - /// True for a type that is an array of unknown bound. + /// True for a type that is an unbounded array. template<typename _Tp> struct is_unbounded_array : public __is_array_unknown_bounds<_Tp> diff --git a/libstdc++-v3/testsuite/20_util/__is_complete_or_unbounded/memoization.cc b/libstdc++-v3/testsuite/20_util/__is_complete_or_unbounded/memoization.cc new file mode 100644 index 0000000..83afb40 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/__is_complete_or_unbounded/memoization.cc @@ -0,0 +1,29 @@ +// { dg-do compile { target c++11 } } + +// 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/>. + +#include <type_traits> + +struct X; +static_assert( + !std::__is_complete_or_unbounded(std::__type_identity<X>{}), "error"); + +struct X{}; +static_assert( + std::__is_complete_or_unbounded(std::__type_identity<X>{}), + "Result memoized. This leads to worse diagnostics"); diff --git a/libstdc++-v3/testsuite/20_util/__is_complete_or_unbounded/memoization_neg.cc b/libstdc++-v3/testsuite/20_util/__is_complete_or_unbounded/memoization_neg.cc new file mode 100644 index 0000000..04b83a2 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/__is_complete_or_unbounded/memoization_neg.cc @@ -0,0 +1,27 @@ +// { dg-do compile { target c++11 } } +// { dg-prune-output "must be a complete" } + +// 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/>. + +#include <type_traits> + +struct X; +constexpr bool res_incomplete = std::is_move_constructible<X>::value; // { dg-error "required from here" } + +struct X{}; +constexpr bool res_complete = std::is_default_constructible<X>::value; // { dg-bogus "required from here" } diff --git a/libstdc++-v3/testsuite/20_util/__is_complete_or_unbounded/value.cc b/libstdc++-v3/testsuite/20_util/__is_complete_or_unbounded/value.cc new file mode 100644 index 0000000..5a03ad6 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/__is_complete_or_unbounded/value.cc @@ -0,0 +1,100 @@ +// { dg-do compile { target c++11 } } + +// 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/>. + +#include <type_traits> + + +struct incomplete_type; +class incomplete_type2; +union incomplete_union; +enum class incomplete_enum: int; +enum incomplete_enum2: int; +static_assert(!std::__is_complete_or_unbounded(std::__type_identity<incomplete_type>{}), ""); +static_assert(!std::__is_complete_or_unbounded(std::__type_identity<incomplete_type2>{}), ""); +static_assert(!std::__is_complete_or_unbounded(std::__type_identity<incomplete_union>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_enum>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_enum2>{}), ""); + +static_assert(!std::__is_complete_or_unbounded(std::__type_identity<incomplete_type[42]>{}), ""); +static_assert(!std::__is_complete_or_unbounded(std::__type_identity<incomplete_type2[42]>{}), ""); +static_assert(!std::__is_complete_or_unbounded(std::__type_identity<incomplete_union[42]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_enum[42]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_enum2[42]>{}), ""); + +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_type[]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_type2[]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_union[]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_enum[]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_enum2[]>{}), ""); + + +struct complete_type{ ~complete_type() = delete; }; +class complete_type2{ int i; }; +union complete_union{}; +enum class complete_enum: int {}; +enum complete_enum2: int {}; +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_type>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_type2>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_union>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_enum>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_enum2>{}), ""); + +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_type[42]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_type2[42]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_union[42]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_enum[42]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_enum2[42]>{}), ""); + +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_type[]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_type2[]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_union[]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_enum[]>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_enum2[]>{}), ""); + + +static_assert(std::__is_complete_or_unbounded(std::__type_identity<const complete_type>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<const complete_type2>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<const complete_union>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<const complete_enum>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<const complete_enum2>{}), ""); + +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_type*>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_type*>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<const incomplete_type*>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<const complete_type*>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_type&>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_type&>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<incomplete_type&&>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<complete_type&&>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<int complete_type::*>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<int (complete_type::*)(int)>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<int incomplete_type::*>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<int (incomplete_type::*)(int)>{}), ""); + +static_assert(std::__is_complete_or_unbounded(std::__type_identity<void(*)() noexcept>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<void(...) noexcept>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<void(&)(int)>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<void(*)()>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<void(incomplete_type)>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<void(&)()>{}), ""); + +static_assert(std::__is_complete_or_unbounded(std::__type_identity<std::nullptr_t>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<void>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<void*>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<const void* const>{}), ""); diff --git a/libstdc++-v3/testsuite/20_util/is_abstract/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_abstract/incomplete_neg.cc new file mode 100644 index 0000000..94f4ecd --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_abstract/incomplete_neg.cc @@ -0,0 +1,29 @@ +// { dg-do compile { target c++11 } } +// { dg-prune-output "invalid use of incomplete type" } +// { dg-prune-output "must be a complete" } +// +// 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/>. + +#include <type_traits> + +class X; + +void test01() +{ + std::is_abstract<X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/is_aggregate/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_aggregate/incomplete_neg.cc new file mode 100644 index 0000000..8a3dd55 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_aggregate/incomplete_neg.cc @@ -0,0 +1,29 @@ +// { dg-do compile { target c++17 } } +// +// 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; + +void test01() +{ + std::is_aggregate<X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/is_class/value.cc b/libstdc++-v3/testsuite/20_util/is_class/value.cc index 6391e28..801dc67 100644 --- a/libstdc++-v3/testsuite/20_util/is_class/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_class/value.cc @@ -27,6 +27,7 @@ void test01() // Positive tests. static_assert(test_category<is_class, ClassType>(true), ""); + static_assert(test_category<is_class, IncompleteClass>(true), ""); static_assert(test_category<is_class, DerivedType>(true), ""); static_assert(test_category<is_class, ConvType>(true), ""); static_assert(test_category<is_class, AbstractClass>(true), ""); @@ -47,4 +48,5 @@ void test01() static_assert(test_category<is_class, int (ClassType::*) (int)>(false), ""); static_assert(test_category<is_class, int (int)>(false), ""); static_assert(test_category<is_class, EnumType>(false), ""); + static_assert(test_category<is_class, IncompleteUnion>(false), ""); } diff --git a/libstdc++-v3/testsuite/20_util/is_function/value.cc b/libstdc++-v3/testsuite/20_util/is_function/value.cc index cef2e3d..7b94b58 100644 --- a/libstdc++-v3/testsuite/20_util/is_function/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_function/value.cc @@ -46,4 +46,6 @@ void test01() // Sanity check. static_assert(test_category<is_function, ClassType>(false), ""); + static_assert(test_category<is_function, IncompleteClass>(false), ""); + static_assert(test_category<is_function, IncompleteUnion>(false), ""); } diff --git a/libstdc++-v3/testsuite/20_util/is_move_constructible/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_move_constructible/incomplete_neg.cc new file mode 100644 index 0000000..d6a08d7 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_move_constructible/incomplete_neg.cc @@ -0,0 +1,29 @@ +// { dg-do compile { target c++11 } } +// +// 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; + +void test01() +{ + std::is_move_constructible<X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_move_assignable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_move_assignable/incomplete_neg.cc new file mode 100644 index 0000000..ebceec5 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_move_assignable/incomplete_neg.cc @@ -0,0 +1,29 @@ +// { dg-do compile { target c++11 } } +// +// 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; + +void test01() +{ + std::is_nothrow_move_assignable<X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/is_polymorphic/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_polymorphic/incomplete_neg.cc new file mode 100644 index 0000000..8cd1b40 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_polymorphic/incomplete_neg.cc @@ -0,0 +1,29 @@ +// { dg-do compile { target c++11 } } +// { dg-prune-output "invalid use of incomplete type" } +// { dg-prune-output "must be a complete" } +// +// 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/>. + +#include <type_traits> + +class X; + +void test01() +{ + std::is_polymorphic<X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/is_reference/value.cc b/libstdc++-v3/testsuite/20_util/is_reference/value.cc index 79a9973..4676894 100644 --- a/libstdc++-v3/testsuite/20_util/is_reference/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_reference/value.cc @@ -33,8 +33,11 @@ void test01() static_assert(test_category<is_reference, int&&>(true), ""); static_assert(test_category<is_reference, ClassType&&>(true), ""); static_assert(test_category<is_reference, int(&&)(int)>(true), ""); + static_assert(test_category<is_reference, IncompleteClass&>(true), ""); + static_assert(test_category<is_reference, const IncompleteClass&>(true), ""); // Sanity check. static_assert(test_category<is_reference, ClassType>(false), ""); + static_assert(test_category<is_reference, IncompleteClass>(false), ""); } diff --git a/libstdc++-v3/testsuite/20_util/is_unbounded_array/value.cc b/libstdc++-v3/testsuite/20_util/is_unbounded_array/value.cc index 19fb052..bca0e3c 100644 --- a/libstdc++-v3/testsuite/20_util/is_unbounded_array/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_unbounded_array/value.cc @@ -44,6 +44,8 @@ void test01() static_assert(test_category<is_unbounded_array, ClassType[]>(true), ""); static_assert(test_category<is_unbounded_array, ClassType[2][3]>(false), ""); static_assert(test_category<is_unbounded_array, ClassType[][3]>(true), ""); + static_assert(test_category<is_unbounded_array, IncompleteClass[2][3]>(false), ""); + static_assert(test_category<is_unbounded_array, IncompleteClass[][3]>(true), ""); static_assert(test_category<is_unbounded_array, int(*)[2]>(false), ""); static_assert(test_category<is_unbounded_array, int(*)[]>(false), ""); static_assert(test_category<is_unbounded_array, int(&)[2]>(false), ""); @@ -51,6 +53,8 @@ void test01() // Sanity check. static_assert(test_category<is_unbounded_array, ClassType>(false), ""); + static_assert(test_category<is_unbounded_array, IncompleteClass>(false), ""); + static_assert(test_category<is_unbounded_array, IncompleteUnion>(false), ""); } template <class... T> void pos() diff --git a/libstdc++-v3/testsuite/20_util/is_union/value.cc b/libstdc++-v3/testsuite/20_util/is_union/value.cc index 7d0f201..54df151 100644 --- a/libstdc++-v3/testsuite/20_util/is_union/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_union/value.cc @@ -27,6 +27,7 @@ void test01() // Positive tests. static_assert(test_category<is_union, UnionType>(true), ""); + static_assert(test_category<is_union, IncompleteUnion>(true), ""); // Negative tests. static_assert(test_category<is_union, ClassType>(false), ""); @@ -47,4 +48,5 @@ void test01() static_assert(test_category<is_union, int (ClassType::*) (int)>(false), ""); static_assert(test_category<is_union, int (int)>(false), ""); static_assert(test_category<is_union, EnumType>(false), ""); + static_assert(test_category<is_union, IncompleteClass>(false), ""); } diff --git a/libstdc++-v3/testsuite/20_util/is_void/value.cc b/libstdc++-v3/testsuite/20_util/is_void/value.cc index f04cdd6..dc116f7 100644 --- a/libstdc++-v3/testsuite/20_util/is_void/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_void/value.cc @@ -47,4 +47,6 @@ void test01() // Sanity check. static_assert(test_category<is_void, ClassType>(false), ""); + static_assert(test_category<is_void, IncompleteClass>(false), ""); + static_assert(test_category<is_void, IncompleteUnion>(false), ""); } diff --git a/libstdc++-v3/testsuite/util/testsuite_tr1.h b/libstdc++-v3/testsuite/util/testsuite_tr1.h index b431682..2e1cbb0 100644 --- a/libstdc++-v3/testsuite/util/testsuite_tr1.h +++ b/libstdc++-v3/testsuite/util/testsuite_tr1.h @@ -126,6 +126,8 @@ namespace __gnu_test union UnionType { }; + union IncompleteUnion; + class IncompleteClass; struct ExplicitClass