[libcxx-commits] [libcxx] [libc++] Implement P2985R0: std::is_virtual_base_of (PR #105847)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Aug 23 08:38:58 PDT 2024
https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/105847
This trait is implemented in C++26 conditionally on the compiler supporting the __builtin_is_virtual_base_of intrinsic. I believe only tip-of-trunk Clang currently implements that builtin.
Closes #105432
>From eb33f895f596abdc465982b9b5ce32cb2a946ddc Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 23 Aug 2024 11:34:36 -0400
Subject: [PATCH] [libc++] Implement P2985R0: std::is_virtual_base_of
This trait is implemented in C++26 conditionally on the compiler
supporting the __builtin_is_virtual_base_of intrinsic. I believe
only tip-of-trunk Clang currently implements that builtin.
Closes #105432
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/docs/ReleaseNotes/20.rst | 2 +-
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
libcxx/include/__type_traits/is_base_of.h | 13 ++
libcxx/include/type_traits | 3 +
libcxx/include/version | 4 +-
.../type_traits.version.compile.pass.cpp | 6 +-
.../version.version.compile.pass.cpp | 6 +-
.../meta/meta.rel/is_virtual_base_of.pass.cpp | 166 ++++++++++++++++++
.../generate_feature_test_macro_components.py | 3 +-
10 files changed, 196 insertions(+), 11 deletions(-)
create mode 100644 libcxx/test/std/utilities/meta/meta.rel/is_virtual_base_of.pass.cpp
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index a1506e115fe70f..f6d3142c1e2d3e 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -442,7 +442,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_inplace_vector`` *unimplemented*
---------------------------------------------------------- -----------------
- ``__cpp_lib_is_virtual_base_of`` *unimplemented*
+ ``__cpp_lib_is_virtual_base_of`` ``202406L``
---------------------------------------------------------- -----------------
``__cpp_lib_is_within_lifetime`` *unimplemented*
---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst
index fe9f4c1973cdb4..bc28f380945bc3 100644
--- a/libcxx/docs/ReleaseNotes/20.rst
+++ b/libcxx/docs/ReleaseNotes/20.rst
@@ -38,7 +38,7 @@ What's New in Libc++ 20.0.0?
Implemented Papers
------------------
-- TODO
+- P2985R0: A type trait for detecting virtual base classes (`Github <https://github.com/llvm/llvm-project/issues/105432>`__)
Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index dd62bcc2555ffc..d95cb11f483c00 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -68,7 +68,7 @@
"`P2389R2 <https://wg21.link/P2389R2>`__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19.0",""
"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","","|ranges|"
"`P3217R0 <https://wg21.link/P3217R0>`__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","",""
-"`P2985R0 <https://wg21.link/P2985R0>`__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","","",""
+"`P2985R0 <https://wg21.link/P2985R0>`__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20.0",""
"`P0843R14 <https://wg21.link/P0843R14>`__","``inplace_vector``","2024-06 (St. Louis)","","",""
"`P3235R3 <https://wg21.link/P3235R3>`__","``std::print`` more types faster with less memory","2024-06 (St. Louis)","","","|format| |DR|"
"`P2968R2 <https://wg21.link/P2968R2>`__","Make ``std::ignore`` a first-class object","2024-06 (St. Louis)","|Complete|","19.0",""
diff --git a/libcxx/include/__type_traits/is_base_of.h b/libcxx/include/__type_traits/is_base_of.h
index 090abeeb54dccb..3aac3629a02521 100644
--- a/libcxx/include/__type_traits/is_base_of.h
+++ b/libcxx/include/__type_traits/is_base_of.h
@@ -26,6 +26,19 @@ template <class _Bp, class _Dp>
inline constexpr bool is_base_of_v = __is_base_of(_Bp, _Dp);
#endif
+#if _LIBCPP_STD_VER >= 26
+# if __has_builtin(__builtin_is_virtual_base_of)
+
+template <class _Base, class _Derived>
+struct _LIBCPP_TEMPLATE_VIS is_virtual_base_of
+ : public integral_constant<bool, __builtin_is_virtual_base_of(_Base, _Derived)> {};
+
+template <class _Base, class _Derived>
+inline constexpr bool is_virtual_base_of_v = __builtin_is_virtual_base_of(_Base, _Derived);
+
+# endif
+#endif
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___TYPE_TRAITS_IS_BASE_OF_H
diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits
index 7f231cd09df510..5937d4fdc9e1a7 100644
--- a/libcxx/include/type_traits
+++ b/libcxx/include/type_traits
@@ -144,6 +144,7 @@ namespace std
// Relationships between types:
template <class T, class U> struct is_same;
template <class Base, class Derived> struct is_base_of;
+ template <class Base, class Derived> struct is_virtual_base_of; // C++26
template <class From, class To> struct is_convertible;
template <typename From, typename To> struct is_nothrow_convertible; // C++20
@@ -391,6 +392,8 @@ namespace std
= is_same<T, U>::value; // C++17
template <class Base, class Derived> inline constexpr bool is_base_of_v
= is_base_of<Base, Derived>::value; // C++17
+ template <class Base, class Derived> inline constexpr bool is_virtual_base_of_v
+ = is_virtual_base_of<Base, Derived>::value; // C++26
template <class From, class To> inline constexpr bool is_convertible_v
= is_convertible<From, To>::value; // C++17
template <class Fn, class... ArgTypes> inline constexpr bool is_invocable_v
diff --git a/libcxx/include/version b/libcxx/include/version
index fe64343eafbc9c..a19be2d294afd3 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -531,7 +531,9 @@ __cpp_lib_void_t 201411L <type_traits>
// # define __cpp_lib_generate_random 202403L
// # define __cpp_lib_hazard_pointer 202306L
// # define __cpp_lib_inplace_vector 202406L
-// # define __cpp_lib_is_virtual_base_of 202406L
+# if __has_builtin(__builtin_is_virtual_base_of)
+# define __cpp_lib_is_virtual_base_of 202406L
+# endif
// # define __cpp_lib_is_within_lifetime 202306L
// # define __cpp_lib_linalg 202311L
# undef __cpp_lib_mdspan
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
index bb69ca7368aafa..1cbf2699a95bcc 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
@@ -857,16 +857,16 @@
# error "__cpp_lib_is_swappable should have the value 201603L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
+# if __has_builtin(__builtin_is_virtual_base_of)
# ifndef __cpp_lib_is_virtual_base_of
# error "__cpp_lib_is_virtual_base_of should be defined in c++26"
# endif
# if __cpp_lib_is_virtual_base_of != 202406L
# error "__cpp_lib_is_virtual_base_of should have the value 202406L in c++26"
# endif
-# else // _LIBCPP_VERSION
+# else
# ifdef __cpp_lib_is_virtual_base_of
-# error "__cpp_lib_is_virtual_base_of should not be defined because it is unimplemented in libc++!"
+# error "__cpp_lib_is_virtual_base_of should not be defined when the requirement '__has_builtin(__builtin_is_virtual_base_of)' is not met!"
# endif
# endif
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index b8bad696f1bae0..bd2959d55dc20d 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -7172,16 +7172,16 @@
# error "__cpp_lib_is_swappable should have the value 201603L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
+# if __has_builtin(__builtin_is_virtual_base_of)
# ifndef __cpp_lib_is_virtual_base_of
# error "__cpp_lib_is_virtual_base_of should be defined in c++26"
# endif
# if __cpp_lib_is_virtual_base_of != 202406L
# error "__cpp_lib_is_virtual_base_of should have the value 202406L in c++26"
# endif
-# else // _LIBCPP_VERSION
+# else
# ifdef __cpp_lib_is_virtual_base_of
-# error "__cpp_lib_is_virtual_base_of should not be defined because it is unimplemented in libc++!"
+# error "__cpp_lib_is_virtual_base_of should not be defined when the requirement '__has_builtin(__builtin_is_virtual_base_of)' is not met!"
# endif
# endif
diff --git a/libcxx/test/std/utilities/meta/meta.rel/is_virtual_base_of.pass.cpp b/libcxx/test/std/utilities/meta/meta.rel/is_virtual_base_of.pass.cpp
new file mode 100644
index 00000000000000..6b34d56e2c6f45
--- /dev/null
+++ b/libcxx/test/std/utilities/meta/meta.rel/is_virtual_base_of.pass.cpp
@@ -0,0 +1,166 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// These compilers don't support __builtin_is_virtual_base_of yet.
+// UNSUPPORTED: clang-17, clang-18, clang-19, gcc-14, apple-clang-16, apple-clang-17
+
+// <type_traits>
+
+// std::is_virtual_base_of
+
+#include <type_traits>
+#include <cassert>
+
+template <bool expected, class Base, class Derived>
+void test() {
+ // Test the type of the variables
+ {
+ static_assert(std::is_same_v<bool const, decltype(std::is_virtual_base_of<Base, Derived>::value)>);
+ static_assert(std::is_same_v<bool const, decltype(std::is_virtual_base_of_v<Base, Derived>)>);
+ }
+
+ // Test their value
+ {
+ static_assert(std::is_virtual_base_of<Base, Derived>::value == expected);
+ static_assert(std::is_virtual_base_of<const Base, Derived>::value == expected);
+ static_assert(std::is_virtual_base_of<Base, const Derived>::value == expected);
+ static_assert(std::is_virtual_base_of<const Base, const Derived>::value == expected);
+
+ static_assert(std::is_virtual_base_of_v<Base, Derived> == expected);
+ static_assert(std::is_virtual_base_of_v<const Base, Derived> == expected);
+ static_assert(std::is_virtual_base_of_v<Base, const Derived> == expected);
+ static_assert(std::is_virtual_base_of_v<const Base, const Derived> == expected);
+ }
+
+ // Check the relationship with is_base_of. If it's not a base of, it can't be a virtual base of.
+ { static_assert(!std::is_base_of_v<Base, Derived> ? !std::is_virtual_base_of_v<Base, Derived> : true); }
+
+ // Make sure they can be referenced at runtime
+ {
+ bool const& a = std::is_virtual_base_of<Base, Derived>::value;
+ bool const& b = std::is_virtual_base_of_v<Base, Derived>;
+ assert(a == expected);
+ assert(b == expected);
+ }
+}
+
+struct Incomplete;
+struct Unrelated {};
+union IncompleteUnion;
+union Union {
+ int i;
+ float f;
+};
+
+class Base {};
+class Derived : Base {};
+class Derived2 : Base {};
+class Derived2a : Derived {};
+class Derived2b : Derived {};
+class Derived3Virtual : virtual Derived2a, virtual Derived2b {};
+
+struct DerivedTransitiveViaNonVirtual : Derived3Virtual {};
+struct DerivedTransitiveViaVirtual : virtual Derived3Virtual {};
+
+template <typename T>
+struct CrazyDerived : T {};
+template <typename T>
+struct CrazyDerivedVirtual : virtual T {};
+
+struct DerivedPrivate : private virtual Base {};
+struct DerivedProtected : protected virtual Base {};
+struct DerivedPrivatePrivate : private DerivedPrivate {};
+struct DerivedPrivateProtected : private DerivedProtected {};
+struct DerivedProtectedPrivate : protected DerivedProtected {};
+struct DerivedProtectedProtected : protected DerivedProtected {};
+struct DerivedTransitivePrivate : private Derived, private Derived2 {};
+
+int main(int, char**) {
+ // Test with non-virtual inheritance
+ {
+ test<false, Base, Base>();
+ test<false, Base, Derived>();
+ test<false, Base, Derived2>();
+ test<false, Derived, DerivedTransitivePrivate>();
+ test<false, Derived, Base>();
+ test<false, Incomplete, Derived>();
+
+ // Derived must be a complete type if Base and Derived are non-union class types
+ // test<false, Base, Incomplete>();
+ }
+
+ // Test with virtual inheritance
+ {
+ test<false, Base, Derived3Virtual>();
+ test<false, Derived, Derived3Virtual>();
+ test<true, Derived2b, Derived3Virtual>();
+ test<true, Derived2a, Derived3Virtual>();
+ test<true, Base, DerivedPrivate>();
+ test<true, Base, DerivedProtected>();
+ test<true, Base, DerivedPrivatePrivate>();
+ test<true, Base, DerivedPrivateProtected>();
+ test<true, Base, DerivedProtectedPrivate>();
+ test<true, Base, DerivedProtectedProtected>();
+ test<true, Derived2a, DerivedTransitiveViaNonVirtual>();
+ test<true, Derived2b, DerivedTransitiveViaNonVirtual>();
+ test<true, Derived2a, DerivedTransitiveViaVirtual>();
+ test<true, Derived2b, DerivedTransitiveViaVirtual>();
+ test<false, Base, CrazyDerived<Base>>();
+ test<false, CrazyDerived<Base>, Base>();
+ test<true, Base, CrazyDerivedVirtual<Base>>();
+ test<false, CrazyDerivedVirtual<Base>, Base>();
+ }
+
+ // Test unrelated types
+ {
+ test<false, Base&, Derived&>();
+ test<false, Base[3], Derived[3]>();
+ test<false, Unrelated, Derived>();
+ test<false, Base, Unrelated>();
+ test<false, Base, void>();
+ test<false, void, Derived>();
+ }
+
+ // Test scalar types
+ {
+ test<false, int, Base>();
+ test<false, int, Derived>();
+ test<false, int, Incomplete>();
+ test<false, int, int>();
+
+ test<false, Base, int>();
+ test<false, Derived, int>();
+ test<false, Incomplete, int>();
+
+ test<false, int[], int[]>();
+ test<false, long, int>();
+ test<false, int, long>();
+ }
+
+ // Test unions
+ {
+ test<false, Union, Union>();
+ test<false, IncompleteUnion, IncompleteUnion>();
+ test<false, Union, IncompleteUnion>();
+ test<false, IncompleteUnion, Union>();
+ test<false, Incomplete, IncompleteUnion>();
+ test<false, IncompleteUnion, Incomplete>();
+ test<false, Unrelated, IncompleteUnion>();
+ test<false, IncompleteUnion, Unrelated>();
+ test<false, int, IncompleteUnion>();
+ test<false, IncompleteUnion, int>();
+ test<false, Unrelated, Union>();
+ test<false, Union, Unrelated>();
+ test<false, int, Unrelated>();
+ test<false, Union, int>();
+ }
+
+ return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index b041b08f02aac5..f402d4de2275e5 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -784,7 +784,8 @@ def add_version_header(tc):
"c++26": 202406 # P2985R0 A type trait for detecting virtual base classes
},
"headers": ["type_traits"],
- "unimplemented": True,
+ "test_suite_guard": "__has_builtin(__builtin_is_virtual_base_of)",
+ "libcxx_guard": "__has_builtin(__builtin_is_virtual_base_of)",
},
{
"name": "__cpp_lib_is_within_lifetime",
More information about the libcxx-commits
mailing list