[libcxx-commits] [libcxx] 7458908 - [libc++] Improve binary size when using __transaction

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Sun Jan 22 19:57:38 PST 2023


Author: Nikolas Klauser
Date: 2023-01-23T04:57:32+01:00
New Revision: 7458908f12da37e90262bdfaf753d15fb07006e7

URL: https://github.com/llvm/llvm-project/commit/7458908f12da37e90262bdfaf753d15fb07006e7
DIFF: https://github.com/llvm/llvm-project/commit/7458908f12da37e90262bdfaf753d15fb07006e7.diff

LOG: [libc++] Improve binary size when using __transaction

__exception_guard is a no-op in -fno-exceptions mode to produce better code-gen. This means that we don't provide the strong exception guarantees. However, Clang doesn't generate cleanup code with exceptions disabled, so even if we wanted to provide the strong exception guarantees we couldn't. This is also only relevant for constructs with a stack of -fexceptions > -fno-exceptions > -fexceptions code, since the exception can't be caught where exceptions are disabled. While -fexceptions > -fno-exceptions is quite common (e.g. libc++.dylib > -fno-exceptions), having another layer with exceptions enabled seems a lot less common, especially one that tries to catch an exception through -fno-exceptions code.

Fixes https://github.com/llvm/llvm-project/issues/56783

Reviewed By: ldionne, Mordante, huixie90, #libc

Spies: EricWF, alexfh, hans, joanahalili, libcxx-commits

Differential Revision: https://reviews.llvm.org/D133661

Added: 
    libcxx/include/__utility/exception_guard.h
    libcxx/test/libcxx/utilities/assert.exception_guard.no_exceptions.pass.cpp
    libcxx/test/libcxx/utilities/exception_guard.no_exceptions.pass.cpp
    libcxx/test/libcxx/utilities/exception_guard.pass.cpp

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/__expected/expected.h
    libcxx/include/__memory/uninitialized_algorithms.h
    libcxx/include/__memory_resource/polymorphic_allocator.h
    libcxx/include/module.modulemap.in
    libcxx/include/utility
    libcxx/include/vector
    libcxx/test/libcxx/private_headers.verify.cpp

Removed: 
    libcxx/include/__utility/transaction.h
    libcxx/test/libcxx/utilities/transaction.pass.cpp


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 44e129aeb391c..5d4fa911865f1 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -705,6 +705,7 @@ set(files
   __utility/cmp.h
   __utility/convert_to_integral.h
   __utility/declval.h
+  __utility/exception_guard.h
   __utility/exchange.h
   __utility/forward.h
   __utility/forward_like.h
@@ -717,7 +718,6 @@ set(files
   __utility/rel_ops.h
   __utility/swap.h
   __utility/to_underlying.h
-  __utility/transaction.h
   __utility/unreachable.h
   __variant/monostate.h
   __verbose_abort

diff  --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 593ec4bb39f88..d5fa88ac110e7 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -44,11 +44,11 @@
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
+#include <__utility/exception_guard.h>
 #include <__utility/forward.h>
 #include <__utility/in_place.h>
 #include <__utility/move.h>
 #include <__utility/swap.h>
-#include <__utility/transaction.h>
 #include <cstdlib> // for std::abort
 #include <initializer_list>
 
@@ -292,7 +292,7 @@ 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));
-      __transaction __trans([&] { std::construct_at(std::addressof(__oldval), std::move(__tmp)); });
+      __exception_guard __trans([&] { std::construct_at(std::addressof(__oldval), std::move(__tmp)); });
       std::construct_at(std::addressof(__newval), std::forward<_Args>(__args)...);
       __trans.__complete();
     }
@@ -451,7 +451,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_));
-        __transaction __trans([&] {
+        __exception_guard __trans([&] {
           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 +464,9 @@ 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_));
-        __transaction __trans([&] { std::construct_at(std::addressof(__with_val.__union_.__val_), std::move(__tmp)); });
+        __exception_guard __trans([&] {
+          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_));
         __trans.__complete();
         std::destroy_at(std::addressof(__with_err.__union_.__unex_));

diff  --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index ed6cc4ca5a841..63a45b2ac87b1 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -31,9 +31,9 @@
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_const.h>
 #include <__type_traits/remove_extent.h>
+#include <__utility/exception_guard.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
-#include <__utility/transaction.h>
 #include <new>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -421,7 +421,10 @@ constexpr void __allocator_construct_at(_Alloc& __alloc, _Tp* __loc) {
         _Tp& __array = *__loc;
 
         // If an exception is thrown, destroy what we have constructed so far in reverse order.
-        __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i); });
+        __exception_guard __guard([&]() {
+          std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+        });
+
         for (; __i != extent_v<_Tp>; ++__i) {
             std::__allocator_construct_at(__elem_alloc, std::addressof(__array[__i]));
         }
@@ -458,7 +461,9 @@ constexpr void __allocator_construct_at(_Alloc& __alloc, _Tp* __loc, _Arg const&
         _Tp& __array = *__loc;
 
         // If an exception is thrown, destroy what we have constructed so far in reverse order.
-        __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i); });
+        __exception_guard __guard([&]() {
+          std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+        });
         for (; __i != extent_v<_Tp>; ++__i) {
             std::__allocator_construct_at(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
         }
@@ -483,7 +488,7 @@ constexpr void __uninitialized_allocator_fill_n(_Alloc& __alloc, _BidirIter __it
     _BidirIter __begin = __it;
 
     // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+    __exception_guard __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
     for (; __n != 0; --__n, ++__it) {
         std::__allocator_construct_at(__value_alloc, std::addressof(*__it), __value);
     }
@@ -500,7 +505,7 @@ constexpr void __uninitialized_allocator_value_construct_n(_Alloc& __alloc, _Bid
     _BidirIter __begin = __it;
 
     // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+    __exception_guard __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
     for (; __n != 0; --__n, ++__it) {
         std::__allocator_construct_at(__value_alloc, std::addressof(*__it));
     }
@@ -541,21 +546,15 @@ class _AllocatorDestroyRangeReverse {
 template <class _Alloc, class _Iter1, class _Sent1, class _Iter2>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2
 __uninitialized_allocator_copy(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) {
-#ifndef _LIBCPP_NO_EXCEPTIONS
   auto __destruct_first = __first2;
-  try {
-#endif
+  auto __guard =
+      std::__make_exception_guard(_AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2));
   while (__first1 != __last1) {
     allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__first2), *__first1);
     ++__first1;
     ++__first2;
   }
-#ifndef _LIBCPP_NO_EXCEPTIONS
-  } catch (...) {
-    _AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2)();
-    throw;
-  }
-#endif
+  __guard.__complete();
   return __first2;
 }
 
@@ -597,10 +596,9 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2 __uninitialized_alloc
     _Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) {
   static_assert(__is_cpp17_move_insertable<_Alloc>::value,
                 "The specified type does not meet the requirements of Cpp17MoveInsertable");
-#ifndef _LIBCPP_NO_EXCEPTIONS
   auto __destruct_first = __first2;
-  try {
-#endif
+  auto __guard =
+      std::__make_exception_guard(_AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2));
   while (__first1 != __last1) {
 #ifndef _LIBCPP_NO_EXCEPTIONS
     allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__first2), std::move_if_noexcept(*__first1));
@@ -610,12 +608,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2 __uninitialized_alloc
     ++__first1;
     ++__first2;
   }
-#ifndef _LIBCPP_NO_EXCEPTIONS
-  } catch (...) {
-    _AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2)();
-    throw;
-  }
-#endif
+  __guard.__complete();
   return __first2;
 }
 

diff  --git a/libcxx/include/__memory_resource/polymorphic_allocator.h b/libcxx/include/__memory_resource/polymorphic_allocator.h
index 8e59dfc55d78c..2489502bcdaf2 100644
--- a/libcxx/include/__memory_resource/polymorphic_allocator.h
+++ b/libcxx/include/__memory_resource/polymorphic_allocator.h
@@ -12,7 +12,7 @@
 #include <__assert>
 #include <__config>
 #include <__memory_resource/memory_resource.h>
-#include <__utility/transaction.h>
+#include <__utility/exception_guard.h>
 #include <cstddef>
 #include <limits>
 #include <new>
@@ -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>();
-    __transaction __guard([&] { deallocate_object(__ptr); });
+    __exception_guard __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
new file mode 100644
index 0000000000000..737d1a69c9717
--- /dev/null
+++ b/libcxx/include/__utility/exception_guard.h
@@ -0,0 +1,128 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___UTILITY_TRANSACTION_H
+#define _LIBCPP___UTILITY_TRANSACTION_H
+
+#include <__assert>
+#include <__config>
+#include <__type_traits/is_nothrow_move_constructible.h>
+#include <__utility/exchange.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __exception_guard is a helper class for writing code with the strong exception guarantee.
+//
+// When writing code that can throw an exception, one can store rollback instructions in an
+// exception guard so that if an exception is thrown at any point during the lifetime of the
+// exception guard, it will be rolled back automatically. When the exception guard is done, one
+// must mark it as being complete so it isn't rolled back when the exception guard is destroyed.
+//
+// Exception guards are not default constructible, they can't be copied or assigned to, but
+// they can be moved around for convenience.
+//
+// __exception_guard is a no-op in -fno-exceptions mode to produce better code-gen. This means
+// that we don't provide the strong exception guarantees. However, Clang doesn't generate cleanup
+// code with exceptions disabled, so even if we wanted to provide the strong exception guarantees
+// we couldn't. This is also only relevant for constructs with a stack of
+// -fexceptions > -fno-exceptions > -fexceptions code, since the exception can't be caught where
+// exceptions are disabled. While -fexceptions > -fno-exceptions is quite common
+// (e.g. libc++.dylib > -fno-exceptions), having another layer with exceptions enabled seems a lot
+// less common, especially one that tries to catch an exception through -fno-exceptions code.
+//
+// __exception_guard can help greatly simplify code that would normally be cluttered by
+// `#if _LIBCPP_NO_EXCEPTIONS`. For example:
+//
+//    template <class Iterator, class Size, class OutputIterator>
+//    Iterator uninitialized_copy_n(Iterator iter, Size n, OutputIterator out) {
+//        typedef typename iterator_traits<Iterator>::value_type value_type;
+//        __exception_guard guard([start=out, &out] {
+//            std::destroy(start, out);
+//        });
+//
+//        for (; n > 0; ++iter, ++out, --n) {
+//            ::new ((void*)std::addressof(*out)) value_type(*iter);
+//        }
+//        guard.__complete();
+//        return out;
+//    }
+//
+
+#ifndef _LIBCPP_NO_EXCEPTIONS
+template <class _Rollback>
+struct __exception_guard {
+  __exception_guard() = delete;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit __exception_guard(_Rollback __rollback)
+      : __rollback_(std::move(__rollback)), __completed_(false) {}
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __exception_guard(__exception_guard&& __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;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __complete() _NOEXCEPT { __completed_ = true; }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__exception_guard() {
+    if (!__completed_)
+      __rollback_();
+  }
+
+private:
+  _Rollback __rollback_;
+  bool __completed_;
+};
+#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) {}
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG __exception_guard(__exception_guard&& __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;
+
+  _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_ASSERT(__completed_, "__exception_guard not completed with exceptions disabled");
+  }
+
+private:
+  bool __completed_ = false;
+};
+#endif // _LIBCPP_NO_EXCEPTIONS
+
+_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__exception_guard);
+
+template <class _Rollback>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __exception_guard<_Rollback> __make_exception_guard(_Rollback __rollback) {
+  return __exception_guard<_Rollback>(std::move(__rollback));
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_TRANSACTION_H

diff  --git a/libcxx/include/__utility/transaction.h b/libcxx/include/__utility/transaction.h
deleted file mode 100644
index 3baedd2baf7b8..0000000000000
--- a/libcxx/include/__utility/transaction.h
+++ /dev/null
@@ -1,97 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP___UTILITY_TRANSACTION_H
-#define _LIBCPP___UTILITY_TRANSACTION_H
-
-#include <__config>
-#include <__type_traits/is_nothrow_move_constructible.h>
-#include <__utility/exchange.h>
-#include <__utility/move.h>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-// __transaction is a helper class for writing code with the strong exception guarantee.
-//
-// When writing code that can throw an exception, one can store rollback instructions in a
-// transaction so that if an exception is thrown at any point during the lifetime of the
-// transaction, it will be rolled back automatically. When the transaction is done, one
-// must mark it as being complete so it isn't rolled back when the transaction is destroyed.
-//
-// Transactions are not default constructible, they can't be copied or assigned to, but
-// they can be moved around for convenience.
-//
-// __transaction can help greatly simplify code that would normally be cluttered by
-// `#if _LIBCPP_NO_EXCEPTIONS`. For example:
-//
-//    template <class Iterator, class Size, class OutputIterator>
-//    Iterator uninitialized_copy_n(Iterator iter, Size n, OutputIterator out) {
-//        typedef typename iterator_traits<Iterator>::value_type value_type;
-//        __transaction transaction([start=out, &out] {
-//            std::destroy(start, out);
-//        });
-//
-//        for (; n > 0; ++iter, ++out, --n) {
-//            ::new ((void*)std::addressof(*out)) value_type(*iter);
-//        }
-//        transaction.__complete();
-//        return out;
-//    }
-//
-template <class _Rollback>
-struct __transaction {
-    __transaction() = delete;
-
-    _LIBCPP_HIDE_FROM_ABI
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit __transaction(_Rollback __rollback)
-        : __rollback_(_VSTD::move(__rollback))
-        , __completed_(false)
-    { }
-
-    _LIBCPP_HIDE_FROM_ABI
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 __transaction(__transaction&& __other)
-        _NOEXCEPT_(is_nothrow_move_constructible<_Rollback>::value)
-        : __rollback_(_VSTD::move(__other.__rollback_))
-        , __completed_(__other.__completed_)
-    {
-        __other.__completed_ = true;
-    }
-
-    __transaction(__transaction const&) = delete;
-    __transaction& operator=(__transaction const&) = delete;
-    __transaction& operator=(__transaction&&) = delete;
-
-    _LIBCPP_HIDE_FROM_ABI
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 void __complete() _NOEXCEPT {
-        __completed_ = true;
-    }
-
-    _LIBCPP_HIDE_FROM_ABI
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__transaction() {
-        if (!__completed_)
-            __rollback_();
-    }
-
-private:
-    _Rollback __rollback_;
-    bool __completed_;
-};
-_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__transaction);
-
-template <class _Rollback>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __transaction<_Rollback> __make_transaction(_Rollback __rollback) {
-  return __transaction<_Rollback>(std::move(__rollback));
-}
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP___UTILITY_TRANSACTION_H

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index ab9a21398fa78..5715775443ec3 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1565,6 +1565,7 @@ module std [system] {
       module cmp                 { private header "__utility/cmp.h" }
       module convert_to_integral { private header "__utility/convert_to_integral.h" }
       module declval             { private header "__utility/declval.h" }
+      module exception_guard     { private header "__utility/exception_guard.h" }
       module exchange            { private header "__utility/exchange.h" }
       module forward             { private header "__utility/forward.h" }
       module forward_like        { private header "__utility/forward_like.h" }
@@ -1578,7 +1579,6 @@ module std [system] {
       module rel_ops             { private header "__utility/rel_ops.h" }
       module swap                { private header "__utility/swap.h" }
       module to_underlying       { private header "__utility/to_underlying.h" }
-      module transaction         { private header "__utility/transaction.h" }
       module unreachable         { private header "__utility/unreachable.h" }
     }
   }

diff  --git a/libcxx/include/utility b/libcxx/include/utility
index 1d56759310f92..a4d8cf853d20b 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -243,6 +243,7 @@ template <class T>
 #include <__utility/auto_cast.h>
 #include <__utility/cmp.h>
 #include <__utility/declval.h>
+#include <__utility/exception_guard.h>
 #include <__utility/exchange.h>
 #include <__utility/forward.h>
 #include <__utility/forward_like.h>
@@ -255,7 +256,6 @@ template <class T>
 #include <__utility/rel_ops.h>
 #include <__utility/swap.h>
 #include <__utility/to_underlying.h>
-#include <__utility/transaction.h>
 #include <__utility/unreachable.h>
 #include <version>
 

diff  --git a/libcxx/include/vector b/libcxx/include/vector
index 4c939806dc12b..4b7ae130a7bc4 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -308,10 +308,10 @@ template<class T, class charT> requires is-vector-bool-reference<T> // Since C++
 #include <__split_buffer>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/noexcept_move_assign_container.h>
+#include <__utility/exception_guard.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 #include <__utility/swap.h>
-#include <__utility/transaction.h>
 #include <climits>
 #include <cstdlib>
 #include <cstring>
@@ -1073,7 +1073,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20
 vector<_Tp, _Allocator>::vector(size_type __n)
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     if (__n > 0)
     {
@@ -1089,7 +1089,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20
 vector<_Tp, _Allocator>::vector(size_type __n, const allocator_type& __a)
     : __end_cap_(nullptr, __a)
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     if (__n > 0)
     {
@@ -1104,7 +1104,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20
 vector<_Tp, _Allocator>::vector(size_type __n, const value_type& __x)
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     if (__n > 0)
     {
@@ -1121,7 +1121,7 @@ template <class _InputIterator, __enable_if_t<__is_exactly_cpp17_input_iterator<
 _LIBCPP_CONSTEXPR_SINCE_CXX20
 vector<_Tp, _Allocator>::vector(_InputIterator __first, _InputIterator __last)
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     for (; __first != __last; ++__first)
         emplace_back(*__first);
@@ -1136,7 +1136,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20
 vector<_Tp, _Allocator>::vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a)
     : __end_cap_(nullptr, __a)
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     for (; __first != __last; ++__first)
         emplace_back(*__first);
@@ -1150,7 +1150,7 @@ template <class _ForwardIterator, __enable_if_t<__is_cpp17_forward_iterator<_For
 _LIBCPP_CONSTEXPR_SINCE_CXX20
 vector<_Tp, _Allocator>::vector(_ForwardIterator __first, _ForwardIterator __last)
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     size_type __n = static_cast<size_type>(std::distance(__first, __last));
     if (__n > 0)
@@ -1169,7 +1169,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20
 vector<_Tp, _Allocator>::vector(_ForwardIterator __first, _ForwardIterator __last, const allocator_type& __a)
     : __end_cap_(nullptr, __a)
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     size_type __n = static_cast<size_type>(std::distance(__first, __last));
     if (__n > 0)
@@ -1185,7 +1185,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20
 vector<_Tp, _Allocator>::vector(const vector& __x)
     : __end_cap_(nullptr, __alloc_traits::select_on_container_copy_construction(__x.__alloc()))
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     size_type __n = __x.size();
     if (__n > 0)
@@ -1201,7 +1201,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20
 vector<_Tp, _Allocator>::vector(const vector& __x, const __type_identity_t<allocator_type>& __a)
     : __end_cap_(nullptr, __a)
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     size_type __n = __x.size();
     if (__n > 0)
@@ -1249,7 +1249,7 @@ vector<_Tp, _Allocator>::vector(vector&& __x, const __type_identity_t<allocator_
     else
     {
         typedef move_iterator<iterator> _Ip;
-        auto __guard = std::__make_transaction(__destroy_vector(*this));
+        auto __guard = std::__make_exception_guard(__destroy_vector(*this));
         assign(_Ip(__x.begin()), _Ip(__x.end()));
         __guard.__complete();
     }
@@ -1262,7 +1262,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20
 inline _LIBCPP_HIDE_FROM_ABI
 vector<_Tp, _Allocator>::vector(initializer_list<value_type> __il)
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     if (__il.size() > 0)
     {
@@ -1278,7 +1278,7 @@ inline _LIBCPP_HIDE_FROM_ABI
 vector<_Tp, _Allocator>::vector(initializer_list<value_type> __il, const allocator_type& __a)
     : __end_cap_(nullptr, __a)
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     std::__debug_db_insert_c(this);
     if (__il.size() > 0)
     {
@@ -2657,7 +2657,7 @@ vector<bool, _Allocator>::vector(_ForwardIterator __first, _ForwardIterator __la
       __size_(0),
       __cap_alloc_(0, __default_init_tag())
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     size_type __n = static_cast<size_type>(std::distance(__first, __last));
     if (__n > 0)
     {
@@ -2676,7 +2676,7 @@ vector<bool, _Allocator>::vector(_ForwardIterator __first, _ForwardIterator __la
       __size_(0),
       __cap_alloc_(0, static_cast<__storage_allocator>(__a))
 {
-    auto __guard = std::__make_transaction(__destroy_vector(*this));
+    auto __guard = std::__make_exception_guard(__destroy_vector(*this));
     size_type __n = static_cast<size_type>(std::distance(__first, __last));
     if (__n > 0)
     {

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index d67705378adb1..1498faa71ddec 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -717,6 +717,7 @@ END-SCRIPT
 #include <__utility/cmp.h> // expected-error@*:* {{use of private header from outside its module: '__utility/cmp.h'}}
 #include <__utility/convert_to_integral.h> // expected-error@*:* {{use of private header from outside its module: '__utility/convert_to_integral.h'}}
 #include <__utility/declval.h> // expected-error@*:* {{use of private header from outside its module: '__utility/declval.h'}}
+#include <__utility/exception_guard.h> // expected-error@*:* {{use of private header from outside its module: '__utility/exception_guard.h'}}
 #include <__utility/exchange.h> // expected-error@*:* {{use of private header from outside its module: '__utility/exchange.h'}}
 #include <__utility/forward.h> // expected-error@*:* {{use of private header from outside its module: '__utility/forward.h'}}
 #include <__utility/forward_like.h> // expected-error@*:* {{use of private header from outside its module: '__utility/forward_like.h'}}
@@ -729,7 +730,6 @@ END-SCRIPT
 #include <__utility/rel_ops.h> // expected-error@*:* {{use of private header from outside its module: '__utility/rel_ops.h'}}
 #include <__utility/swap.h> // expected-error@*:* {{use of private header from outside its module: '__utility/swap.h'}}
 #include <__utility/to_underlying.h> // expected-error@*:* {{use of private header from outside its module: '__utility/to_underlying.h'}}
-#include <__utility/transaction.h> // expected-error@*:* {{use of private header from outside its module: '__utility/transaction.h'}}
 #include <__utility/unreachable.h> // expected-error@*:* {{use of private header from outside its module: '__utility/unreachable.h'}}
 #include <__variant/monostate.h> // expected-error@*:* {{use of private header from outside its module: '__variant/monostate.h'}}
 // GENERATED-MARKER

diff  --git a/libcxx/test/libcxx/utilities/assert.exception_guard.no_exceptions.pass.cpp b/libcxx/test/libcxx/utilities/assert.exception_guard.no_exceptions.pass.cpp
new file mode 100644
index 0000000000000..5c9cc38c0561e
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/assert.exception_guard.no_exceptions.pass.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// REQUIRES: has-unix-headers
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}}
+// ADDITIONAL_COMPILE_FLAGS: -fno-exceptions -D_LIBCPP_ENABLE_ASSERTIONS
+
+// ADDITIONAL_COMPILE_FLAGS: -Wno-private-header
+
+#include <__utility/exception_guard.h>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::__make_exception_guard([] {}), "__exception_guard not completed with exceptions disabled");
+}

diff  --git a/libcxx/test/libcxx/utilities/exception_guard.no_exceptions.pass.cpp b/libcxx/test/libcxx/utilities/exception_guard.no_exceptions.pass.cpp
new file mode 100644
index 0000000000000..0b6233e3ef67e
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/exception_guard.no_exceptions.pass.cpp
@@ -0,0 +1,19 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// ADDITIONAL_COMPILE_FLAGS: -fno-exceptions -D_LIBCPP_ENABLE_ASSERTIONS
+
+#include <utility>
+
+int main(int, char**) {
+  auto guard = std::__make_exception_guard([] {});
+  auto guard2 = std::move(guard);
+  guard2.__complete();
+}

diff  --git a/libcxx/test/libcxx/utilities/transaction.pass.cpp b/libcxx/test/libcxx/utilities/exception_guard.pass.cpp
similarity index 81%
rename from libcxx/test/libcxx/utilities/transaction.pass.cpp
rename to libcxx/test/libcxx/utilities/exception_guard.pass.cpp
index e517ffdc9e9f5..71e60fc94542d 100644
--- a/libcxx/test/libcxx/utilities/transaction.pass.cpp
+++ b/libcxx/test/libcxx/utilities/exception_guard.pass.cpp
@@ -8,7 +8,8 @@
 
 // UNSUPPORTED: c++03
 
-#include <utility> // for __transaction
+// UNSUPPORTED: no-exceptions
+
 #include <cassert>
 #include <type_traits>
 #include <utility>
@@ -22,7 +23,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
         bool rolled_back = false;
         {
             auto rollback = [&] { rolled_back = true; };
-            std::__transaction<decltype(rollback)> t(rollback);
+            std::__exception_guard<decltype(rollback)> g(rollback);
         }
         assert(rolled_back);
     }
@@ -33,8 +34,8 @@ TEST_CONSTEXPR_CXX20 bool test() {
         bool rolled_back = false;
         {
             auto rollback = [&] { rolled_back = true; };
-            std::__transaction<decltype(rollback)> t(rollback);
-            t.__complete();
+            std::__exception_guard<decltype(rollback)> g(rollback);
+            g.__complete();
         }
         assert(!rolled_back);
     }
@@ -47,8 +48,8 @@ TEST_CONSTEXPR_CXX20 bool test() {
             int rollbacks = 0;
             {
                 auto rollback = [&] { ++rollbacks; };
-                std::__transaction<decltype(rollback)> t(rollback);
-                auto other = std::move(t);
+                std::__exception_guard<decltype(rollback)> g(rollback);
+                auto other = std::move(g);
             }
             assert(rollbacks == 1);
         }
@@ -58,8 +59,8 @@ TEST_CONSTEXPR_CXX20 bool test() {
             int rollbacks = 0;
             {
                 auto rollback = [&] { ++rollbacks; };
-                std::__transaction<decltype(rollback)> t(rollback);
-                auto other = std::move(t);
+                std::__exception_guard<decltype(rollback)> g(rollback);
+                auto other = std::move(g);
                 other.__complete();
             }
             assert(rollbacks == 0);
@@ -69,7 +70,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
     // Basic properties of the type
     {
         struct Rollback { void operator()() const { } };
-        using Transaction = std::__transaction<Rollback>;
+        using Transaction = std::__exception_guard<Rollback>;
 
         static_assert(!std::is_default_constructible<Transaction>::value, "");
 
@@ -85,7 +86,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
                 ThrowOnMove(ThrowOnMove&&) noexcept(false) { }
                 void operator()() const { }
             };
-            using ThrowOnMoveTransaction = std::__transaction<ThrowOnMove>;
+            using ThrowOnMoveTransaction = std::__exception_guard<ThrowOnMove>;
 
             ASSERT_NOEXCEPT(std::declval<Transaction>().__complete());
             static_assert( std::is_nothrow_move_constructible<Transaction>::value, "");
@@ -104,7 +105,7 @@ void test_exceptions() {
         bool rolled_back = false;
         auto rollback = [&] { rolled_back = true; };
         try {
-            std::__transaction<decltype(rollback)> t(rollback);
+            std::__exception_guard<decltype(rollback)> g(rollback);
             throw 0;
         } catch (...) { }
         assert(rolled_back);
@@ -116,14 +117,14 @@ void test_exceptions() {
         bool rolled_back = false;
         auto rollback = [&] { rolled_back = true; };
         try {
-            std::__transaction<decltype(rollback)> t(rollback);
-            t.__complete();
+            std::__exception_guard<decltype(rollback)> g(rollback);
+            g.__complete();
             throw 0;
         } catch (...) { }
         assert(!rolled_back);
     }
 
-    // Make sure __transaction does not rollback if the transaction is marked as
+    // Make sure __exception_guard does not rollback if the transaction is marked as
     // completed within a destructor.
     {
         struct S {
@@ -131,8 +132,8 @@ void test_exceptions() {
 
             ~S() {
                 auto rollback = [this]{ x_ = true; };
-                std::__transaction<decltype(rollback)> t(rollback);
-                t.__complete();
+                std::__exception_guard<decltype(rollback)> g(rollback);
+                g.__complete();
             }
 
             bool& x_;


        


More information about the libcxx-commits mailing list