Skip to content

Commit bba4e08

Browse files
committed
[libc++] Workaround clang bug in __has_unique_object_representations
Clang currently has a bug in the __has_unique_object_representations builtin where it doesn't provide consistent answers based on the order of instantiation of templates. This was reported as #95311. This patch adds a workaround in libc++ to avoid breaking users until Clang has been fixed. It also revamps the tests a bit.
1 parent 7b80384 commit bba4e08

File tree

2 files changed

+79
-73
lines changed

2 files changed

+79
-73
lines changed

libcxx/include/__type_traits/has_unique_object_representation.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <__config>
1313
#include <__type_traits/integral_constant.h>
14+
#include <__type_traits/remove_all_extents.h>
1415

1516
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
1617
# pragma GCC system_header
@@ -22,7 +23,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2223

2324
template <class _Tp>
2425
struct _LIBCPP_TEMPLATE_VIS has_unique_object_representations
25-
: public integral_constant<bool, __has_unique_object_representations(_Tp)> {};
26+
// TODO: We work around a Clang bug in __has_unique_object_representations by instantiating the
27+
// builtin on the non-array type first and discarding that. This is issue #95311.
28+
// This workaround can be removed once the bug has been fixed in all supported Clangs.
29+
: public integral_constant<bool,
30+
((void)__has_unique_object_representations(remove_all_extents_t<_Tp>),
31+
__has_unique_object_representations(_Tp))> {};
2632

2733
template <class _Tp>
2834
inline constexpr bool has_unique_object_representations_v = __has_unique_object_representations(_Tp);

libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/has_unique_object_representations.pass.cpp

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -14,95 +14,95 @@
1414

1515
#include <type_traits>
1616

17-
#include "test_macros.h"
18-
19-
template <class T>
20-
void test_has_unique_object_representations()
21-
{
22-
static_assert( std::has_unique_object_representations<T>::value, "");
23-
static_assert( std::has_unique_object_representations<const T>::value, "");
24-
static_assert( std::has_unique_object_representations<volatile T>::value, "");
25-
static_assert( std::has_unique_object_representations<const volatile T>::value, "");
26-
27-
static_assert( std::has_unique_object_representations_v<T>, "");
28-
static_assert( std::has_unique_object_representations_v<const T>, "");
29-
static_assert( std::has_unique_object_representations_v<volatile T>, "");
30-
static_assert( std::has_unique_object_representations_v<const volatile T>, "");
17+
template <bool ExpectedValue, class T>
18+
void test() {
19+
static_assert(std::has_unique_object_representations<T>::value == ExpectedValue);
20+
static_assert(std::has_unique_object_representations<const T>::value == ExpectedValue);
21+
static_assert(std::has_unique_object_representations<volatile T>::value == ExpectedValue);
22+
static_assert(std::has_unique_object_representations<const volatile T>::value == ExpectedValue);
23+
24+
static_assert(std::has_unique_object_representations_v<T> == ExpectedValue);
25+
static_assert(std::has_unique_object_representations_v<const T> == ExpectedValue);
26+
static_assert(std::has_unique_object_representations_v<volatile T> == ExpectedValue);
27+
static_assert(std::has_unique_object_representations_v<const volatile T> == ExpectedValue);
3128
}
3229

33-
template <class T>
34-
void test_has_not_has_unique_object_representations()
35-
{
36-
static_assert(!std::has_unique_object_representations<T>::value, "");
37-
static_assert(!std::has_unique_object_representations<const T>::value, "");
38-
static_assert(!std::has_unique_object_representations<volatile T>::value, "");
39-
static_assert(!std::has_unique_object_representations<const volatile T>::value, "");
40-
41-
static_assert(!std::has_unique_object_representations_v<T>, "");
42-
static_assert(!std::has_unique_object_representations_v<const T>, "");
43-
static_assert(!std::has_unique_object_representations_v<volatile T>, "");
44-
static_assert(!std::has_unique_object_representations_v<const volatile T>, "");
45-
}
46-
47-
class Empty
48-
{
49-
};
50-
51-
class NotEmpty
52-
{
53-
virtual ~NotEmpty();
54-
};
30+
class Empty {};
5531

5632
union EmptyUnion {};
57-
struct NonEmptyUnion {int x; unsigned y;};
5833

59-
struct bit_zero
60-
{
61-
int : 0;
34+
struct NonEmptyUnion {
35+
int x;
36+
unsigned y;
6237
};
6338

64-
class Abstract
65-
{
66-
virtual ~Abstract() = 0;
39+
struct ZeroWidthBitfield {
40+
int : 0;
6741
};
6842

69-
struct A
70-
{
71-
~A();
72-
unsigned foo;
43+
class Virtual {
44+
virtual ~Virtual();
7345
};
7446

75-
struct B
76-
{
77-
char bar;
78-
int foo;
47+
class Abstract {
48+
virtual ~Abstract() = 0;
7949
};
8050

51+
struct UnsignedInt {
52+
unsigned foo;
53+
};
8154

82-
int main(int, char**)
83-
{
84-
test_has_not_has_unique_object_representations<void>();
85-
test_has_not_has_unique_object_representations<Empty>();
86-
test_has_not_has_unique_object_representations<EmptyUnion>();
87-
test_has_not_has_unique_object_representations<NotEmpty>();
88-
test_has_not_has_unique_object_representations<bit_zero>();
89-
test_has_not_has_unique_object_representations<Abstract>();
90-
test_has_not_has_unique_object_representations<B>();
91-
92-
// I would expect all three of these to have unique representations.
93-
// I would also expect that there are systems where they do not.
94-
// test_has_not_has_unique_object_representations<int&>();
95-
// test_has_not_has_unique_object_representations<int *>();
96-
// test_has_not_has_unique_object_representations<double>();
55+
struct WithoutPadding {
56+
int x;
57+
int y;
58+
};
9759

60+
struct WithPadding {
61+
char bar;
62+
int foo;
63+
};
9864

99-
test_has_unique_object_representations<unsigned>();
100-
test_has_unique_object_representations<NonEmptyUnion>();
101-
test_has_unique_object_representations<char[3]>();
102-
test_has_unique_object_representations<char[3][4]>();
103-
test_has_unique_object_representations<char[3][4][5]>();
104-
test_has_unique_object_representations<char[]>();
65+
template <int>
66+
class NTTP_ClassType_WithoutPadding {
67+
int x;
68+
};
10569

70+
int main(int, char**) {
71+
test<false, void>();
72+
test<false, Empty>();
73+
test<false, EmptyUnion>();
74+
test<false, Virtual>();
75+
test<false, ZeroWidthBitfield>();
76+
test<false, Abstract>();
77+
test<false, WithPadding>();
78+
test<false, WithPadding[]>();
79+
test<false, WithPadding[][3]>();
80+
81+
// I would also expect that there are systems where they do not.
82+
// I would expect all three of these to have unique representations.
83+
// test<false, int&>();
84+
// test<false, int *>();
85+
// test<false, double>();
86+
87+
test<true, unsigned>();
88+
test<true, UnsignedInt>();
89+
test<true, WithoutPadding>();
90+
test<true, NonEmptyUnion>();
91+
test<true, char[3]>();
92+
test<true, char[3][4]>();
93+
test<true, char[3][4][5]>();
94+
test<true, char[]>();
95+
test<true, char[][2]>();
96+
test<true, char[][2][3]>();
97+
98+
// Important test case for https://github.com/llvm/llvm-project/issues/95311.
99+
// Note that the order is important here, we want to instantiate the array
100+
// variants before the non-array ones, otherwise we don't trigger the bug.
101+
{
102+
test<true, NTTP_ClassType_WithoutPadding<0>[]>();
103+
test<true, NTTP_ClassType_WithoutPadding<0>[][3]>();
104+
test<true, NTTP_ClassType_WithoutPadding<0>>();
105+
}
106106

107107
return 0;
108108
}

0 commit comments

Comments
 (0)