Skip to content

Commit 77ac365

Browse files
committed
[libc++] Fix ODR violation with placeholders
In D145589, we made the std::bind placeholders inline constexpr to satisfy C++17. It turns out that this causes ODR violations since the shared library provides strong definitions for those placeholders, and the linker on Windows actually complains about this. Fortunately, C++17 only encourages implementations to use `inline constexpr`, it doesn't force them. So instead, we unconditionally define the placeholders as `extern const`, which avoids the ODR violation and is indistinguishable from `inline constexpr` for most purposes, since the placeholders are empty types anyway. Note that we could also go back to the pre-D145589 state of defining them as non-inline constexpr variables in C++17, however that is definitely non-conforming since that means the placeholders have different addresses in different TUs. This is all a bit pedantic, but all in all I feel that `extern const` provides the best bang for our buck, and I can't really find any downsides to that solution. Differential Revision: https://reviews.llvm.org/D149292
1 parent 6d0ca5a commit 77ac365

File tree

2 files changed

+65
-96
lines changed

2 files changed

+65
-96
lines changed

libcxx/include/__functional/bind.h

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@ namespace placeholders
5151

5252
template <int _Np> struct __ph {};
5353

54-
#if defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_BUILDING_LIBRARY)
54+
// C++17 recommends that we implement placeholders as `inline constexpr`, but allows
55+
// implementing them as `extern <implementation-defined>`. Libc++ implements them as
56+
// `extern const` in all standard modes to avoid an ABI break in C++03: making them
57+
// `inline constexpr` requires removing their definition in the shared library to
58+
// avoid ODR violations, which is an ABI break.
59+
//
60+
// In practice, since placeholders are empty, `extern const` is almost impossible
61+
// to distinguish from `inline constexpr` from a usage stand point.
5562
_LIBCPP_FUNC_VIS extern const __ph<1> _1;
5663
_LIBCPP_FUNC_VIS extern const __ph<2> _2;
5764
_LIBCPP_FUNC_VIS extern const __ph<3> _3;
@@ -62,29 +69,6 @@ _LIBCPP_FUNC_VIS extern const __ph<7> _7;
6269
_LIBCPP_FUNC_VIS extern const __ph<8> _8;
6370
_LIBCPP_FUNC_VIS extern const __ph<9> _9;
6471
_LIBCPP_FUNC_VIS extern const __ph<10> _10;
65-
#elif _LIBCPP_STD_VER >= 17
66-
inline constexpr __ph<1> _1{};
67-
inline constexpr __ph<2> _2{};
68-
inline constexpr __ph<3> _3{};
69-
inline constexpr __ph<4> _4{};
70-
inline constexpr __ph<5> _5{};
71-
inline constexpr __ph<6> _6{};
72-
inline constexpr __ph<7> _7{};
73-
inline constexpr __ph<8> _8{};
74-
inline constexpr __ph<9> _9{};
75-
inline constexpr __ph<10> _10{};
76-
#else
77-
constexpr __ph<1> _1{};
78-
constexpr __ph<2> _2{};
79-
constexpr __ph<3> _3{};
80-
constexpr __ph<4> _4{};
81-
constexpr __ph<5> _5{};
82-
constexpr __ph<6> _6{};
83-
constexpr __ph<7> _7{};
84-
constexpr __ph<8> _8{};
85-
constexpr __ph<9> _9{};
86-
constexpr __ph<10> _10{};
87-
#endif // defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_BUILDING_LIBRARY)
8872

8973
} // namespace placeholders
9074

libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.place/placeholders.pass.cpp

Lines changed: 57 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -8,91 +8,76 @@
88

99
// <functional>
1010

11-
// placeholders
12-
// The placeholders are constexpr in C++17 and beyond.
13-
// libc++ provides constexpr placeholders in C++11 and beyond.
11+
// namespace placeholders {
12+
// // M is the implementation-defined number of placeholders
13+
// extern unspecified _1;
14+
// extern unspecified _2;
15+
// .
16+
// .
17+
// .
18+
// extern unspecified _Mp;
19+
// }
20+
21+
// The Standard recommends implementing them as `inline constexpr` in C++17.
22+
//
23+
// Libc++ implements the placeholders as `extern const` in all standard modes
24+
// to avoid an ABI break in C++03: making them `inline constexpr` requires removing
25+
// their definition in the shared library to avoid ODR violations, which is an
26+
// ABI break.
27+
//
28+
// Concretely, `extern const` is almost indistinguishable from constexpr for the
29+
// placeholders since they are empty types.
1430

1531
#include <functional>
1632
#include <type_traits>
1733

1834
#include "test_macros.h"
1935

2036
template <class T>
21-
void
22-
test(const T& t)
23-
{
24-
// Test default constructible.
25-
T t2;
26-
((void)t2);
27-
// Test copy constructible.
28-
T t3 = t;
29-
((void)t3);
37+
TEST_CONSTEXPR_CXX17 void test(const T& t) {
38+
// Test default constructible.
39+
{
40+
T x; (void)x;
41+
}
42+
43+
// Test copy constructible.
44+
{
45+
T x = t; (void)x;
3046
static_assert(std::is_nothrow_copy_constructible<T>::value, "");
3147
static_assert(std::is_nothrow_move_constructible<T>::value, "");
32-
}
48+
}
3349

34-
#if TEST_STD_VER >= 11
35-
constexpr decltype(std::placeholders::_1) default1{};
36-
constexpr decltype(std::placeholders::_2) default2{};
37-
constexpr decltype(std::placeholders::_3) default3{};
38-
constexpr decltype(std::placeholders::_4) default4{};
39-
constexpr decltype(std::placeholders::_5) default5{};
40-
constexpr decltype(std::placeholders::_6) default6{};
41-
constexpr decltype(std::placeholders::_7) default7{};
42-
constexpr decltype(std::placeholders::_8) default8{};
43-
constexpr decltype(std::placeholders::_9) default9{};
44-
constexpr decltype(std::placeholders::_10) default10{};
45-
46-
constexpr decltype(std::placeholders::_1) cp1 = std::placeholders::_1;
47-
constexpr decltype(std::placeholders::_2) cp2 = std::placeholders::_2;
48-
constexpr decltype(std::placeholders::_3) cp3 = std::placeholders::_3;
49-
constexpr decltype(std::placeholders::_4) cp4 = std::placeholders::_4;
50-
constexpr decltype(std::placeholders::_5) cp5 = std::placeholders::_5;
51-
constexpr decltype(std::placeholders::_6) cp6 = std::placeholders::_6;
52-
constexpr decltype(std::placeholders::_7) cp7 = std::placeholders::_7;
53-
constexpr decltype(std::placeholders::_8) cp8 = std::placeholders::_8;
54-
constexpr decltype(std::placeholders::_9) cp9 = std::placeholders::_9;
55-
constexpr decltype(std::placeholders::_10) cp10 = std::placeholders::_10;
56-
#endif // TEST_STD_VER >= 11
57-
58-
void use_placeholders_to_prevent_unused_warning() {
59-
#if TEST_STD_VER >= 11
60-
((void)cp1);
61-
((void)cp2);
62-
((void)cp3);
63-
((void)cp4);
64-
((void)cp5);
65-
((void)cp6);
66-
((void)cp7);
67-
((void)cp8);
68-
((void)cp9);
69-
((void)cp10);
70-
((void)default1);
71-
((void)default2);
72-
((void)default3);
73-
((void)default4);
74-
((void)default5);
75-
((void)default6);
76-
((void)default7);
77-
((void)default8);
78-
((void)default9);
79-
((void)default10);
50+
// It is implementation-defined whether placeholder types are CopyAssignable.
51+
// CopyAssignable placeholders' copy assignment operators shall not throw exceptions.
52+
#ifdef _LIBCPP_VERSION
53+
{
54+
T x;
55+
x = t;
56+
static_assert(std::is_nothrow_copy_assignable<T>::value, "");
57+
static_assert(std::is_nothrow_move_assignable<T>::value, "");
58+
}
8059
#endif
8160
}
8261

83-
int main(int, char**)
84-
{
85-
use_placeholders_to_prevent_unused_warning();
86-
test(std::placeholders::_1);
87-
test(std::placeholders::_2);
88-
test(std::placeholders::_3);
89-
test(std::placeholders::_4);
90-
test(std::placeholders::_5);
91-
test(std::placeholders::_6);
92-
test(std::placeholders::_7);
93-
test(std::placeholders::_8);
94-
test(std::placeholders::_9);
95-
test(std::placeholders::_10);
62+
TEST_CONSTEXPR_CXX17 bool test_all() {
63+
test(std::placeholders::_1);
64+
test(std::placeholders::_2);
65+
test(std::placeholders::_3);
66+
test(std::placeholders::_4);
67+
test(std::placeholders::_5);
68+
test(std::placeholders::_6);
69+
test(std::placeholders::_7);
70+
test(std::placeholders::_8);
71+
test(std::placeholders::_9);
72+
test(std::placeholders::_10);
73+
return true;
74+
}
75+
76+
int main(int, char**) {
77+
test_all();
78+
#if TEST_STD_VER >= 17
79+
static_assert(test_all(), "");
80+
#endif
9681

9782
return 0;
9883
}

0 commit comments

Comments
 (0)