diff --git a/stl/inc/filesystem b/stl/inc/filesystem index 0130975f0ef..250e3443168 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -1455,8 +1455,10 @@ namespace filesystem { _Path_iterator(const _Base_iter& _Position_, const path* _Mypath_) noexcept : _Position(_Position_), _Element(), _Mypath(_Mypath_) {} - _Path_iterator(const _Base_iter& _Position_, wstring_view _Element_text, const path* _Mypath_) - : _Position(_Position_), _Element(_Element_text), _Mypath(_Mypath_) {} + _Path_iterator(const _Base_iter& _Position_, const path& _Element_, const path* _Mypath_) + : _Position(_Position_), _Element(_Element_), _Mypath(_Mypath_) {} + _Path_iterator(const _Base_iter& _Position_, path&& _Element_, const path* _Mypath_) + : _Position(_Position_), _Element(_STD move(_Element_)), _Mypath(_Mypath_) {} _Path_iterator(const _Path_iterator&) = default; _Path_iterator(_Path_iterator&&) = default; @@ -1600,8 +1602,13 @@ namespace filesystem { using _Prevent_inheriting_unwrap = _Path_iterator; template , int> = 0> - _NODISCARD _Path_iterator<_Unwrapped_t> _Unwrapped() const { - return {_Position._Unwrapped(), _Element.native(), _Mypath}; + _NODISCARD _Path_iterator<_Unwrapped_t> _Unwrapped() const& { + return {_Position._Unwrapped(), _Element, _Mypath}; + } + template , int> = 0> + _NODISCARD _Path_iterator<_Unwrapped_t<_Iter2>> _Unwrapped() && noexcept { + _STL_INTERNAL_STATIC_ASSERT(_Is_nothrow_unwrappable_v<_Iter2>); + return {_Position._Unwrapped(), _STD move(_Element), _Mypath}; } static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v<_Base_iter>; diff --git a/stl/inc/iterator b/stl/inc/iterator index f719511760a..8486fbee1d0 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -1371,12 +1371,19 @@ public: using _Prevent_inheriting_unwrap = counted_iterator; - _NODISCARD constexpr counted_iterator<_Unwrapped_t> - _Unwrapped() const& requires _Unwrappable_v { + // clang-format off + _NODISCARD constexpr counted_iterator<_Unwrapped_t> _Unwrapped() const& + noexcept(noexcept(counted_iterator<_Unwrapped_t>{_Current._Unwrapped(), _Length})) + requires _Unwrappable_v { + // clang-format on return counted_iterator<_Unwrapped_t>{_Current._Unwrapped(), _Length}; } - _NODISCARD constexpr counted_iterator<_Unwrapped_t<_Iter>> _Unwrapped() && requires _Unwrappable_v<_Iter> { + // clang-format off + _NODISCARD constexpr counted_iterator<_Unwrapped_t<_Iter>> _Unwrapped() && + noexcept(noexcept(counted_iterator<_Unwrapped_t<_Iter>>{_STD move(_Current)._Unwrapped(), _Length})) + requires _Unwrappable_v<_Iter> { + // clang-format on return counted_iterator<_Unwrapped_t<_Iter>>{_STD move(_Current)._Unwrapped(), _Length}; } diff --git a/stl/inc/ranges b/stl/inc/ranges index eda6d3e0b15..c6fefc7d056 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2388,12 +2388,15 @@ namespace ranges { // clang-format off _NODISCARD constexpr auto _Unwrapped() const& + noexcept(noexcept(_Sentinel<_Const, false>{_Get_unwrapped(_Last)})) requires _Wrapped && _Unwrappable_v&> { // clang-format on return _Sentinel<_Const, false>{_Get_unwrapped(_Last)}; } // clang-format off - _NODISCARD constexpr auto _Unwrapped() && requires _Wrapped && _Unwrappable_v> { + _NODISCARD constexpr auto _Unwrapped() && + noexcept(noexcept(_Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last))})) + requires _Wrapped && _Unwrappable_v> { // clang-format on return _Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last))}; } @@ -2644,12 +2647,15 @@ namespace ranges { // clang-format off _NODISCARD constexpr auto _Unwrapped() const& + noexcept(noexcept(_Sentinel<_Const, false>{_Get_unwrapped(_Last), _Pred})) requires _Wrapped && _Unwrappable_v&> { // clang-format on return _Sentinel<_Const, false>{_Get_unwrapped(_Last), _Pred}; } // clang-format off - _NODISCARD constexpr auto _Unwrapped() && requires _Wrapped && _Unwrappable_v> { + _NODISCARD constexpr auto _Unwrapped() && + noexcept(noexcept(_Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last)), _Pred})) + requires _Wrapped && _Unwrappable_v> { // clang-format on return _Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last)), _Pred}; } diff --git a/stl/inc/xutility b/stl/inc/xutility index 30bcfb7b623..160f6d9255b 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -896,8 +896,15 @@ _INLINE_VAR constexpr bool _Unwrappable_v<_Iter, void_t&>()._Seek_to(_STD declval<_Iter>()._Unwrapped()))>> = _Allow_inheriting_unwrap_v<_Remove_cvref_t<_Iter>>; +template > +_INLINE_VAR constexpr bool _Is_nothrow_unwrappable_v = noexcept(_STD declval<_Iter>()._Unwrapped()); + +template +_INLINE_VAR constexpr bool _Is_nothrow_unwrappable_v<_Iter, false> = false; + template -_NODISCARD constexpr decltype(auto) _Get_unwrapped(_Iter&& _It) { +_NODISCARD constexpr decltype(auto) _Get_unwrapped(_Iter&& _It) noexcept( + !_Unwrappable_v<_Iter> || _Is_nothrow_unwrappable_v<_Iter>) { // unwrap an iterator previously subjected to _Adl_verify_range or otherwise validated if constexpr (is_pointer_v>) { // special-case pointers and arrays return _It + 0; @@ -1347,9 +1354,15 @@ public: } template , int> = 0> - _NODISCARD constexpr reverse_iterator<_Unwrapped_t> _Unwrapped() const { + _NODISCARD constexpr reverse_iterator<_Unwrapped_t> _Unwrapped() const& noexcept( + noexcept(static_cast>>(current._Unwrapped()))) { return static_cast>>(current._Unwrapped()); } + template , int> = 0> + _NODISCARD constexpr reverse_iterator<_Unwrapped_t<_BidIt2>> _Unwrapped() && noexcept( + noexcept(static_cast>>(_STD move(current)._Unwrapped()))) { + return static_cast>>(_STD move(current)._Unwrapped()); + } static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v<_BidIt>; @@ -3404,11 +3417,13 @@ public: } template , int> = 0> - _NODISCARD constexpr move_iterator<_Unwrapped_t> _Unwrapped() const& { + _NODISCARD constexpr move_iterator<_Unwrapped_t> _Unwrapped() const& noexcept( + noexcept(static_cast>>(_Current._Unwrapped()))) { return static_cast>>(_Current._Unwrapped()); } template , int> = 0> - _NODISCARD constexpr move_iterator<_Unwrapped_t<_Iter2>> _Unwrapped() && { + _NODISCARD constexpr move_iterator<_Unwrapped_t<_Iter2>> _Unwrapped() && noexcept( + noexcept(static_cast>>(_STD move(_Current)._Unwrapped()))) { return static_cast>>(_STD move(_Current)._Unwrapped()); } diff --git a/tests/std/test.lst b/tests/std/test.lst index 15b0eb8f766..4512b1ac36f 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -209,6 +209,7 @@ tests\GH_002711_Zc_alignedNew- tests\GH_002760_syncstream_memory_leak tests\GH_002769_handle_deque_block_pointers tests\GH_002789_Hash_vec_Tidy +tests\GH_002989_nothrow_unwrappable tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function tests\LWG3121_constrained_tuple_forwarding_ctor diff --git a/tests/std/tests/GH_002989_nothrow_unwrappable/env.lst b/tests/std/tests/GH_002989_nothrow_unwrappable/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_002989_nothrow_unwrappable/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp b/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp new file mode 100644 index 00000000000..a92bc561ce4 --- /dev/null +++ b/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#if _HAS_CXX17 +#include +#endif + +#if _HAS_CXX20 +#include +#endif + +using namespace std; + +#if _HAS_CXX17 +using filesystem::path; +#endif + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +struct Predicate { + template + bool operator()(const T&) const { + return true; + } +}; + +template +void do_single_test() { + // !a || b is equivalent to a => b (a implies b) + // This is written this way to avoid `if constexpr` in C++14 mode. + STATIC_ASSERT(_Unwrappable_v == _Is_nothrow_unwrappable_v); + STATIC_ASSERT(_Unwrappable_v == _Is_nothrow_unwrappable_v); + STATIC_ASSERT(noexcept(_Get_unwrapped(declval()))); + + STATIC_ASSERT(!_Unwrappable_v || _Is_nothrow_unwrappable_v == CopyUnwrapNothrow); + STATIC_ASSERT(!_Unwrappable_v || _Is_nothrow_unwrappable_v == CopyUnwrapNothrow); + STATIC_ASSERT(noexcept(_Get_unwrapped(declval())) == CopyUnwrapNothrow); + STATIC_ASSERT(noexcept(_Get_unwrapped(declval())) == CopyUnwrapNothrow); +} + +template +void do_full_test() { + do_single_test(); + do_single_test, CopyUnwrapNothrow>(); + do_single_test, CopyUnwrapNothrow>(); + +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 + using R = ranges::subrange; + + // TRANSITION, GH-2997 + do_single_test>, true>(); + // TRANSITION, GH-2997 + do_single_test>, true>(); + if constexpr (bidirectional_iterator) { + do_single_test>, CopyUnwrapNothrow>(); + } +#endif // __cpp_lib_concepts +} + +struct BidiIterUnwrapThrowing : vector::iterator { + using Base = vector::iterator; + + using Base::Base; + + using iterator_concept = bidirectional_iterator_tag; + using iterator_category = bidirectional_iterator_tag; + + BidiIterUnwrapThrowing& operator++() { + Base::operator++(); + return *this; + } + BidiIterUnwrapThrowing operator++(int) { + auto res = *this; + Base::operator++(); + return res; + } + BidiIterUnwrapThrowing& operator--() { + Base::operator--(); + return *this; + } + BidiIterUnwrapThrowing operator--(int) { + auto res = *this; + Base::operator--(); + return res; + } + + using _Prevent_inheriting_unwrap = BidiIterUnwrapThrowing; + + int* _Unwrapped() const& noexcept(false) { + return Base::_Unwrapped(); + } + int* _Unwrapped() && noexcept { + return move(*this).Base::_Unwrapped(); + } + + void _Seek_to(int* p) & noexcept { + Base::_Seek_to(p); + } +}; + +int main() { + do_single_test(); + do_full_test(); + do_single_test(); + + do_full_test::iterator>(); + do_full_test::const_iterator>(); + do_full_test::iterator>(); + do_full_test::const_iterator>(); + + do_full_test(); +#if _HAS_CXX17 + do_full_test(); +#endif +}