diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv index 9bf31c417f3c9..fdf381862d87b 100644 --- a/libcxx/docs/Status/Cxx2cIssues.csv +++ b/libcxx/docs/Status/Cxx2cIssues.csv @@ -107,7 +107,7 @@ "`LWG4153 `__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","" "`LWG4154 `__","The Mandates for ``std::packaged_task``'s constructor from a callable entity should consider decaying","2024-11 (Wrocław)","","","" "`LWG4157 `__","The resolution of LWG3465 was damaged by P2167R3","2024-11 (Wrocław)","","20","" -"`LWG4164 `__","Missing guarantees for ``forward_list`` modifiers","2024-11 (Wrocław)","","","" +"`LWG4164 `__","Missing guarantees for ``forward_list`` modifiers","2024-11 (Wrocław)","|Complete|","21","" "`LWG4169 `__","``std::atomic``'s default constructor should be constrained","2024-11 (Wrocław)","","","" "`LWG4170 `__","``contiguous_iterator`` should require ``to_address(I{})``","2024-11 (Wrocław)","","","" "","","","","","" diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list index f264f567c9bb3..fc4a9949f37b3 100644 --- a/libcxx/include/forward_list +++ b/libcxx/include/forward_list @@ -806,7 +806,9 @@ public: } # endif // _LIBCPP_CXX03_LANG _LIBCPP_HIDE_FROM_ABI iterator insert_after(const_iterator __p, const value_type& __v); - _LIBCPP_HIDE_FROM_ABI iterator insert_after(const_iterator __p, size_type __n, const value_type& __v); + _LIBCPP_HIDE_FROM_ABI iterator insert_after(const_iterator __p, size_type __n, const value_type& __v) { + return __insert_after(__p, __n, __v); + } template ::value, int> = 0> _LIBCPP_HIDE_FROM_ABI iterator insert_after(const_iterator __p, _InputIterator __f, _InputIterator __l); @@ -876,6 +878,9 @@ private: template _LIBCPP_HIDE_FROM_ABI void __assign_with_sentinel(_Iter __f, _Sent __l); + template + _LIBCPP_HIDE_FROM_ABI iterator __insert_after(const_iterator __p, size_type __n, _Args&&... __args); + template static _LIBCPP_HIDE_FROM_ABI __node_pointer __merge(__node_pointer __f1, __node_pointer __f2, _Compare& __comp); @@ -1129,17 +1134,18 @@ forward_list<_Tp, _Alloc>::insert_after(const_iterator __p, const value_type& __ } template +template typename forward_list<_Tp, _Alloc>::iterator -forward_list<_Tp, _Alloc>::insert_after(const_iterator __p, size_type __n, const value_type& __v) { +forward_list<_Tp, _Alloc>::__insert_after(const_iterator __p, size_type __n, _Args&&... __args) { __begin_node_pointer __r = __p.__get_begin(); if (__n > 0) { - __node_pointer __first = this->__create_node(/* next = */ nullptr, __v); + __node_pointer __first = this->__create_node(/* next = */ nullptr, std::forward<_Args>(__args)...); __node_pointer __last = __first; # if _LIBCPP_HAS_EXCEPTIONS try { # endif // _LIBCPP_HAS_EXCEPTIONS for (--__n; __n != 0; --__n, __last = __last->__next_) { - __last->__next_ = this->__create_node(/* next = */ nullptr, __v); + __last->__next_ = this->__create_node(/* next = */ nullptr, std::forward<_Args>(__args)...); } # if _LIBCPP_HAS_EXCEPTIONS } catch (...) { @@ -1239,14 +1245,8 @@ void forward_list<_Tp, _Alloc>::resize(size_type __n) { ; if (__i != __e) erase_after(__p, __e); - else { - __n -= __sz; - if (__n > 0) { - for (__begin_node_pointer __ptr = __p.__get_begin(); __n > 0; --__n, __ptr = __ptr->__next_as_begin()) { - __ptr->__next_ = this->__create_node(/* next = */ nullptr); - } - } - } + else + __insert_after(__p, __n - __sz); } template @@ -1259,14 +1259,8 @@ void forward_list<_Tp, _Alloc>::resize(size_type __n, const value_type& __v) { ; if (__i != __e) erase_after(__p, __e); - else { - __n -= __sz; - if (__n > 0) { - for (__begin_node_pointer __ptr = __p.__get_begin(); __n > 0; --__n, __ptr = __ptr->__next_as_begin()) { - __ptr->__next_ = this->__create_node(/* next = */ nullptr, __v); - } - } - } + else + __insert_after(__p, __n - __sz, __v); } template diff --git a/libcxx/test/std/containers/exception_safety_helpers.h b/libcxx/test/std/containers/exception_safety_helpers.h index da247571d303f..2d26735d9bb4e 100644 --- a/libcxx/test/std/containers/exception_safety_helpers.h +++ b/libcxx/test/std/containers/exception_safety_helpers.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include "test_macros.h" @@ -53,6 +54,33 @@ int ThrowingCopy::created_by_copying = 0; template int ThrowingCopy::destroyed = 0; +template +struct ThrowingDefault { + static bool throwing_enabled; + static int default_constructed; + static int destroyed; + int x = 0; + + ThrowingDefault() { + ++default_constructed; + if (throwing_enabled && default_constructed == N) { + throw -1; + } + } + + ThrowingDefault(int value) : x(value) {} + ThrowingDefault(const ThrowingDefault& other) = default; + friend bool operator==(const ThrowingDefault& lhs, const ThrowingDefault& rhs) { return lhs.x == rhs.x; } + friend bool operator<(const ThrowingDefault& lhs, const ThrowingDefault& rhs) { return lhs.x < rhs.x; } + + static void reset() { default_constructed = destroyed = 0; } +}; + +template +bool ThrowingDefault::throwing_enabled = true; +template +int ThrowingDefault::default_constructed = 0; + template struct std::hash> { std::size_t operator()(const ThrowingCopy& value) const { return value.x; } @@ -95,6 +123,29 @@ void test_exception_safety_throwing_copy_container(Func&& func) { } } +template +void test_strong_exception_safety_throwing_copy(Func&& func) { + using T = ThrowingCopy; + T::throwing_enabled = false; + + std::forward_list c0(Size); + for (int i = 0; i < Size; ++i) + c0.emplace_front(i); + std::forward_list c = c0; + T in[Size]; + T::reset(); + T::throwing_enabled = true; + try { + func(c, in, in + Size); + assert(false); // The function call above should throw. + + } catch (int) { + assert(T::created_by_copying == ThrowOn); + assert(T::destroyed == ThrowOn - 1); // No destructor call for the partially-constructed element. + assert(c == c0); // Strong exception guarantee + } +} + #endif // !defined(TEST_HAS_NO_EXCEPTIONS) #endif // SUPPORT_EXCEPTION_SAFETY_HELPERS_H diff --git a/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp index faf42202e951a..a6579e144e6d3 100644 --- a/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp +++ b/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp @@ -35,18 +35,23 @@ // void assign_range(R&& rg); // C++23 // // void push_front(const value_type& v); -// template R> +// template reference emplace_front(Args&&... args); // reference in C++17 +// template R> // void prepend_range(R&& rg); // C++23 // // iterator insert_after(const_iterator p, const value_type& v); +// iterator insert_after(const_iterator p, value_type&& v); // iterator insert_after(const_iterator p, size_type n, const value_type& v); +// iterator insert_after(const_iterator p, initializer_list il); // template // iterator insert_after(const_iterator p, // InputIterator first, InputIterator last); -// template R> +// template R> // iterator insert_range_after(const_iterator position, R&& rg); // C++23 -// +// template +// iterator emplace_after(const_iterator p, Args&&... args); // void resize(size_type n, const value_type& v); +// void resize(size_type n); #include @@ -65,16 +70,33 @@ int main(int, char**) { using T = ThrowingCopy; // void push_front(const value_type& v); - test_exception_safety_throwing_copy([](T* from, T*) { - std::forward_list c; + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T*) { + c.push_front(*from); + }); + + // template reference emplace_front(Args&&... args); + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T*) { c.push_front(*from); }); // iterator insert_after(const_iterator p, const value_type& v); - test_exception_safety_throwing_copy([](T* from, T*) { - std::forward_list c; + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T*) { c.insert_after(c.before_begin(), *from); }); + + // iterator insert_after(const_iterator p, value_type&& v); + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T*) { + c.insert_after(c.before_begin(), std::move(*from)); + }); + + // template + // iterator emplace_after(const_iterator p, Args&&... args); + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T*) { + c.emplace_after(c.before_begin(), *from); + }); + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T*) { + c.emplace_after(c.before_begin(), std::move(*from)); + }); } { @@ -169,40 +191,53 @@ int main(int, char**) { #if TEST_STD_VER >= 23 // template R> // void prepend_range(R&& rg); // C++23 - test_exception_safety_throwing_copy([](T* from, T* to) { - std::forward_list c; + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T* to) { c.prepend_range(std::ranges::subrange(from, to)); }); #endif // iterator insert_after(const_iterator p, size_type n, const value_type& v); - test_exception_safety_throwing_copy([](T* from, T*) { - std::forward_list c; + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T*) { c.insert_after(c.before_begin(), Size, *from); }); // template // iterator insert_after(const_iterator p, // InputIterator first, InputIterator last); - test_exception_safety_throwing_copy([](T* from, T* to) { - std::forward_list c; + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T* to) { c.insert_after(c.before_begin(), from, to); }); + // iterator insert_after(const_iterator p, initializer_list il); + std::initializer_list il{1, 2, 3, 4, 5}; + test_strong_exception_safety_throwing_copy([&](std::forward_list& c, T*, T*) { + c.insert_after(c.before_begin(), il); + }); + #if TEST_STD_VER >= 23 // template R> // iterator insert_range_after(const_iterator position, R&& rg); // C++23 - test_exception_safety_throwing_copy([](T* from, T* to) { - std::forward_list c; + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T* to) { c.insert_range_after(c.before_begin(), std::ranges::subrange(from, to)); }); #endif // void resize(size_type n, const value_type& v); - test_exception_safety_throwing_copy([](T* from, T*) { - std::forward_list c; - c.resize(Size, *from); - }); + test_strong_exception_safety_throwing_copy([](std::forward_list& c, T* from, T*) { + c.resize(Size * 3, *from); + }); + + { // void resize(size_type n); + using X = ThrowingDefault; + std::forward_list c0{X{1}, X{2}, X{3}}; + std::forward_list c = c0; + try { + c.resize(3 * ThrowOn); + assert(false); + } catch (int) { + assert(c == c0); + } + } } return 0;