[libcxx-commits] [libcxx] 1a17739 - [libc++] Avoid ODR violations in __exception_guard

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Wed Feb 1 17:07:22 PST 2023


Author: Nikolas Klauser
Date: 2023-02-02T02:07:17+01:00
New Revision: 1a17739d3aa78599c32f6106e05dcfa7f3f9e823

URL: https://github.com/llvm/llvm-project/commit/1a17739d3aa78599c32f6106e05dcfa7f3f9e823
DIFF: https://github.com/llvm/llvm-project/commit/1a17739d3aa78599c32f6106e05dcfa7f3f9e823.diff

LOG: [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

Added: 
    libcxx/test/libcxx/utilities/exception_guard.odr.sh.cpp

Modified: 
    libcxx/include/__expected/expected.h
    libcxx/include/__memory/uninitialized_algorithms.h
    libcxx/include/__memory_resource/polymorphic_allocator.h
    libcxx/include/__utility/exception_guard.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index e1f590c65efe..ca3e8a59922d 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -292,7 +292,8 @@ class expected {
           "be reverted to the previous state in case an exception is thrown during the assignment.");
       _T2 __tmp(std::move(__oldval));
       std::destroy_at(std::addressof(__oldval));
-      __exception_guard __trans([&] { std::construct_at(std::addressof(__oldval), std::move(__tmp)); });
+      auto __trans =
+          std::__make_exception_guard([&] { std::construct_at(std::addressof(__oldval), std::move(__tmp)); });
       std::construct_at(std::addressof(__newval), std::forward<_Args>(__args)...);
       __trans.__complete();
     }
@@ -451,7 +452,7 @@ class expected {
       if constexpr (is_nothrow_move_constructible_v<_Err>) {
         _Err __tmp(std::move(__with_err.__union_.__unex_));
         std::destroy_at(std::addressof(__with_err.__union_.__unex_));
-        __exception_guard __trans([&] {
+        auto __trans = std::__make_exception_guard([&] {
           std::construct_at(std::addressof(__with_err.__union_.__unex_), std::move(__tmp));
         });
         std::construct_at(std::addressof(__with_err.__union_.__val_), std::move(__with_val.__union_.__val_));
@@ -464,7 +465,7 @@ class expected {
                       "that it can be reverted to the previous state in case an exception is thrown during swap.");
         _Tp __tmp(std::move(__with_val.__union_.__val_));
         std::destroy_at(std::addressof(__with_val.__union_.__val_));
-        __exception_guard __trans([&] {
+        auto __trans = std::__make_exception_guard([&] {
           std::construct_at(std::addressof(__with_val.__union_.__val_), std::move(__tmp));
         });
         std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__with_err.__union_.__unex_));

diff  --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 0067780c3f5d..90aecb7d6ad2 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -421,7 +421,7 @@ constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* _
         _Tp& __array = *__loc;
 
         // If an exception is thrown, destroy what we have constructed so far in reverse order.
-        __exception_guard __guard([&]() {
+        auto __guard = std::__make_exception_guard([&]() {
           std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
         });
 
@@ -461,7 +461,7 @@ constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* _
         _Tp& __array = *__loc;
 
         // If an exception is thrown, destroy what we have constructed so far in reverse order.
-        __exception_guard __guard([&]() {
+        auto __guard = std::__make_exception_guard([&]() {
           std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
         });
         for (; __i != extent_v<_Tp>; ++__i) {
@@ -488,7 +488,7 @@ __uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __
     _BidirIter __begin = __it;
 
     // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    __exception_guard __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+    auto __guard = std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
     for (; __n != 0; --__n, ++__it) {
         std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
     }
@@ -505,7 +505,7 @@ __uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _B
     _BidirIter __begin = __it;
 
     // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    __exception_guard __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+    auto __guard = std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
     for (; __n != 0; --__n, ++__it) {
         std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
     }

diff  --git a/libcxx/include/__memory_resource/polymorphic_allocator.h b/libcxx/include/__memory_resource/polymorphic_allocator.h
index 2489502bcdaf..f7b9a0b408c1 100644
--- a/libcxx/include/__memory_resource/polymorphic_allocator.h
+++ b/libcxx/include/__memory_resource/polymorphic_allocator.h
@@ -98,7 +98,7 @@ class _LIBCPP_TEMPLATE_VIS polymorphic_allocator {
   template <class _Type, class... _CtorArgs>
   [[nodiscard]] _Type* new_object(_CtorArgs&&... __ctor_args) {
     _Type* __ptr = allocate_object<_Type>();
-    __exception_guard __guard([&] { deallocate_object(__ptr); });
+    auto __guard = std::__make_exception_guard([&] { deallocate_object(__ptr); });
     construct(__ptr, std::forward<_CtorArgs>(__ctor_args)...);
     __guard.__complete();
     return __ptr;

diff  --git a/libcxx/include/__utility/exception_guard.h b/libcxx/include/__utility/exception_guard.h
index 737d1a69c971..46f9359a5c0e 100644
--- a/libcxx/include/__utility/exception_guard.h
+++ b/libcxx/include/__utility/exception_guard.h
@@ -60,25 +60,26 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #ifndef _LIBCPP_NO_EXCEPTIONS
 template <class _Rollback>
-struct __exception_guard {
-  __exception_guard() = delete;
+struct __exception_guard_exceptions {
+  __exception_guard_exceptions() = delete;
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit __exception_guard(_Rollback __rollback)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit __exception_guard_exceptions(_Rollback __rollback)
       : __rollback_(std::move(__rollback)), __completed_(false) {}
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __exception_guard(__exception_guard&& __other)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+  __exception_guard_exceptions(__exception_guard_exceptions&& __other)
       _NOEXCEPT_(is_nothrow_move_constructible<_Rollback>::value)
       : __rollback_(std::move(__other.__rollback_)), __completed_(__other.__completed_) {
     __other.__completed_ = true;
   }
 
-  __exception_guard(__exception_guard const&)            = delete;
-  __exception_guard& operator=(__exception_guard const&) = delete;
-  __exception_guard& operator=(__exception_guard&&)      = delete;
+  __exception_guard_exceptions(__exception_guard_exceptions const&)            = delete;
+  __exception_guard_exceptions& operator=(__exception_guard_exceptions const&) = delete;
+  __exception_guard_exceptions& operator=(__exception_guard_exceptions&&)      = delete;
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __complete() _NOEXCEPT { __completed_ = true; }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__exception_guard() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__exception_guard_exceptions() {
     if (!__completed_)
       __rollback_();
   }
@@ -87,36 +88,46 @@ struct __exception_guard {
   _Rollback __rollback_;
   bool __completed_;
 };
+
+_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__exception_guard_exceptions);
+
+template <class _Rollback>
+using __exception_guard = __exception_guard_exceptions<_Rollback>;
 #else  // _LIBCPP_NO_EXCEPTIONS
 template <class _Rollback>
-struct __exception_guard {
-  __exception_guard() = delete;
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG explicit __exception_guard(_Rollback) {}
+struct __exception_guard_noexceptions {
+  __exception_guard_noexceptions() = delete;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+      _LIBCPP_NODEBUG explicit __exception_guard_noexceptions(_Rollback) {}
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG __exception_guard(__exception_guard&& __other)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG
+  __exception_guard_noexceptions(__exception_guard_noexceptions&& __other)
       _NOEXCEPT_(is_nothrow_move_constructible<_Rollback>::value)
       : __completed_(__other.__completed_) {
     __other.__completed_ = true;
   }
 
-  __exception_guard(__exception_guard const&)            = delete;
-  __exception_guard& operator=(__exception_guard const&) = delete;
-  __exception_guard& operator=(__exception_guard&&)      = delete;
+  __exception_guard_noexceptions(__exception_guard_noexceptions const&)            = delete;
+  __exception_guard_noexceptions& operator=(__exception_guard_noexceptions const&) = delete;
+  __exception_guard_noexceptions& operator=(__exception_guard_noexceptions&&)      = delete;
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG void __complete() _NOEXCEPT {
     __completed_ = true;
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG ~__exception_guard() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG ~__exception_guard_noexceptions() {
     _LIBCPP_ASSERT(__completed_, "__exception_guard not completed with exceptions disabled");
   }
 
 private:
   bool __completed_ = false;
 };
-#endif // _LIBCPP_NO_EXCEPTIONS
 
-_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__exception_guard);
+_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__exception_guard_noexceptions);
+
+template <class _Rollback>
+using __exception_guard = __exception_guard_noexceptions<_Rollback>;
+#endif // _LIBCPP_NO_EXCEPTIONS
 
 template <class _Rollback>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __exception_guard<_Rollback> __make_exception_guard(_Rollback __rollback) {

diff  --git a/libcxx/test/libcxx/utilities/exception_guard.odr.sh.cpp b/libcxx/test/libcxx/utilities/exception_guard.odr.sh.cpp
new file mode 100644
index 000000000000..07bbdbb8bc2f
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/exception_guard.odr.sh.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that we don't get ODR violations with __exception_guard when
+// linking together TUs compiled with 
diff erent values of -f[no-]exceptions.
+
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -o %t.except.o   -O1 -Wno-private-header -fexceptions
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -o %t.noexcept.o -O1 -Wno-private-header -fno-exceptions
+// RUN: %{cxx} %{flags} %{link_flags} -o %t.exe %t.except.o %t.noexcept.o
+// RUN: %{run}
+
+#include <__utility/exception_guard.h>
+#include <cassert>
+#include <cstring>
+#include <typeinfo>
+
+struct Rollback {
+  void operator()() {}
+};
+
+#if defined(__cpp_exceptions) && __cpp_exceptions >= 199711L
+
+const char* func();
+
+int main(int, char**) {
+  assert(std::strcmp(typeid(std::__exception_guard<Rollback>).name(), func()) != 0);
+
+  return 0;
+}
+
+#else
+
+const char* func() { return typeid(std::__exception_guard<Rollback>).name(); }
+
+#endif


        


More information about the libcxx-commits mailing list