Skip to content

Commit 50e9744

Browse files
ldionneAlexisPerry
authored andcommitted
[libc++] Workaround clang bug in __has_unique_object_representations (llvm#95314)
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 llvm#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 e30362d commit 50e9744

File tree

3 files changed

+113
-109
lines changed

3 files changed

+113
-109
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 and GCC bug in __has_unique_object_representations by using remove_all_extents
27+
// even though it should not be necessary. This was reported to the compilers:
28+
// - Clang: https://github.com/llvm/llvm-project/issues/95311
29+
// - GCC: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115476
30+
// remove_all_extents_t can be removed once all the compilers we support have fixed this bug.
31+
: public integral_constant<bool, __has_unique_object_representations(remove_all_extents_t<_Tp>)> {};
2632

2733
template <class _Tp>
2834
inline constexpr bool has_unique_object_representations_v = __has_unique_object_representations(_Tp);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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
10+
11+
// type_traits
12+
13+
// has_unique_object_representations
14+
15+
#include <type_traits>
16+
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);
28+
}
29+
30+
class Empty {};
31+
32+
union EmptyUnion {};
33+
34+
struct NonEmptyUnion {
35+
int x;
36+
unsigned y;
37+
};
38+
39+
struct ZeroWidthBitfield {
40+
int : 0;
41+
};
42+
43+
class Virtual {
44+
virtual ~Virtual();
45+
};
46+
47+
class Abstract {
48+
virtual ~Abstract() = 0;
49+
};
50+
51+
struct UnsignedInt {
52+
unsigned foo;
53+
};
54+
55+
struct WithoutPadding {
56+
int x;
57+
int y;
58+
};
59+
60+
struct WithPadding {
61+
char bar;
62+
int foo;
63+
};
64+
65+
template <int>
66+
class NTTP_ClassType_WithoutPadding {
67+
int x;
68+
};
69+
70+
void test() {
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+
}
106+
}

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

Lines changed: 0 additions & 108 deletions
This file was deleted.

0 commit comments

Comments
 (0)