Commit 608a080c by Antony Polukhin Committed by Jonathan Wakely

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

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

2019-05-31  Antony Polukhin  <antoshkka@gmail.com>

	PR libstdc++/71579
	* include/std/type_traits __type_identity, __is_complete_or_unbounded):
	New helpers for checking preconditions in traits.
	(is_trivial, is_trivially_copyable, is_standard_layout, is_pod)
	(is_literal_type, is_empty, is_polymorphic, is_final, is_abstract)
	(is_destructible, is_nothrow_destructible, is_constructible)
	(is_default_constructible, is_copy_constructible)
	(is_move_constructible, is_nothrow_default_constructible)
	(is_nothrow_constructible, is_nothrow_copy_constructible)
	(is_nothrow_move_constructible, is_copy_assignable, is_move_assignable)
	(is_nothrow_assignable, is_nothrow_copy_assignable)
	(is_nothrow_move_assignable, is_trivially_constructible)
	(is_trivially_copy_constructible, is_trivially_move_constructible)
	is_trivially_assignable, is_trivially_copy_assignable)
	(is_trivially_move_assignable, is_trivially_destructible)
	(alignment_of, is_swappable, is_nothrow_swappable, is_invocable)
	(is_invocable_r, is_nothrow_invocable)
	(has_unique_object_representations, is_aggregate): Add static_asserts
	to make sure that type traits are not misused with incomplete types.
	(__is_constructible_impl, __is_nothrow_default_constructible_impl)
	(__is_nothrow_constructible_impl, __is_nothrow_assignable_impl): 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: New
	test.
	* testsuite/20_util/is_complete_or_unbounded/value.cc: New test.
	* testsuite/20_util/is_abstract/incomplete_neg.cc: New test.
	* testsuite/20_util/is_aggregate/incomplete_neg.cc: New test.
	* testsuite/20_util/is_class/value.cc: Check incomplete type.
	* testsuite/20_util/is_function/value.cc: Likewise.
	* testsuite/20_util/is_move_constructible/incomplete_neg.cc: New test.
	* testsuite/20_util/is_nothrow_move_assignable/incomplete_neg.cc: New
	test.
	* testsuite/20_util/is_polymorphic/incomplete_neg.cc: New test.
	* testsuite/20_util/is_reference/value.cc: Check incomplete types.
	* 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.

From-SVN: r271806
parent aeedf077
2019-05-31 Antony Polukhin <antoshkka@gmail.com>
PR libstdc++/71579
* include/std/type_traits __type_identity, __is_complete_or_unbounded):
New helpers for checking preconditions in traits.
(is_trivial, is_trivially_copyable, is_standard_layout, is_pod)
(is_literal_type, is_empty, is_polymorphic, is_final, is_abstract)
(is_destructible, is_nothrow_destructible, is_constructible)
(is_default_constructible, is_copy_constructible)
(is_move_constructible, is_nothrow_default_constructible)
(is_nothrow_constructible, is_nothrow_copy_constructible)
(is_nothrow_move_constructible, is_copy_assignable, is_move_assignable)
(is_nothrow_assignable, is_nothrow_copy_assignable)
(is_nothrow_move_assignable, is_trivially_constructible)
(is_trivially_copy_constructible, is_trivially_move_constructible)
is_trivially_assignable, is_trivially_copy_assignable)
(is_trivially_move_assignable, is_trivially_destructible)
(alignment_of, is_swappable, is_nothrow_swappable, is_invocable)
(is_invocable_r, is_nothrow_invocable)
(has_unique_object_representations, is_aggregate): Add static_asserts
to make sure that type traits are not misused with incomplete types.
(__is_constructible_impl, __is_nothrow_default_constructible_impl)
(__is_nothrow_constructible_impl, __is_nothrow_assignable_impl): 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: New
test.
* testsuite/20_util/is_complete_or_unbounded/value.cc: New test.
* testsuite/20_util/is_abstract/incomplete_neg.cc: New test.
* testsuite/20_util/is_aggregate/incomplete_neg.cc: New test.
* testsuite/20_util/is_class/value.cc: Check incomplete type.
* testsuite/20_util/is_function/value.cc: Likewise.
* testsuite/20_util/is_move_constructible/incomplete_neg.cc: New test.
* testsuite/20_util/is_nothrow_move_assignable/incomplete_neg.cc: New
test.
* testsuite/20_util/is_polymorphic/incomplete_neg.cc: New test.
* testsuite/20_util/is_reference/value.cc: Check incomplete types.
* 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-31 Jonathan Wakely <jwakely@redhat.com>
* include/bits/random.h (random_device::_M_init(const char*, size_t)):
......
// { 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" }
}
// { 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" }
}
......@@ -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), "");
}
// { 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");
// { 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" }
// { 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>{}), "");
......@@ -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), "");
}
// { 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" }
}
// { 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" }
}
// { 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" }
}
......@@ -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), "");
}
......@@ -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()
......
......@@ -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), "");
}
......@@ -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), "");
}
......@@ -126,6 +126,8 @@ namespace __gnu_test
union UnionType { };
union IncompleteUnion;
class IncompleteClass;
struct ExplicitClass
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment