[libcxx-commits] [libcxx] 5098b56 - [libc++] Introduce a standalone __scope_guard and use it in <string> (#114867)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Nov 11 15:00:38 PST 2024


Author: Nikolas Klauser
Date: 2024-11-12T00:00:34+01:00
New Revision: 5098b56d22b53196e92788fbfd70b01212376db4

URL: https://github.com/llvm/llvm-project/commit/5098b56d22b53196e92788fbfd70b01212376db4
DIFF: https://github.com/llvm/llvm-project/commit/5098b56d22b53196e92788fbfd70b01212376db4.diff

LOG: [libc++] Introduce a standalone __scope_guard and use it in <string> (#114867)

This introduces a new `__scope_guard` without any fancy features. The
scope guard is used in `<string>` to simplify some of the ASan
annotations (especially by making it harder to forget them where
exceptions are thrown).

Added: 
    libcxx/include/__utility/scope_guard.h

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/__flat_map/flat_map.h
    libcxx/include/__utility/exception_guard.h
    libcxx/include/module.modulemap
    libcxx/include/string

Removed: 
    


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 6dd392685c18ee..dd774b25a81eda 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -880,6 +880,7 @@ set(files
   __utility/priority_tag.h
   __utility/private_constructor_tag.h
   __utility/rel_ops.h
+  __utility/scope_guard.h
   __utility/small_buffer.h
   __utility/swap.h
   __utility/to_underlying.h

diff  --git a/libcxx/include/__flat_map/flat_map.h b/libcxx/include/__flat_map/flat_map.h
index 77dc2b4eb48ee4..073b63cd8f0a66 100644
--- a/libcxx/include/__flat_map/flat_map.h
+++ b/libcxx/include/__flat_map/flat_map.h
@@ -60,6 +60,7 @@
 #include <__type_traits/maybe_const.h>
 #include <__utility/exception_guard.h>
 #include <__utility/pair.h>
+#include <__utility/scope_guard.h>
 #include <__vector/vector.h>
 #include <initializer_list>
 #include <stdexcept>

diff  --git a/libcxx/include/__utility/exception_guard.h b/libcxx/include/__utility/exception_guard.h
index 00b835d3e2a2fc..a03bd7e8f35227 100644
--- a/libcxx/include/__utility/exception_guard.h
+++ b/libcxx/include/__utility/exception_guard.h
@@ -137,12 +137,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __exception_guard<_Rollback> __make_exce
   return __exception_guard<_Rollback>(std::move(__rollback));
 }
 
-template <class _Rollback>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __exception_guard_exceptions<_Rollback>
-__make_scope_guard(_Rollback __rollback) {
-  return __exception_guard_exceptions<_Rollback>(std::move(__rollback));
-}
-
 _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS

diff  --git a/libcxx/include/__utility/scope_guard.h b/libcxx/include/__utility/scope_guard.h
new file mode 100644
index 00000000000000..133e54212ed592
--- /dev/null
+++ b/libcxx/include/__utility/scope_guard.h
@@ -0,0 +1,60 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_SCOPE_GUARD_H
+#define _LIBCPP___UTILITY_SCOPE_GUARD_H
+
+#include <__assert>
+#include <__config>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Func>
+class __scope_guard {
+  _Func __func_;
+  bool __moved_from_;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __scope_guard(_Func __func) : __func_(std::move(__func)) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__scope_guard() { __func_(); }
+
+  __scope_guard(const __scope_guard&) = delete;
+
+// C++17 has mandatory RVO, so we don't need the move constructor anymore to make __make_scope_guard work.
+#if _LIBCPP_STD_VER <= 14
+  __scope_guard(__scope_guard&& __other) : __func_(__other.__func_) {
+    _LIBCPP_ASSERT_INTERNAL(!__other.__moved_from_, "Cannot move twice from __scope_guard");
+    __other.__moved_from_ = true;
+  }
+#else
+  __scope_guard(__scope_guard&&) = delete;
+#endif
+
+  __scope_guard& operator=(const __scope_guard&) = delete;
+  __scope_guard& operator=(__scope_guard&&)      = delete;
+};
+
+template <class _Func>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __scope_guard<_Func> __make_scope_guard(_Func __func) {
+  return __scope_guard<_Func>(std::move(__func));
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___UTILITY_SCOPE_GUARD_H

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 2a4b9de436eab6..9027c28632dcda 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -2012,6 +2012,7 @@ module std [system] {
     module priority_tag               { header "__utility/priority_tag.h" }
     module private_constructor_tag    { header "__utility/private_constructor_tag.h" }
     module rel_ops                    { header "__utility/rel_ops.h" }
+    module scope_guard                { header "__utility/scope_guard.h" }
     module small_buffer               { header "__utility/small_buffer.h" }
     module swap                       { header "__utility/swap.h" }
     module to_underlying              { header "__utility/to_underlying.h" }

diff  --git a/libcxx/include/string b/libcxx/include/string
index 1034be8fdad8b3..a3f0bdea7ccb1d 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -638,6 +638,7 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
 #include <__utility/forward.h>
 #include <__utility/is_pointer_in_range.h>
 #include <__utility/move.h>
+#include <__utility/scope_guard.h>
 #include <__utility/swap.h>
 #include <__utility/unreachable.h>
 #include <climits>
@@ -929,6 +930,15 @@ private:
 
   _LIBCPP_COMPRESSED_PAIR(__rep, __rep_, allocator_type, __alloc_);
 
+  // annotate the string with its size() at scope exit. The string has to be in a valid state at that point.
+  struct __annotate_new_size {
+    basic_string& __str_;
+
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __annotate_new_size(basic_string& __str) : __str_(__str) {}
+
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void operator()() { __str_.__annotate_new(__str_.size()); }
+  };
+
   // Construct a string with the given allocator and enough storage to hold `__size` characters, but
   // don't initialize the characters. The contents of the string, including the null terminator, must be
   // initialized separately.
@@ -2171,6 +2181,7 @@ private:
         __alloc_ = __str.__alloc_;
       } else {
         __annotate_delete();
+        auto __guard       = std::__make_scope_guard(__annotate_new_size(*this));
         allocator_type __a = __str.__alloc_;
         auto __allocation  = std::__allocate_at_least(__a, __str.__get_long_cap());
         __begin_lifetime(__allocation.ptr, __allocation.count);
@@ -2180,7 +2191,6 @@ private:
         __set_long_pointer(__allocation.ptr);
         __set_long_cap(__allocation.count);
         __set_long_size(__str.size());
-        __annotate_new(__get_long_size());
       }
     }
   }
@@ -2508,6 +2518,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
   size_type __cap =
       __old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1;
   __annotate_delete();
+  auto __guard      = std::__make_scope_guard(__annotate_new_size(*this));
   auto __allocation = std::__allocate_at_least(__alloc_, __cap + 1);
   pointer __p       = __allocation.ptr;
   __begin_lifetime(__p, __allocation.count);
@@ -2526,7 +2537,6 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
   __old_sz = __n_copy + __n_add + __sec_cp_sz;
   __set_long_size(__old_sz);
   traits_type::assign(__p[__old_sz], value_type());
-  __annotate_new(__old_sz);
 }
 
 // __grow_by is deprecated because it does not set the size. It may not update the size when the size is changed, and it
@@ -2550,7 +2560,6 @@ _LIBCPP_DEPRECATED_("use __grow_by_without_replace") basic_string<_CharT, _Trait
   pointer __old_p = __get_pointer();
   size_type __cap =
       __old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1;
-  __annotate_delete();
   auto __allocation = std::__allocate_at_least(__alloc_, __cap + 1);
   pointer __p       = __allocation.ptr;
   __begin_lifetime(__p, __allocation.count);
@@ -2575,11 +2584,12 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by_without_replace(
     size_type __n_copy,
     size_type __n_del,
     size_type __n_add) {
+  __annotate_delete();
+  auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
   _LIBCPP_SUPPRESS_DEPRECATED_PUSH
   __grow_by(__old_cap, __delta_cap, __old_sz, __n_copy, __n_del, __n_add);
   _LIBCPP_SUPPRESS_DEPRECATED_POP
   __set_long_size(__old_sz - __n_del + __n_add);
-  __annotate_new(__old_sz - __n_del + __n_add);
 }
 
 // assign
@@ -3364,6 +3374,7 @@ template <class _CharT, class _Traits, class _Allocator>
 inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target_capacity) {
   __annotate_delete();
+  auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
   size_type __cap = capacity();
   size_type __sz  = size();
 
@@ -3395,7 +3406,6 @@ basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target
         // due to swapping the elements.
         if (__allocation.count - 1 > capacity()) {
           __alloc_traits::deallocate(__alloc_, __allocation.ptr, __allocation.count);
-          __annotate_new(__sz); // Undoes the __annotate_delete()
           return;
         }
         __new_data        = __allocation.ptr;
@@ -3420,7 +3430,6 @@ basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target
     __set_long_pointer(__new_data);
   } else
     __set_short_size(__sz);
-  __annotate_new(__sz);
 }
 
 template <class _CharT, class _Traits, class _Allocator>


        


More information about the libcxx-commits mailing list