Skip to content

Commit 140c68d

Browse files
philnik777tru
authored andcommitted
[libc++] Avoid ODR violations in __exception_guard
Having an ODR violation with `__exception_guard` seems to be problematic in LTO builds. To avoid the ODR violation, give the class different names for exception/no-exceptions mode and have an alias to the correct class. Reviewed By: ldionne, #libc, alexfh Spies: aeubanks, dblaikie, joanahalili, alexfh, rupprecht, libcxx-commits Differential Revision: https://reviews.llvm.org/D143071 (cherry picked from commit 1a17739)
1 parent a18482a commit 140c68d

File tree

5 files changed

+78
-26
lines changed

5 files changed

+78
-26
lines changed

libcxx/include/__expected/expected.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ class expected {
292292
"be reverted to the previous state in case an exception is thrown during the assignment.");
293293
_T2 __tmp(std::move(__oldval));
294294
std::destroy_at(std::addressof(__oldval));
295-
__exception_guard __trans([&] { std::construct_at(std::addressof(__oldval), std::move(__tmp)); });
295+
auto __trans =
296+
std::__make_exception_guard([&] { std::construct_at(std::addressof(__oldval), std::move(__tmp)); });
296297
std::construct_at(std::addressof(__newval), std::forward<_Args>(__args)...);
297298
__trans.__complete();
298299
}
@@ -451,7 +452,7 @@ class expected {
451452
if constexpr (is_nothrow_move_constructible_v<_Err>) {
452453
_Err __tmp(std::move(__with_err.__union_.__unex_));
453454
std::destroy_at(std::addressof(__with_err.__union_.__unex_));
454-
__exception_guard __trans([&] {
455+
auto __trans = std::__make_exception_guard([&] {
455456
std::construct_at(std::addressof(__with_err.__union_.__unex_), std::move(__tmp));
456457
});
457458
std::construct_at(std::addressof(__with_err.__union_.__val_), std::move(__with_val.__union_.__val_));
@@ -464,7 +465,7 @@ class expected {
464465
"that it can be reverted to the previous state in case an exception is thrown during swap.");
465466
_Tp __tmp(std::move(__with_val.__union_.__val_));
466467
std::destroy_at(std::addressof(__with_val.__union_.__val_));
467-
__exception_guard __trans([&] {
468+
auto __trans = std::__make_exception_guard([&] {
468469
std::construct_at(std::addressof(__with_val.__union_.__val_), std::move(__tmp));
469470
});
470471
std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__with_err.__union_.__unex_));

libcxx/include/__memory/uninitialized_algorithms.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* _
421421
_Tp& __array = *__loc;
422422

423423
// If an exception is thrown, destroy what we have constructed so far in reverse order.
424-
__exception_guard __guard([&]() {
424+
auto __guard = std::__make_exception_guard([&]() {
425425
std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
426426
});
427427

@@ -461,7 +461,7 @@ constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* _
461461
_Tp& __array = *__loc;
462462

463463
// If an exception is thrown, destroy what we have constructed so far in reverse order.
464-
__exception_guard __guard([&]() {
464+
auto __guard = std::__make_exception_guard([&]() {
465465
std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
466466
});
467467
for (; __i != extent_v<_Tp>; ++__i) {
@@ -488,7 +488,7 @@ __uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __
488488
_BidirIter __begin = __it;
489489

490490
// If an exception is thrown, destroy what we have constructed so far in reverse order.
491-
__exception_guard __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
491+
auto __guard = std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
492492
for (; __n != 0; --__n, ++__it) {
493493
std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
494494
}
@@ -505,7 +505,7 @@ __uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _B
505505
_BidirIter __begin = __it;
506506

507507
// If an exception is thrown, destroy what we have constructed so far in reverse order.
508-
__exception_guard __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
508+
auto __guard = std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
509509
for (; __n != 0; --__n, ++__it) {
510510
std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
511511
}

libcxx/include/__memory_resource/polymorphic_allocator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class _LIBCPP_TEMPLATE_VIS polymorphic_allocator {
9898
template <class _Type, class... _CtorArgs>
9999
[[nodiscard]] _Type* new_object(_CtorArgs&&... __ctor_args) {
100100
_Type* __ptr = allocate_object<_Type>();
101-
__exception_guard __guard([&] { deallocate_object(__ptr); });
101+
auto __guard = std::__make_exception_guard([&] { deallocate_object(__ptr); });
102102
construct(__ptr, std::forward<_CtorArgs>(__ctor_args)...);
103103
__guard.__complete();
104104
return __ptr;

libcxx/include/__utility/exception_guard.h

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,26 @@ _LIBCPP_BEGIN_NAMESPACE_STD
6060

6161
#ifndef _LIBCPP_NO_EXCEPTIONS
6262
template <class _Rollback>
63-
struct __exception_guard {
64-
__exception_guard() = delete;
63+
struct __exception_guard_exceptions {
64+
__exception_guard_exceptions() = delete;
6565

66-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit __exception_guard(_Rollback __rollback)
66+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit __exception_guard_exceptions(_Rollback __rollback)
6767
: __rollback_(std::move(__rollback)), __completed_(false) {}
6868

69-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __exception_guard(__exception_guard&& __other)
69+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
70+
__exception_guard_exceptions(__exception_guard_exceptions&& __other)
7071
_NOEXCEPT_(is_nothrow_move_constructible<_Rollback>::value)
7172
: __rollback_(std::move(__other.__rollback_)), __completed_(__other.__completed_) {
7273
__other.__completed_ = true;
7374
}
7475

75-
__exception_guard(__exception_guard const&) = delete;
76-
__exception_guard& operator=(__exception_guard const&) = delete;
77-
__exception_guard& operator=(__exception_guard&&) = delete;
76+
__exception_guard_exceptions(__exception_guard_exceptions const&) = delete;
77+
__exception_guard_exceptions& operator=(__exception_guard_exceptions const&) = delete;
78+
__exception_guard_exceptions& operator=(__exception_guard_exceptions&&) = delete;
7879

7980
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __complete() _NOEXCEPT { __completed_ = true; }
8081

81-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__exception_guard() {
82+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__exception_guard_exceptions() {
8283
if (!__completed_)
8384
__rollback_();
8485
}
@@ -87,36 +88,46 @@ struct __exception_guard {
8788
_Rollback __rollback_;
8889
bool __completed_;
8990
};
91+
92+
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__exception_guard_exceptions);
93+
94+
template <class _Rollback>
95+
using __exception_guard = __exception_guard_exceptions<_Rollback>;
9096
#else // _LIBCPP_NO_EXCEPTIONS
9197
template <class _Rollback>
92-
struct __exception_guard {
93-
__exception_guard() = delete;
94-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG explicit __exception_guard(_Rollback) {}
98+
struct __exception_guard_noexceptions {
99+
__exception_guard_noexceptions() = delete;
100+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
101+
_LIBCPP_NODEBUG explicit __exception_guard_noexceptions(_Rollback) {}
95102

96-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG __exception_guard(__exception_guard&& __other)
103+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG
104+
__exception_guard_noexceptions(__exception_guard_noexceptions&& __other)
97105
_NOEXCEPT_(is_nothrow_move_constructible<_Rollback>::value)
98106
: __completed_(__other.__completed_) {
99107
__other.__completed_ = true;
100108
}
101109

102-
__exception_guard(__exception_guard const&) = delete;
103-
__exception_guard& operator=(__exception_guard const&) = delete;
104-
__exception_guard& operator=(__exception_guard&&) = delete;
110+
__exception_guard_noexceptions(__exception_guard_noexceptions const&) = delete;
111+
__exception_guard_noexceptions& operator=(__exception_guard_noexceptions const&) = delete;
112+
__exception_guard_noexceptions& operator=(__exception_guard_noexceptions&&) = delete;
105113

106114
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG void __complete() _NOEXCEPT {
107115
__completed_ = true;
108116
}
109117

110-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG ~__exception_guard() {
118+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG ~__exception_guard_noexceptions() {
111119
_LIBCPP_ASSERT(__completed_, "__exception_guard not completed with exceptions disabled");
112120
}
113121

114122
private:
115123
bool __completed_ = false;
116124
};
117-
#endif // _LIBCPP_NO_EXCEPTIONS
118125

119-
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__exception_guard);
126+
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__exception_guard_noexceptions);
127+
128+
template <class _Rollback>
129+
using __exception_guard = __exception_guard_noexceptions<_Rollback>;
130+
#endif // _LIBCPP_NO_EXCEPTIONS
120131

121132
template <class _Rollback>
122133
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __exception_guard<_Rollback> __make_exception_guard(_Rollback __rollback) {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
// Make sure that we don't get ODR violations with __exception_guard when
10+
// linking together TUs compiled with different values of -f[no-]exceptions.
11+
12+
// RUN: %{cxx} %s %{flags} %{compile_flags} -c -o %t.except.o -O1 -Wno-private-header -fexceptions
13+
// RUN: %{cxx} %s %{flags} %{compile_flags} -c -o %t.noexcept.o -O1 -Wno-private-header -fno-exceptions
14+
// RUN: %{cxx} %{flags} %{link_flags} -o %t.exe %t.except.o %t.noexcept.o
15+
// RUN: %{run}
16+
17+
#include <__utility/exception_guard.h>
18+
#include <cassert>
19+
#include <cstring>
20+
#include <typeinfo>
21+
22+
struct Rollback {
23+
void operator()() {}
24+
};
25+
26+
#if defined(__cpp_exceptions) && __cpp_exceptions >= 199711L
27+
28+
const char* func();
29+
30+
int main(int, char**) {
31+
assert(std::strcmp(typeid(std::__exception_guard<Rollback>).name(), func()) != 0);
32+
33+
return 0;
34+
}
35+
36+
#else
37+
38+
const char* func() { return typeid(std::__exception_guard<Rollback>).name(); }
39+
40+
#endif

0 commit comments

Comments
 (0)