Skip to content

Commit 5098b56

Browse files
authored
[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).
1 parent 448d7da commit 5098b56

File tree

6 files changed

+78
-12
lines changed

6 files changed

+78
-12
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,7 @@ set(files
880880
__utility/priority_tag.h
881881
__utility/private_constructor_tag.h
882882
__utility/rel_ops.h
883+
__utility/scope_guard.h
883884
__utility/small_buffer.h
884885
__utility/swap.h
885886
__utility/to_underlying.h

libcxx/include/__flat_map/flat_map.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include <__type_traits/maybe_const.h>
6161
#include <__utility/exception_guard.h>
6262
#include <__utility/pair.h>
63+
#include <__utility/scope_guard.h>
6364
#include <__vector/vector.h>
6465
#include <initializer_list>
6566
#include <stdexcept>

libcxx/include/__utility/exception_guard.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __exception_guard<_Rollback> __make_exce
137137
return __exception_guard<_Rollback>(std::move(__rollback));
138138
}
139139

140-
template <class _Rollback>
141-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __exception_guard_exceptions<_Rollback>
142-
__make_scope_guard(_Rollback __rollback) {
143-
return __exception_guard_exceptions<_Rollback>(std::move(__rollback));
144-
}
145-
146140
_LIBCPP_END_NAMESPACE_STD
147141

148142
_LIBCPP_POP_MACROS
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP___UTILITY_SCOPE_GUARD_H
11+
#define _LIBCPP___UTILITY_SCOPE_GUARD_H
12+
13+
#include <__assert>
14+
#include <__config>
15+
#include <__utility/move.h>
16+
17+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
18+
# pragma GCC system_header
19+
#endif
20+
21+
_LIBCPP_PUSH_MACROS
22+
#include <__undef_macros>
23+
24+
_LIBCPP_BEGIN_NAMESPACE_STD
25+
26+
template <class _Func>
27+
class __scope_guard {
28+
_Func __func_;
29+
bool __moved_from_;
30+
31+
public:
32+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __scope_guard(_Func __func) : __func_(std::move(__func)) {}
33+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__scope_guard() { __func_(); }
34+
35+
__scope_guard(const __scope_guard&) = delete;
36+
37+
// C++17 has mandatory RVO, so we don't need the move constructor anymore to make __make_scope_guard work.
38+
#if _LIBCPP_STD_VER <= 14
39+
__scope_guard(__scope_guard&& __other) : __func_(__other.__func_) {
40+
_LIBCPP_ASSERT_INTERNAL(!__other.__moved_from_, "Cannot move twice from __scope_guard");
41+
__other.__moved_from_ = true;
42+
}
43+
#else
44+
__scope_guard(__scope_guard&&) = delete;
45+
#endif
46+
47+
__scope_guard& operator=(const __scope_guard&) = delete;
48+
__scope_guard& operator=(__scope_guard&&) = delete;
49+
};
50+
51+
template <class _Func>
52+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __scope_guard<_Func> __make_scope_guard(_Func __func) {
53+
return __scope_guard<_Func>(std::move(__func));
54+
}
55+
56+
_LIBCPP_END_NAMESPACE_STD
57+
58+
_LIBCPP_POP_MACROS
59+
60+
#endif // _LIBCPP___UTILITY_SCOPE_GUARD_H

libcxx/include/module.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,6 +2012,7 @@ module std [system] {
20122012
module priority_tag { header "__utility/priority_tag.h" }
20132013
module private_constructor_tag { header "__utility/private_constructor_tag.h" }
20142014
module rel_ops { header "__utility/rel_ops.h" }
2015+
module scope_guard { header "__utility/scope_guard.h" }
20152016
module small_buffer { header "__utility/small_buffer.h" }
20162017
module swap { header "__utility/swap.h" }
20172018
module to_underlying { header "__utility/to_underlying.h" }

libcxx/include/string

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,7 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
638638
#include <__utility/forward.h>
639639
#include <__utility/is_pointer_in_range.h>
640640
#include <__utility/move.h>
641+
#include <__utility/scope_guard.h>
641642
#include <__utility/swap.h>
642643
#include <__utility/unreachable.h>
643644
#include <climits>
@@ -929,6 +930,15 @@ private:
929930

930931
_LIBCPP_COMPRESSED_PAIR(__rep, __rep_, allocator_type, __alloc_);
931932

933+
// annotate the string with its size() at scope exit. The string has to be in a valid state at that point.
934+
struct __annotate_new_size {
935+
basic_string& __str_;
936+
937+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __annotate_new_size(basic_string& __str) : __str_(__str) {}
938+
939+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void operator()() { __str_.__annotate_new(__str_.size()); }
940+
};
941+
932942
// Construct a string with the given allocator and enough storage to hold `__size` characters, but
933943
// don't initialize the characters. The contents of the string, including the null terminator, must be
934944
// initialized separately.
@@ -2171,6 +2181,7 @@ private:
21712181
__alloc_ = __str.__alloc_;
21722182
} else {
21732183
__annotate_delete();
2184+
auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
21742185
allocator_type __a = __str.__alloc_;
21752186
auto __allocation = std::__allocate_at_least(__a, __str.__get_long_cap());
21762187
__begin_lifetime(__allocation.ptr, __allocation.count);
@@ -2180,7 +2191,6 @@ private:
21802191
__set_long_pointer(__allocation.ptr);
21812192
__set_long_cap(__allocation.count);
21822193
__set_long_size(__str.size());
2183-
__annotate_new(__get_long_size());
21842194
}
21852195
}
21862196
}
@@ -2508,6 +2518,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
25082518
size_type __cap =
25092519
__old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1;
25102520
__annotate_delete();
2521+
auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
25112522
auto __allocation = std::__allocate_at_least(__alloc_, __cap + 1);
25122523
pointer __p = __allocation.ptr;
25132524
__begin_lifetime(__p, __allocation.count);
@@ -2526,7 +2537,6 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
25262537
__old_sz = __n_copy + __n_add + __sec_cp_sz;
25272538
__set_long_size(__old_sz);
25282539
traits_type::assign(__p[__old_sz], value_type());
2529-
__annotate_new(__old_sz);
25302540
}
25312541

25322542
// __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
25502560
pointer __old_p = __get_pointer();
25512561
size_type __cap =
25522562
__old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1;
2553-
__annotate_delete();
25542563
auto __allocation = std::__allocate_at_least(__alloc_, __cap + 1);
25552564
pointer __p = __allocation.ptr;
25562565
__begin_lifetime(__p, __allocation.count);
@@ -2575,11 +2584,12 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by_without_replace(
25752584
size_type __n_copy,
25762585
size_type __n_del,
25772586
size_type __n_add) {
2587+
__annotate_delete();
2588+
auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
25782589
_LIBCPP_SUPPRESS_DEPRECATED_PUSH
25792590
__grow_by(__old_cap, __delta_cap, __old_sz, __n_copy, __n_del, __n_add);
25802591
_LIBCPP_SUPPRESS_DEPRECATED_POP
25812592
__set_long_size(__old_sz - __n_del + __n_add);
2582-
__annotate_new(__old_sz - __n_del + __n_add);
25832593
}
25842594

25852595
// assign
@@ -3364,6 +3374,7 @@ template <class _CharT, class _Traits, class _Allocator>
33643374
inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void
33653375
basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target_capacity) {
33663376
__annotate_delete();
3377+
auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
33673378
size_type __cap = capacity();
33683379
size_type __sz = size();
33693380

@@ -3395,7 +3406,6 @@ basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target
33953406
// due to swapping the elements.
33963407
if (__allocation.count - 1 > capacity()) {
33973408
__alloc_traits::deallocate(__alloc_, __allocation.ptr, __allocation.count);
3398-
__annotate_new(__sz); // Undoes the __annotate_delete()
33993409
return;
34003410
}
34013411
__new_data = __allocation.ptr;
@@ -3420,7 +3430,6 @@ basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target
34203430
__set_long_pointer(__new_data);
34213431
} else
34223432
__set_short_size(__sz);
3423-
__annotate_new(__sz);
34243433
}
34253434

34263435
template <class _CharT, class _Traits, class _Allocator>

0 commit comments

Comments
 (0)