Skip to content

Commit 1c48c9c

Browse files
authored
[libc++] Implement P2985R0: std::is_virtual_base_of (llvm#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 llvm#105432
1 parent ca95bee commit 1c48c9c

File tree

10 files changed

+195
-11
lines changed

10 files changed

+195
-11
lines changed

libcxx/docs/FeatureTestMacroTable.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ Status
442442
---------------------------------------------------------- -----------------
443443
``__cpp_lib_inplace_vector`` *unimplemented*
444444
---------------------------------------------------------- -----------------
445-
``__cpp_lib_is_virtual_base_of`` *unimplemented*
445+
``__cpp_lib_is_virtual_base_of`` ``202406L``
446446
---------------------------------------------------------- -----------------
447447
``__cpp_lib_is_within_lifetime`` *unimplemented*
448448
---------------------------------------------------------- -----------------

libcxx/docs/ReleaseNotes/20.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ What's New in Libc++ 20.0.0?
3838
Implemented Papers
3939
------------------
4040

41-
- TODO
41+
- P2985R0: A type trait for detecting virtual base classes (`Github <https://github.com/llvm/llvm-project/issues/105432>`__)
4242

4343

4444
Improvements and New Features

libcxx/docs/Status/Cxx2cPapers.csv

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
"`P2389R2 <https://wg21.link/P2389R2>`__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19.0",""
6969
"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","","|ranges|"
7070
"`P3217R0 <https://wg21.link/P3217R0>`__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","",""
71-
"`P2985R0 <https://wg21.link/P2985R0>`__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","","",""
71+
"`P2985R0 <https://wg21.link/P2985R0>`__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20.0",""
7272
"`P0843R14 <https://wg21.link/P0843R14>`__","``inplace_vector``","2024-06 (St. Louis)","","",""
7373
"`P3235R3 <https://wg21.link/P3235R3>`__","``std::print`` more types faster with less memory","2024-06 (St. Louis)","","","|format| |DR|"
7474
"`P2968R2 <https://wg21.link/P2968R2>`__","Make ``std::ignore`` a first-class object","2024-06 (St. Louis)","|Complete|","19.0",""

libcxx/include/__type_traits/is_base_of.h

+12
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ template <class _Bp, class _Dp>
2626
inline constexpr bool is_base_of_v = __is_base_of(_Bp, _Dp);
2727
#endif
2828

29+
#if _LIBCPP_STD_VER >= 26
30+
# if __has_builtin(__builtin_is_virtual_base_of)
31+
32+
template <class _Base, class _Derived>
33+
struct _LIBCPP_TEMPLATE_VIS is_virtual_base_of : public bool_constant<__builtin_is_virtual_base_of(_Base, _Derived)> {};
34+
35+
template <class _Base, class _Derived>
36+
inline constexpr bool is_virtual_base_of_v = __builtin_is_virtual_base_of(_Base, _Derived);
37+
38+
# endif
39+
#endif
40+
2941
_LIBCPP_END_NAMESPACE_STD
3042

3143
#endif // _LIBCPP___TYPE_TRAITS_IS_BASE_OF_H

libcxx/include/type_traits

+3
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ namespace std
144144
// Relationships between types:
145145
template <class T, class U> struct is_same;
146146
template <class Base, class Derived> struct is_base_of;
147+
template <class Base, class Derived> struct is_virtual_base_of; // C++26
147148
148149
template <class From, class To> struct is_convertible;
149150
template <typename From, typename To> struct is_nothrow_convertible; // C++20
@@ -391,6 +392,8 @@ namespace std
391392
= is_same<T, U>::value; // C++17
392393
template <class Base, class Derived> inline constexpr bool is_base_of_v
393394
= is_base_of<Base, Derived>::value; // C++17
395+
template <class Base, class Derived> inline constexpr bool is_virtual_base_of_v
396+
= is_virtual_base_of<Base, Derived>::value; // C++26
394397
template <class From, class To> inline constexpr bool is_convertible_v
395398
= is_convertible<From, To>::value; // C++17
396399
template <class Fn, class... ArgTypes> inline constexpr bool is_invocable_v

libcxx/include/version

+3-1
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,9 @@ __cpp_lib_void_t 201411L <type_traits>
531531
// # define __cpp_lib_generate_random 202403L
532532
// # define __cpp_lib_hazard_pointer 202306L
533533
// # define __cpp_lib_inplace_vector 202406L
534-
// # define __cpp_lib_is_virtual_base_of 202406L
534+
# if __has_builtin(__builtin_is_virtual_base_of)
535+
# define __cpp_lib_is_virtual_base_of 202406L
536+
# endif
535537
// # define __cpp_lib_is_within_lifetime 202306L
536538
// # define __cpp_lib_linalg 202311L
537539
# undef __cpp_lib_mdspan

libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -857,16 +857,16 @@
857857
# error "__cpp_lib_is_swappable should have the value 201603L in c++26"
858858
# endif
859859

860-
# if !defined(_LIBCPP_VERSION)
860+
# if __has_builtin(__builtin_is_virtual_base_of)
861861
# ifndef __cpp_lib_is_virtual_base_of
862862
# error "__cpp_lib_is_virtual_base_of should be defined in c++26"
863863
# endif
864864
# if __cpp_lib_is_virtual_base_of != 202406L
865865
# error "__cpp_lib_is_virtual_base_of should have the value 202406L in c++26"
866866
# endif
867-
# else // _LIBCPP_VERSION
867+
# else
868868
# ifdef __cpp_lib_is_virtual_base_of
869-
# error "__cpp_lib_is_virtual_base_of should not be defined because it is unimplemented in libc++!"
869+
# error "__cpp_lib_is_virtual_base_of should not be defined when the requirement '__has_builtin(__builtin_is_virtual_base_of)' is not met!"
870870
# endif
871871
# endif
872872

libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -7172,16 +7172,16 @@
71727172
# error "__cpp_lib_is_swappable should have the value 201603L in c++26"
71737173
# endif
71747174

7175-
# if !defined(_LIBCPP_VERSION)
7175+
# if __has_builtin(__builtin_is_virtual_base_of)
71767176
# ifndef __cpp_lib_is_virtual_base_of
71777177
# error "__cpp_lib_is_virtual_base_of should be defined in c++26"
71787178
# endif
71797179
# if __cpp_lib_is_virtual_base_of != 202406L
71807180
# error "__cpp_lib_is_virtual_base_of should have the value 202406L in c++26"
71817181
# endif
7182-
# else // _LIBCPP_VERSION
7182+
# else
71837183
# ifdef __cpp_lib_is_virtual_base_of
7184-
# error "__cpp_lib_is_virtual_base_of should not be defined because it is unimplemented in libc++!"
7184+
# error "__cpp_lib_is_virtual_base_of should not be defined when the requirement '__has_builtin(__builtin_is_virtual_base_of)' is not met!"
71857185
# endif
71867186
# endif
71877187

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
10+
11+
// These compilers don't support __builtin_is_virtual_base_of yet.
12+
// UNSUPPORTED: clang-17, clang-18, clang-19, gcc-14, apple-clang-16, apple-clang-17
13+
14+
// <type_traits>
15+
16+
// std::is_virtual_base_of
17+
18+
#include <type_traits>
19+
#include <cassert>
20+
21+
template <bool expected, class Base, class Derived>
22+
void test() {
23+
// Test the type of the variables
24+
{
25+
static_assert(std::is_same_v<bool const, decltype(std::is_virtual_base_of<Base, Derived>::value)>);
26+
static_assert(std::is_same_v<bool const, decltype(std::is_virtual_base_of_v<Base, Derived>)>);
27+
}
28+
29+
// Test their value
30+
{
31+
static_assert(std::is_virtual_base_of<Base, Derived>::value == expected);
32+
static_assert(std::is_virtual_base_of<const Base, Derived>::value == expected);
33+
static_assert(std::is_virtual_base_of<Base, const Derived>::value == expected);
34+
static_assert(std::is_virtual_base_of<const Base, const Derived>::value == expected);
35+
36+
static_assert(std::is_virtual_base_of_v<Base, Derived> == expected);
37+
static_assert(std::is_virtual_base_of_v<const Base, Derived> == expected);
38+
static_assert(std::is_virtual_base_of_v<Base, const Derived> == expected);
39+
static_assert(std::is_virtual_base_of_v<const Base, const Derived> == expected);
40+
}
41+
42+
// Check the relationship with is_base_of. If it's not a base of, it can't be a virtual base of.
43+
{ static_assert(!std::is_base_of_v<Base, Derived> ? !std::is_virtual_base_of_v<Base, Derived> : true); }
44+
45+
// Make sure they can be referenced at runtime
46+
{
47+
bool const& a = std::is_virtual_base_of<Base, Derived>::value;
48+
bool const& b = std::is_virtual_base_of_v<Base, Derived>;
49+
assert(a == expected);
50+
assert(b == expected);
51+
}
52+
}
53+
54+
struct Incomplete;
55+
struct Unrelated {};
56+
union IncompleteUnion;
57+
union Union {
58+
int i;
59+
float f;
60+
};
61+
62+
class Base {};
63+
class Derived : Base {};
64+
class Derived2 : Base {};
65+
class Derived2a : Derived {};
66+
class Derived2b : Derived {};
67+
class Derived3Virtual : virtual Derived2a, virtual Derived2b {};
68+
69+
struct DerivedTransitiveViaNonVirtual : Derived3Virtual {};
70+
struct DerivedTransitiveViaVirtual : virtual Derived3Virtual {};
71+
72+
template <typename T>
73+
struct CrazyDerived : T {};
74+
template <typename T>
75+
struct CrazyDerivedVirtual : virtual T {};
76+
77+
struct DerivedPrivate : private virtual Base {};
78+
struct DerivedProtected : protected virtual Base {};
79+
struct DerivedPrivatePrivate : private DerivedPrivate {};
80+
struct DerivedPrivateProtected : private DerivedProtected {};
81+
struct DerivedProtectedPrivate : protected DerivedProtected {};
82+
struct DerivedProtectedProtected : protected DerivedProtected {};
83+
struct DerivedTransitivePrivate : private Derived, private Derived2 {};
84+
85+
int main(int, char**) {
86+
// Test with non-virtual inheritance
87+
{
88+
test<false, Base, Base>();
89+
test<false, Base, Derived>();
90+
test<false, Base, Derived2>();
91+
test<false, Derived, DerivedTransitivePrivate>();
92+
test<false, Derived, Base>();
93+
test<false, Incomplete, Derived>();
94+
95+
// Derived must be a complete type if Base and Derived are non-union class types
96+
// test<false, Base, Incomplete>();
97+
}
98+
99+
// Test with virtual inheritance
100+
{
101+
test<false, Base, Derived3Virtual>();
102+
test<false, Derived, Derived3Virtual>();
103+
test<true, Derived2b, Derived3Virtual>();
104+
test<true, Derived2a, Derived3Virtual>();
105+
test<true, Base, DerivedPrivate>();
106+
test<true, Base, DerivedProtected>();
107+
test<true, Base, DerivedPrivatePrivate>();
108+
test<true, Base, DerivedPrivateProtected>();
109+
test<true, Base, DerivedProtectedPrivate>();
110+
test<true, Base, DerivedProtectedProtected>();
111+
test<true, Derived2a, DerivedTransitiveViaNonVirtual>();
112+
test<true, Derived2b, DerivedTransitiveViaNonVirtual>();
113+
test<true, Derived2a, DerivedTransitiveViaVirtual>();
114+
test<true, Derived2b, DerivedTransitiveViaVirtual>();
115+
test<false, Base, CrazyDerived<Base>>();
116+
test<false, CrazyDerived<Base>, Base>();
117+
test<true, Base, CrazyDerivedVirtual<Base>>();
118+
test<false, CrazyDerivedVirtual<Base>, Base>();
119+
}
120+
121+
// Test unrelated types
122+
{
123+
test<false, Base&, Derived&>();
124+
test<false, Base[3], Derived[3]>();
125+
test<false, Unrelated, Derived>();
126+
test<false, Base, Unrelated>();
127+
test<false, Base, void>();
128+
test<false, void, Derived>();
129+
}
130+
131+
// Test scalar types
132+
{
133+
test<false, int, Base>();
134+
test<false, int, Derived>();
135+
test<false, int, Incomplete>();
136+
test<false, int, int>();
137+
138+
test<false, Base, int>();
139+
test<false, Derived, int>();
140+
test<false, Incomplete, int>();
141+
142+
test<false, int[], int[]>();
143+
test<false, long, int>();
144+
test<false, int, long>();
145+
}
146+
147+
// Test unions
148+
{
149+
test<false, Union, Union>();
150+
test<false, IncompleteUnion, IncompleteUnion>();
151+
test<false, Union, IncompleteUnion>();
152+
test<false, IncompleteUnion, Union>();
153+
test<false, Incomplete, IncompleteUnion>();
154+
test<false, IncompleteUnion, Incomplete>();
155+
test<false, Unrelated, IncompleteUnion>();
156+
test<false, IncompleteUnion, Unrelated>();
157+
test<false, int, IncompleteUnion>();
158+
test<false, IncompleteUnion, int>();
159+
test<false, Unrelated, Union>();
160+
test<false, Union, Unrelated>();
161+
test<false, int, Unrelated>();
162+
test<false, Union, int>();
163+
}
164+
165+
return 0;
166+
}

libcxx/utils/generate_feature_test_macro_components.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,8 @@ def add_version_header(tc):
784784
"c++26": 202406 # P2985R0 A type trait for detecting virtual base classes
785785
},
786786
"headers": ["type_traits"],
787-
"unimplemented": True,
787+
"test_suite_guard": "__has_builtin(__builtin_is_virtual_base_of)",
788+
"libcxx_guard": "__has_builtin(__builtin_is_virtual_base_of)",
788789
},
789790
{
790791
"name": "__cpp_lib_is_within_lifetime",

0 commit comments

Comments
 (0)