diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2021-08-24 14:42:37 +0100 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2021-08-24 16:12:44 +0100 |
commit | 037ef219b27c26d4c125368e685a89da7f8cc701 (patch) | |
tree | a1c12e03ad596ea1384bcd6c4cb9f5a4caf99ea8 /libstdc++-v3 | |
parent | 8ce18a29ef717f5920ebf5dc1d9e84570a1827d4 (diff) | |
download | gcc-037ef219b27c26d4c125368e685a89da7f8cc701.zip gcc-037ef219b27c26d4c125368e685a89da7f8cc701.tar.gz gcc-037ef219b27c26d4c125368e685a89da7f8cc701.tar.bz2 |
libstdc++: Add std::is_layout_compatible trait for C++20
Signed-off-by: Jonathan Wakely <jwakely@redhat.com>
libstdc++-v3/ChangeLog:
* include/std/type_traits (is_layout_compatible): Define.
(is_corresponding_member): Define.
* include/std/version (__cpp_lib_is_layout_compatible): Define.
* testsuite/20_util/is_layout_compatible/is_corresponding_member.cc:
New test.
* testsuite/20_util/is_layout_compatible/value.cc: New test.
* testsuite/20_util/is_layout_compatible/version.cc: New test.
* testsuite/20_util/is_pointer_interconvertible/with_class.cc:
New test.
* testsuite/23_containers/span/layout_compat.cc: Do not use real
std::is_layout_compatible trait if available.
Diffstat (limited to 'libstdc++-v3')
7 files changed, 153 insertions, 9 deletions
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index 1571800..a0010d9 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -3414,6 +3414,31 @@ template<typename _Ret, typename _Fn, typename... _Args> inline constexpr bool is_unbounded_array_v = is_unbounded_array<_Tp>::value; +#if __has_builtin(__is_layout_compatible) + + /// @since C++20 + template<typename _Tp, typename _Up> + struct is_layout_compatible + : bool_constant<__is_layout_compatible(_Tp, _Up)> + { }; + + /// @ingroup variable_templates + /// @since C++20 + template<typename _Tp, typename _Up> + constexpr bool is_layout_compatible_v + = __is_layout_compatible(_Tp, _Up); + +#if __has_builtin(__builtin_is_corresponding_member) +#define __cpp_lib_is_layout_compatible 201907L + + /// @since C++20 + template<typename _S1, typename _S2, typename _M1, typename _M2> + constexpr bool + is_corresponding_member(_M1 _S1::*__m1, _M2 _S2::*__m2) noexcept + { return __builtin_is_corresponding_member(__m1, __m2); } +#endif +#endif + #if __has_builtin(__is_pointer_interconvertible_base_of) /// True if `_Derived` is standard-layout and has a base class of type `_Base` /// @since C++20 diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 925f277..70d573b 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -236,6 +236,10 @@ #ifdef _GLIBCXX_HAS_GTHREADS # define __cpp_lib_jthread 201911L #endif +#if __has_builtin(__is_layout_compatible) \ + && __has_builtin(__builtin_is_corresponding_member) +# define __cpp_lib_is_layout_compatible 201907L +#endif #if __has_builtin(__is_pointer_interconvertible_base_of) \ && __has_builtin(__builtin_is_pointer_interconvertible_with_class) # define __cpp_lib_is_pointer_interconvertible 201907L diff --git a/libstdc++-v3/testsuite/20_util/is_layout_compatible/is_corresponding_member.cc b/libstdc++-v3/testsuite/20_util/is_layout_compatible/is_corresponding_member.cc new file mode 100644 index 0000000..69b359a --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_layout_compatible/is_corresponding_member.cc @@ -0,0 +1,19 @@ +// { dg-options "-std=gnu++20" } +// { dg-do compile { target c++20 } } +#include <type_traits> + +using std::is_corresponding_member; + +struct A { int a; }; +struct B { int b; }; +struct C: public A, public B { }; // not a standard-layout class + +static_assert( is_corresponding_member( &C::a, &C::b ) ); +// Succeeds because arguments have types int A::* and int B::* + +constexpr int C::*a = &C::a; +constexpr int C::*b = &C::b; +static_assert( ! is_corresponding_member( a, b ) ); +// Not corresponding members, because arguments both have type int C::* + +static_assert( noexcept(!is_corresponding_member(a, b)) ); diff --git a/libstdc++-v3/testsuite/20_util/is_layout_compatible/value.cc b/libstdc++-v3/testsuite/20_util/is_layout_compatible/value.cc new file mode 100644 index 0000000..7686b34 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_layout_compatible/value.cc @@ -0,0 +1,56 @@ +// { dg-options "-std=gnu++20" } +// { dg-do compile { target c++20 } } +#include <type_traits> + +#ifndef __cpp_lib_is_layout_compatible +# error "Feature test macro for is_layout_compatible is missing in <type_traits>" +#elif __cpp_lib_is_layout_compatible < 201907L +# error "Feature test macro for is_layout_compatible has wrong value in <type_traits>" +#endif + +template<typename T, typename U> +concept variable_template_is_correct + = std::is_layout_compatible_v<T, U> == std::is_layout_compatible<T, U>::value; + +template<typename T, typename U> +requires variable_template_is_correct<T, U> +constexpr bool is_layout_compatible = std::is_layout_compatible_v<T, U>; + +static_assert( is_layout_compatible<void, void> ); +static_assert( is_layout_compatible<int, int> ); +static_assert( ! is_layout_compatible<int, int[]> ); +static_assert( ! is_layout_compatible<int, int[1]> ); +static_assert( is_layout_compatible<int[], int[]> ); +static_assert( is_layout_compatible<int[1], int[1]> ); +static_assert( ! is_layout_compatible<int[1], int[]> ); +static_assert( ! is_layout_compatible<int[1], int[2]> ); + +struct Incomplete; +// The standard says these are undefined, but they should work really: +// static_assert( is_layout_compatible<Incomplete, Incomplete> ); +// static_assert( ! is_layout_compatible<Incomplete[], Incomplete> ); +static_assert( is_layout_compatible<Incomplete[], Incomplete[]> ); + +enum E1 : int { }; +enum E2 : int; +static_assert( is_layout_compatible<E1, E2> ); +enum E3 : unsigned int; +static_assert( ! is_layout_compatible<E1, E3> ); +enum E4 : char { }; +enum E5 : signed char { }; +enum E6 : unsigned char { }; +static_assert( ! is_layout_compatible<E4, E5> ); +static_assert( ! is_layout_compatible<E4, E6> ); +static_assert( ! is_layout_compatible<E5, E6> ); + +struct A { int a; }; +struct B { const int b; }; +static_assert( is_layout_compatible<A, B> ); +static_assert( is_layout_compatible<B, A> ); + +struct C : A { }; +struct D : B { }; +static_assert( is_layout_compatible<C, D> ); + +struct E : A { int i; }; // not standard-layout +static_assert( ! is_layout_compatible<E, A> ); diff --git a/libstdc++-v3/testsuite/20_util/is_layout_compatible/version.cc b/libstdc++-v3/testsuite/20_util/is_layout_compatible/version.cc new file mode 100644 index 0000000..1a32275 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_layout_compatible/version.cc @@ -0,0 +1,10 @@ +// { dg-options "-std=gnu++20" } +// { dg-do compile { target c++20 } } + +#include <version> + +#ifndef __cpp_lib_is_layout_compatible +# error "Feature test macro for is_layout_compatible is missing in <version>" +#elif __cpp_lib_is_pointer_interconvertible < 201907L +# error "Feature test macro for is_layout_compatible has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/20_util/is_pointer_interconvertible/with_class.cc b/libstdc++-v3/testsuite/20_util/is_pointer_interconvertible/with_class.cc new file mode 100644 index 0000000..28de9b4 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_pointer_interconvertible/with_class.cc @@ -0,0 +1,29 @@ +// { dg-options "-std=gnu++20" } +// { dg-do compile { target c++20 } } +#include <type_traits> + +struct A { int i; long l; }; + +static_assert( std::is_pointer_interconvertible_with_class(&A::i) ); +static_assert( ! std::is_pointer_interconvertible_with_class(&A::l) ); + +constexpr int A::*a = nullptr; +static_assert( ! std::is_pointer_interconvertible_with_class(a) ); +static_assert( noexcept( std::is_pointer_interconvertible_with_class(a) ) ); + +struct B { const int i; }; +static_assert( std::is_pointer_interconvertible_with_class(&B::i) ); + +struct C { int f(); }; +static_assert( ! std::is_pointer_interconvertible_with_class(&C::f) ); + +struct D : A { }; +static_assert( std::is_pointer_interconvertible_with_class(&D::i) ); + +struct E : A { int j; }; +// This works because the type of &E::i is int A::* and A is standard-layout: +static_assert( std::is_pointer_interconvertible_with_class(&E::i) ); +constexpr int E::*e = a; +// This fails because E is not standard-layout: +static_assert( ! std::is_pointer_interconvertible_with_class(e) ); +static_assert( ! std::is_pointer_interconvertible_with_class(&E::j) ); diff --git a/libstdc++-v3/testsuite/23_containers/span/layout_compat.cc b/libstdc++-v3/testsuite/23_containers/span/layout_compat.cc index 04947e9..bb560fe 100644 --- a/libstdc++-v3/testsuite/23_containers/span/layout_compat.cc +++ b/libstdc++-v3/testsuite/23_containers/span/layout_compat.cc @@ -27,22 +27,23 @@ struct iovec { void* iov_base; std::size_t iov_len; }; #endif -#if __cpp_lib_is_layout_compatible -using std::is_layout_compatible_v; -#else -// A poor substitute for is_layout_compatible_v +// std::span cannot possibly be layout-compatible with struct iovec because +// iovec::iov_base is a void* and span<void> is ill-formed. Additionally, +// the libstdc++ std::span uses [[no_unique_address]] on the second member, +// so that it's not present for a span of static extent, and that affects +// layout-compatibility too. +// Use this to check the size and alignment are compatible. template<typename T, typename U> - constexpr bool is_layout_compatible_v + constexpr bool same_size_and_alignment = std::is_standard_layout_v<T> && std::is_standard_layout_v<U> && sizeof(T) == sizeof(U) && alignof(T) == alignof(U); -#endif void test_pr95609() { using rbuf = std::span<const std::byte>; - using wbuf = std::span<std::byte>; + static_assert(same_size_and_alignment<rbuf, struct iovec>); - static_assert(is_layout_compatible_v<rbuf, struct iovec>); - static_assert(is_layout_compatible_v<wbuf, struct iovec>); + using wbuf = std::span<std::byte>; + static_assert(same_size_and_alignment<wbuf, struct iovec>); } |