diff mbox series

PR libstdc++/71579 assert that type traits are not misused with an incomplete type

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

Commit Message

Antony Polukhin May 6, 2019, 11:19 a.m. UTC
This patch adds static asserts for type traits misuse with incomplete
classes and unions. This gives a nice readable error message instead
of an UB and odr-violations.

Some features of the patch:
* each type trait has it's own static_assert inside. This gives better
diagnostics than the approach with putting the assert into a helper
structure and using it in each trait.
* the result of completeness check is not memorized by the compiler.
This gives no false positive after the first failed check.
* some of the compiler builtins already implement the check. But not
all of them! So the asserts are in all the type_traits that may
benefit from the check. This also makes the behavior of libstdc++ more
consistent across different (non GCC) compilers.
* std::is_base_of does not have the assert as it works well in many
cases with incomplete types

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.

Comments

Jonathan Wakely May 8, 2019, 9:10 p.m. UTC | #1
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.
Antony Polukhin May 30, 2019, 6:06 a.m. UTC | #2
чт, 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.
Jonathan Wakely May 30, 2019, 4:38 p.m. UTC | #3
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).
Antony Polukhin May 31, 2019, 5:58 a.m. UTC | #4
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!

>
Jonathan Wakely May 31, 2019, 10:35 a.m. UTC | #5
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.
Jonathan Wakely June 6, 2019, 12:19 p.m. UTC | #6
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.
Antony Polukhin June 20, 2019, 5:49 p.m. UTC | #7
чт, 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.
Ville Voutilainen June 20, 2019, 5:57 p.m. UTC | #8
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.
Antony Polukhin June 20, 2019, 6:55 p.m. UTC | #9
чт, 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.
Antony Polukhin Aug. 12, 2020, 8:19 a.m. UTC | #10
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.
Jonathan Wakely Aug. 19, 2020, 11:29 a.m. UTC | #11
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.
Antony Polukhin Aug. 19, 2020, 4:44 p.m. UTC | #12
ср, 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.
Antony Polukhin Aug. 20, 2020, 3:31 p.m. UTC | #13
ср, 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.
Antony Polukhin Sept. 24, 2020, 7:15 a.m. UTC | #14
Looks like the last patch was not applied. Do I have to change something in
it?
Jonathan Wakely Sept. 24, 2020, 9:29 a.m. UTC | #15
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.
Jonathan Wakely Sept. 24, 2020, 5:53 p.m. UTC | #16
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!
Antony Polukhin Nov. 12, 2020, 6:55 p.m. UTC | #17
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" }
+}
Antony Polukhin Jan. 8, 2021, 5:28 p.m. UTC | #18
On Thu, Nov 12, 2020, 21:55 Antony Polukhin <antoshkka@gmail.com> wrote:

> Final bits for libstdc/71579
>

Gentle reminder on last patch

>
Antony Polukhin May 7, 2021, 6 p.m. UTC | #19
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 mbox series

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