[libc++] Improve binary size when using __transaction
authorNikolas Klauser <nikolasklauser@berlin.de>
Thu, 8 Dec 2022 08:40:54 +0000 (09:40 +0100)
committerNikolas Klauser <nikolasklauser@berlin.de>
Mon, 23 Jan 2023 03:57:32 +0000 (04:57 +0100)
__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

13 files changed:
libcxx/include/CMakeLists.txt
libcxx/include/__expected/expected.h
libcxx/include/__memory/uninitialized_algorithms.h
libcxx/include/__memory_resource/polymorphic_allocator.h
libcxx/include/__utility/exception_guard.h [new file with mode: 0644]
libcxx/include/__utility/transaction.h [deleted file]
libcxx/include/module.modulemap.in
libcxx/include/utility
libcxx/include/vector
libcxx/test/libcxx/private_headers.verify.cpp
libcxx/test/libcxx/utilities/assert.exception_guard.no_exceptions.pass.cpp [new file with mode: 0644]
libcxx/test/libcxx/utilities/exception_guard.no_exceptions.pass.cpp [new file with mode: 0644]
libcxx/test/libcxx/utilities/exception_guard.pass.cpp [moved from libcxx/test/libcxx/utilities/transaction.pass.cpp with 81% similarity]

index 44e129a..5d4fa91 100644 (file)
@@ -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
index 593ec4b..d5fa88a 100644 (file)
 #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 @@ private:
           "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 @@ public:
       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 @@ public:
                       "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_));
index ed6cc4c..63a45b2 100644 (file)
@@ -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 @@ private:
 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;
 }
 
index 8e59dfc..2489502 100644 (file)
@@ -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 @@ public:
   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 (file)
index 0000000..737d1a6
--- /dev/null
@@ -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 (file)
index 3baedd2..0000000
+++ /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
index ab9a213..5715775 100644 (file)
@@ -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" }
     }
   }
index 1d56759..a4d8cf8 100644 (file)
@@ -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>
 
index 4c93980..4b7ae13 100644 (file)
@@ -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)
     {
index d677053..1498faa 100644 (file)
@@ -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 (file)
index 0000000..5c9cc38
--- /dev/null
@@ -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 (file)
index 0000000..0b6233e
--- /dev/null
@@ -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();
+}
@@ -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_;