Skip to content

[libc++] Implement P2985R0: std::is_virtual_base_of #105847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 26, 2024

Conversation

ldionne
Copy link
Member

@ldionne ldionne commented Aug 23, 2024

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

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 llvm#105432
@ldionne ldionne requested a review from a team as a code owner August 23, 2024 15:38
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Aug 23, 2024
@llvmbot
Copy link
Member

llvmbot commented Aug 23, 2024

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/105847.diff

10 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1)
  • (modified) libcxx/docs/ReleaseNotes/20.rst (+1-1)
  • (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1)
  • (modified) libcxx/include/__type_traits/is_base_of.h (+13)
  • (modified) libcxx/include/type_traits (+3)
  • (modified) libcxx/include/version (+3-1)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp (+3-3)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+3-3)
  • (added) libcxx/test/std/utilities/meta/meta.rel/is_virtual_base_of.pass.cpp (+166)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+2-1)
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",

@ldionne ldionne merged commit 1c48c9c into llvm:main Aug 26, 2024
10 of 12 checks passed
@ldionne ldionne deleted the review/is_virtual_base_of branch August 26, 2024 13:58
H-G-Hristov added a commit to H-G-Hristov/llvm-project that referenced this pull request Sep 2, 2024
…e_traits.inc`

`std::is_virtual_base_of` was implemented in llvm#105847
ldionne pushed a commit that referenced this pull request Sep 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

P2985R0: A type trait for detecting virtual base classes
3 participants