diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index a1506e115fe70..f6d3142c1e2d3 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 fe9f4c1973cdb..bc28f380945bc 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 `__) Improvements and New Features diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index dd62bcc2555ff..d95cb11f483c0 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -68,7 +68,7 @@ "`P2389R2 `__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19.0","" "`P3168R2 `__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","","|ranges|" "`P3217R0 `__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","","" -"`P2985R0 `__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","","","" +"`P2985R0 `__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20.0","" "`P0843R14 `__","``inplace_vector``","2024-06 (St. Louis)","","","" "`P3235R3 `__","``std::print`` more types faster with less memory","2024-06 (St. Louis)","","","|format| |DR|" "`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 090abeeb54dcc..488b63719eb60 100644 --- a/libcxx/include/__type_traits/is_base_of.h +++ b/libcxx/include/__type_traits/is_base_of.h @@ -26,6 +26,18 @@ template 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 +struct _LIBCPP_TEMPLATE_VIS is_virtual_base_of : public bool_constant<__builtin_is_virtual_base_of(_Base, _Derived)> {}; + +template +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 7f231cd09df51..5937d4fdc9e1a 100644 --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -144,6 +144,7 @@ namespace std // Relationships between types: template struct is_same; template struct is_base_of; + template struct is_virtual_base_of; // C++26 template struct is_convertible; template struct is_nothrow_convertible; // C++20 @@ -391,6 +392,8 @@ namespace std = is_same::value; // C++17 template inline constexpr bool is_base_of_v = is_base_of::value; // C++17 + template inline constexpr bool is_virtual_base_of_v + = is_virtual_base_of::value; // C++26 template inline constexpr bool is_convertible_v = is_convertible::value; // C++17 template inline constexpr bool is_invocable_v diff --git a/libcxx/include/version b/libcxx/include/version index fe64343eafbc9..a19be2d294afd 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -531,7 +531,9 @@ __cpp_lib_void_t 201411L // # 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 bb69ca7368aaf..1cbf2699a95bc 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 b8bad696f1bae..bd2959d55dc20 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 0000000000000..6b34d56e2c6f4 --- /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 + +// + +// std::is_virtual_base_of + +#include +#include + +template +void test() { + // Test the type of the variables + { + static_assert(std::is_same_v::value)>); + static_assert(std::is_same_v)>); + } + + // Test their value + { + static_assert(std::is_virtual_base_of::value == expected); + static_assert(std::is_virtual_base_of::value == expected); + static_assert(std::is_virtual_base_of::value == expected); + static_assert(std::is_virtual_base_of::value == expected); + + static_assert(std::is_virtual_base_of_v == expected); + static_assert(std::is_virtual_base_of_v == expected); + static_assert(std::is_virtual_base_of_v == expected); + static_assert(std::is_virtual_base_of_v == 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 ? !std::is_virtual_base_of_v : true); } + + // Make sure they can be referenced at runtime + { + bool const& a = std::is_virtual_base_of::value; + bool const& b = std::is_virtual_base_of_v; + 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 +struct CrazyDerived : T {}; +template +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(); + test(); + test(); + test(); + test(); + test(); + + // Derived must be a complete type if Base and Derived are non-union class types + // test(); + } + + // Test with virtual inheritance + { + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test>(); + test, Base>(); + test>(); + test, Base>(); + } + + // Test unrelated types + { + test(); + test(); + test(); + test(); + test(); + test(); + } + + // Test scalar types + { + test(); + test(); + test(); + test(); + + test(); + test(); + test(); + + test(); + test(); + test(); + } + + // Test unions + { + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + } + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index b041b08f02aac..f402d4de2275e 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",