inline constexpr bool is_unbounded_array_v
= is_unbounded_array<_Tp>::value;
+#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
+ template<typename _Base, typename _Derived>
+ struct is_pointer_interconvertible_base_of
+ : bool_constant<__is_pointer_interconvertible_base_of(_Base, _Derived)>
+ { };
+
+ /// @ingroup variable_templates
+ /// @since C++20
+ template<typename _Base, typename _Derived>
+ constexpr bool is_pointer_interconvertible_base_of_v
+ = __is_pointer_interconvertible_base_of(_Base, _Derived);
+
+#if __has_builtin(__builtin_is_pointer_interconvertible_with_class)
+#define __cpp_lib_is_pointer_interconvertible 201907L
+
+ /// True if `__mp` points to the first member of a standard-layout type
+ /// @returns true if `s.*__mp` is pointer-interconvertible with `s`
+ /// @since C++20
+ template<typename _Tp, typename _Mem>
+ constexpr bool
+ is_pointer_interconvertible_with_class(_Mem _Tp::*__mp) noexcept
+ { return __builtin_is_pointer_interconvertible_with_class(__mp); }
+#endif
+#endif
+
#if __cplusplus > 202002L
#define __cpp_lib_is_scoped_enum 202011L
--- /dev/null
+// { dg-options "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+#include <type_traits>
+
+#ifndef __cpp_lib_is_pointer_interconvertible
+# error "Feature test macro for is_pointer_interconvertible is missing in <type_traits>"
+#elif __cpp_lib_is_pointer_interconvertible < 201907L
+# error "Feature test macro for is_pointer_interconvertible has wrong value in <type_traits>"
+#endif
+
+static_assert( std::is_pointer_interconvertible_base_of<void, void>::value
+ == std::is_pointer_interconvertible_base_of_v<void, void> );
+
+struct B { };
+
+static_assert( std::is_pointer_interconvertible_base_of<B, B>::value
+ == std::is_pointer_interconvertible_base_of_v<B, B> );
+
+static_assert( std::is_pointer_interconvertible_base_of_v<B, B> );
+static_assert( std::is_pointer_interconvertible_base_of_v<B, const B> );
+static_assert( std::is_pointer_interconvertible_base_of_v<const B, B> );
+static_assert( std::is_pointer_interconvertible_base_of_v<const B, const B> );
+
+struct D : B { int i; };
+
+static_assert( std::is_pointer_interconvertible_base_of_v<D, D> );
+
+static_assert( std::is_pointer_interconvertible_base_of_v<B, D> );
+static_assert( std::is_pointer_interconvertible_base_of_v<const B, D> );
+static_assert( std::is_pointer_interconvertible_base_of_v<B, const D> );
+static_assert( std::is_pointer_interconvertible_base_of_v<const B, const D> );
+
+static_assert( ! std::is_pointer_interconvertible_base_of_v<D, B> );
+
+struct E : D { };
+// E is not standard-layout
+static_assert( ! std::is_pointer_interconvertible_base_of_v<E, B> );
+
+struct D1 : B { };
+struct D2 : B { };
+struct D3 : D1, D2 { };
+// B is ambiguously derived
+static_assert( ! std::is_pointer_interconvertible_base_of_v<B, D3> );
+
+union U;
+static_assert( ! std::is_pointer_interconvertible_base_of_v<U, U> );
+static_assert( ! std::is_pointer_interconvertible_base_of_v<U, D> );
+
+struct I; // incomplete
+static_assert( std::is_pointer_interconvertible_base_of_v<I, I> );
+static_assert( std::is_pointer_interconvertible_base_of_v<I, const I> );