[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