From 36fcea21f4e422e914e721f6fcd5e119c503fca6 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Wed, 3 Aug 2022 13:57:51 -0700 Subject: [PATCH 1/9] make _Get_unwrapped noexcept when possible additionally, add conditional noexcepts to a lot of `_Unwrapped()`s --- stl/inc/iterator | 13 ++++++++++--- stl/inc/ranges | 10 ++++++++-- stl/inc/xutility | 17 +++++++++++++---- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/stl/inc/iterator b/stl/inc/iterator index f719511760a..38b3ebc8d28 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..ac1f6aa2248 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -896,8 +896,14 @@ _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 = true; + +template +_INLINE_VAR constexpr bool _Is_nothrow_unwrappable_v<_Iter, true> = noexcept(declval<_Iter>()._Unwrapped()); + template -_NODISCARD constexpr decltype(auto) _Get_unwrapped(_Iter&& _It) { +_NODISCARD constexpr decltype(auto) _Get_unwrapped(_Iter&& _It) noexcept(_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,7 +1353,8 @@ 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()); } @@ -3404,11 +3411,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()); } From 21962a40a6d2a392909ce93478d06e99d6b7344e Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Wed, 3 Aug 2022 15:38:30 -0700 Subject: [PATCH 2/9] fix build --- stl/inc/iterator | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/iterator b/stl/inc/iterator index 38b3ebc8d28..8486fbee1d0 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -1373,7 +1373,7 @@ public: // clang-format off _NODISCARD constexpr counted_iterator<_Unwrapped_t> _Unwrapped() const& - noexcept(noexcept(counted_iterator<_Unwrapped_t>{_Current._Unwrapped(), _Length}) + noexcept(noexcept(counted_iterator<_Unwrapped_t>{_Current._Unwrapped(), _Length})) requires _Unwrappable_v { // clang-format on return counted_iterator<_Unwrapped_t>{_Current._Unwrapped(), _Length}; @@ -1381,7 +1381,7 @@ public: // clang-format off _NODISCARD constexpr counted_iterator<_Unwrapped_t<_Iter>> _Unwrapped() && - noexcept(noexcept(counted_iterator<_Unwrapped_t<_Iter>>{_STD move(_Current)._Unwrapped(), _Length}) + 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}; From 10f28224359ec7056a804ea10e7c399a0591a134 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Fri, 5 Aug 2022 11:27:06 -0700 Subject: [PATCH 3/9] add _Unwrapped() && noexcept for path::iterator --- stl/inc/filesystem | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/stl/inc/filesystem b/stl/inc/filesystem index 0130975f0ef..8c8e04aa636 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& noexcept(false) { + return {_Position._Unwrapped(), _Element, _Mypath}; + } + template , int> = 0> + _NODISCARD _Path_iterator<_Unwrapped_t<_Iter2>> _Unwrapped() && noexcept { + _STL_INTERNAL_STATIC_ASSERT(noexcept(_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>; From 3e9c4749c56e827646f802364ef5bbcc02d5431e Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Fri, 5 Aug 2022 11:48:32 -0700 Subject: [PATCH 4/9] add tests --- tests/std/test.lst | 1 + .../GH_002989_nothrow_unwrappable/env.lst | 4 ++ .../GH_002989_nothrow_unwrappable/test.cpp | 37 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 tests/std/tests/GH_002989_nothrow_unwrappable/env.lst create mode 100644 tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp 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..2de7aab2959 --- /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_17_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..e49347806b0 --- /dev/null +++ b/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +using namespace std; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +template +void do_test() { + STATIC_ASSERT(_Is_nothrow_unwrappable_v); + STATIC_ASSERT(_Is_nothrow_unwrappable_v); + STATIC_ASSERT(noexcept(_Get_unwrapped(declval()))); + + STATIC_ASSERT(_Is_nothrow_unwrappable_v == CopyUnwrapNothrow); + STATIC_ASSERT(noexcept(_Get_unwrapped(declval())) == CopyUnwrapNothrow); + STATIC_ASSERT(_Is_nothrow_unwrappable_v == CopyUnwrapNothrow); + STATIC_ASSERT(noexcept(_Get_unwrapped(declval())) == CopyUnwrapNothrow); +} + +int main() { + do_test(); + do_test(); + do_test(); + + do_test::iterator>(); + do_test::const_iterator>(); + do_test::iterator>(); + do_test::const_iterator>(); + + do_test(); +} From e53cbba6fade16d15ab929c810a47208a8a2b4ef Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Fri, 5 Aug 2022 13:40:54 -0700 Subject: [PATCH 5/9] moar testing plus, fix reverse_iterator up with an `_Unwrapped() &&` --- stl/inc/xutility | 9 ++- .../GH_002989_nothrow_unwrappable/test.cpp | 68 ++++++++++++++----- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index ac1f6aa2248..70a5162ec2e 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1353,10 +1353,15 @@ public: } template , int> = 0> - _NODISCARD constexpr reverse_iterator<_Unwrapped_t> _Unwrapped() const - noexcept(noexcept(static_cast>>(current._Unwrapped()))) { + _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>; diff --git a/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp b/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp index e49347806b0..fb2b68623f6 100644 --- a/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp +++ b/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp @@ -7,31 +7,65 @@ #include #include +#if _HAS_CXX20 +#include +#endif + using namespace std; +using filesystem::path; #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) -template -void do_test() { - STATIC_ASSERT(_Is_nothrow_unwrappable_v); - STATIC_ASSERT(_Is_nothrow_unwrappable_v); - STATIC_ASSERT(noexcept(_Get_unwrapped(declval()))); +struct Predicate { + template + bool operator()(const T&) const { + return true; + } +}; + +template +void do_single_test() { + STATIC_ASSERT(_Is_nothrow_unwrappable_v); + STATIC_ASSERT(_Is_nothrow_unwrappable_v); + STATIC_ASSERT(noexcept(_Get_unwrapped(declval()))); + + STATIC_ASSERT(_Is_nothrow_unwrappable_v == CopyUnwrapNothrow); + STATIC_ASSERT(noexcept(_Get_unwrapped(declval())) == CopyUnwrapNothrow); + STATIC_ASSERT(_Is_nothrow_unwrappable_v == 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; + + do_single_test, CopyUnwrapNothrow>(); - STATIC_ASSERT(_Is_nothrow_unwrappable_v == CopyUnwrapNothrow); - STATIC_ASSERT(noexcept(_Get_unwrapped(declval())) == CopyUnwrapNothrow); - STATIC_ASSERT(_Is_nothrow_unwrappable_v == CopyUnwrapNothrow); - STATIC_ASSERT(noexcept(_Get_unwrapped(declval())) == CopyUnwrapNothrow); + // iterator_t does not define _Unwrapped + do_single_test>, true>(); + // iterator_t does not define _Unwrapped + do_single_test>, true>(); + if constexpr (ranges::bidirectional_range) { + // iterator_t does not define _Unwrapped + do_single_test>, true>(); + } +#endif // __cpp_lib_concepts } int main() { - do_test(); - do_test(); - do_test(); + do_single_test(); + do_full_test(); + do_single_test(); - do_test::iterator>(); - do_test::const_iterator>(); - do_test::iterator>(); - do_test::const_iterator>(); + do_full_test::iterator>(); + do_full_test::const_iterator>(); + do_full_test::iterator>(); + do_full_test::const_iterator>(); - do_test(); + do_full_test(); } From 8b48dc4f2735d1f4730e132610ec165e9cb16714 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Fri, 5 Aug 2022 14:10:55 -0700 Subject: [PATCH 6/9] add more tests, use TRANSITION comments --- .../GH_002989_nothrow_unwrappable/test.cpp | 62 +++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp b/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp index fb2b68623f6..e83fcb6387c 100644 --- a/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp +++ b/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp @@ -46,17 +46,70 @@ void do_full_test() { do_single_test, CopyUnwrapNothrow>(); - // iterator_t does not define _Unwrapped + // TRANSITION, GH-2997 do_single_test>, true>(); - // iterator_t does not define _Unwrapped + // TRANSITION, GH-2997 do_single_test>, true>(); if constexpr (ranges::bidirectional_range) { - // iterator_t does not define _Unwrapped - do_single_test>, true>(); + do_single_test>, CopyUnwrapNothrow>(); } #endif // __cpp_lib_concepts } +struct BidiIterUnwrapThrowing : vector::iterator { + using _Base = vector::iterator; + + using _Base::_Base; + using _Base::iterator_category; + +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 + using _Base::iterator_concept; +#endif + + using _Base::pointer; + using _Base::reference; + using _Base::value_type; + + friend bool operator==(const BidiIterUnwrapThrowing& lhs, const BidiIterUnwrapThrowing& rhs) noexcept { + return static_cast(lhs) == static_cast(rhs); + } + friend bool operator!=(const BidiIterUnwrapThrowing& lhs, const BidiIterUnwrapThrowing& rhs) noexcept { + return static_cast(lhs) != static_cast(rhs); + } + + 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 std::move(*this)._Base::_Unwrapped(); + } + + void _Seek_to(int* p) & noexcept { + _Base::_Seek_to(p); + } +}; + int main() { do_single_test(); do_full_test(); @@ -68,4 +121,5 @@ int main() { do_full_test::const_iterator>(); do_full_test(); + do_full_test(); } From 88572556b8c213dbb32f58abe39d4a6e002acaf1 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Sat, 6 Aug 2022 09:16:00 -0700 Subject: [PATCH 7/9] Make `_Is_nothrow_unwrappable_v` more normal --- stl/inc/xutility | 4 ++-- .../std/tests/GH_002989_nothrow_unwrappable/test.cpp | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 70a5162ec2e..2c7150a1086 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -897,13 +897,13 @@ _INLINE_VAR constexpr bool _Unwrappable_v<_Iter, _Allow_inheriting_unwrap_v<_Remove_cvref_t<_Iter>>; template > -_INLINE_VAR constexpr bool _Is_nothrow_unwrappable_v = true; +_INLINE_VAR constexpr bool _Is_nothrow_unwrappable_v = false; template _INLINE_VAR constexpr bool _Is_nothrow_unwrappable_v<_Iter, true> = noexcept(declval<_Iter>()._Unwrapped()); template -_NODISCARD constexpr decltype(auto) _Get_unwrapped(_Iter&& _It) noexcept(_Is_nothrow_unwrappable_v<_Iter>) { +_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; diff --git a/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp b/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp index e83fcb6387c..e41abe9b77c 100644 --- a/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp +++ b/tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp @@ -25,13 +25,17 @@ struct Predicate { template void do_single_test() { - STATIC_ASSERT(_Is_nothrow_unwrappable_v); - STATIC_ASSERT(_Is_nothrow_unwrappable_v); + if constexpr (_Unwrappable_v) { + STATIC_ASSERT(_Is_nothrow_unwrappable_v); + STATIC_ASSERT(_Is_nothrow_unwrappable_v); + } STATIC_ASSERT(noexcept(_Get_unwrapped(declval()))); - STATIC_ASSERT(_Is_nothrow_unwrappable_v == CopyUnwrapNothrow); + if constexpr (_Unwrappable_v) { + STATIC_ASSERT(_Is_nothrow_unwrappable_v == CopyUnwrapNothrow); + STATIC_ASSERT(_Is_nothrow_unwrappable_v == CopyUnwrapNothrow); + } STATIC_ASSERT(noexcept(_Get_unwrapped(declval())) == CopyUnwrapNothrow); - STATIC_ASSERT(_Is_nothrow_unwrappable_v == CopyUnwrapNothrow); STATIC_ASSERT(noexcept(_Get_unwrapped(declval())) == CopyUnwrapNothrow); } From 187ef5310dc24aaadb1bbe14c0ccf0c3f5c91abe Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Sat, 6 Aug 2022 10:27:24 -0700 Subject: [PATCH 8/9] support unwrapping `filter_view::iterator` This is an example of what we might do; it definitely requires discussion --- stl/inc/ranges | 80 +++++++---- stl/inc/xutility | 9 +- tests/std/test.lst | 1 + .../env.lst | 4 + .../test.cpp | 124 ++++++++++++++++++ 5 files changed, 192 insertions(+), 26 deletions(-) create mode 100644 tests/std/tests/GH_002997_unwrapped_view_iterators/env.lst create mode 100644 tests/std/tests/GH_002997_unwrapped_view_iterators/test.cpp diff --git a/stl/inc/ranges b/stl/inc/ranges index 221c753adc2..a521995e4b0 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1917,18 +1917,19 @@ namespace ranges { input_iterator_tag>; }; - template + template class _Iterator : public _Category_base<_Const> { private: - template + template friend class _Iterator; template friend class _Sentinel; - using _Parent_t = _Maybe_const<_Const, transform_view>; - using _Base = _Maybe_const<_Const, _Vw>; + using _Parent_t = _Maybe_const<_Const, transform_view>; + using _Base = _Maybe_const<_Const, _Vw>; + using _Base_iter = _Maybe_unwrapped_iterator_t<_Base, _Is_unwrapped>; - iterator_t<_Base> _Current{}; + _Base_iter _Current{}; _Parent_t* _Parent{}; #if _ITERATOR_DEBUG_LEVEL != 0 @@ -1952,11 +1953,11 @@ namespace ranges { using difference_type = range_difference_t<_Base>; // clang-format off - _Iterator() requires default_initializable> = default; + _Iterator() requires default_initializable<_Base_iter> = default; // clang-format on - constexpr _Iterator(_Parent_t& _Parent_, iterator_t<_Base> _Current_) noexcept( - is_nothrow_move_constructible_v>) // strengthened + constexpr _Iterator(_Parent_t& _Parent_, _Base_iter _Current_) noexcept( + is_nothrow_move_constructible_v<_Base_iter>) // strengthened : _Current{_STD move(_Current_)}, _Parent{_STD addressof(_Parent_)} { #if _ITERATOR_DEBUG_LEVEL != 0 _Adl_verify_range(_Current, _RANGES end(_Parent_._Range)); @@ -1967,17 +1968,18 @@ namespace ranges { } // clang-format off - constexpr _Iterator(_Iterator _It) - noexcept(is_nothrow_constructible_v, iterator_t<_Vw>>) // strengthened - requires _Const && convertible_to, iterator_t<_Base>> + constexpr _Iterator(_Iterator _It) + noexcept(is_nothrow_constructible_v<_Base_iter, _Base_iter>) // strengthened + requires _Const + && convertible_to<_Maybe_unwrapped_iterator_t<_Vw, _Is_unwrapped>, _Base_iter> : _Current{_STD move(_It._Current)}, _Parent{_It._Parent} {} // clang-format on - _NODISCARD constexpr const iterator_t<_Base>& base() const& noexcept { + _NODISCARD constexpr const _Base_iter& base() const& noexcept { return _Current; } - _NODISCARD constexpr iterator_t<_Base> base() && noexcept( - is_nothrow_move_constructible_v>) /* strengthened */ { + _NODISCARD constexpr _Base_iter base() && noexcept( + is_nothrow_move_constructible_v<_Base_iter>) /* strengthened */ { return _STD move(_Current); } @@ -2003,7 +2005,7 @@ namespace ranges { constexpr decltype(auto) operator++(int) noexcept( noexcept(++_Current) - && (!forward_range<_Base> || is_nothrow_copy_constructible_v>) ) /* strengthened */ { + && (!forward_range<_Base> || is_nothrow_copy_constructible_v<_Base_iter>) ) /* strengthened */ { if constexpr (forward_range<_Base>) { auto _Tmp = *this; ++*this; @@ -2026,7 +2028,7 @@ namespace ranges { return *this; } constexpr _Iterator operator--(int) noexcept( - noexcept(--_Current) && is_nothrow_copy_constructible_v>) /* strengthened */ + noexcept(--_Current) && is_nothrow_copy_constructible_v<_Base_iter>) /* strengthened */ requires bidirectional_range<_Base> { auto _Tmp = *this; --*this; @@ -2039,20 +2041,19 @@ namespace ranges { #else // ^^^ _ITERATOR_DEBUG_LEVEL == 0 / _ITERATOR_DEBUG_LEVEL != 0 vvv _STL_VERIFY(_Off == 0 || _Parent, "cannot seek value-initialized transform_view iterator"); - if constexpr (_Offset_verifiable_v>) { + if constexpr (_Offset_verifiable_v<_Base_iter>) { _Current._Verify_offset(_Off); } else { if (_Off < 0) { - if constexpr (sized_sentinel_for, iterator_t<_Base>>) { + if constexpr (sized_sentinel_for<_Base_iter, _Base_iter>) { _STL_VERIFY(_Off >= _RANGES begin(_Parent->_Range) - _Current, "cannot seek transform_view iterator before begin"); } } else if (_Off > 0) { - if constexpr (sized_sentinel_for, iterator_t<_Base>>) { + if constexpr (sized_sentinel_for, _Base_iter>) { _STL_VERIFY(_Off <= _RANGES end(_Parent->_Range) - _Current, "cannot seek transform_view iterator after end"); - } else if constexpr (sized_sentinel_for, - iterator_t<_Base>> && sized_range<_Base>) { + } else if constexpr (sized_sentinel_for<_Base_iter, _Base_iter> && sized_range<_Base>) { const auto _Size = _RANGES distance(_Parent->_Range); _STL_VERIFY(_Off <= _Size - (_Current - _RANGES begin(_Parent->_Range)), "cannot seek transform_view iterator after end"); @@ -2091,8 +2092,8 @@ namespace ranges { } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Left._Current - == _Right._Current)) /* strengthened */ requires equality_comparable> { + noexcept( + _Left._Current == _Right._Current)) /* strengthened */ requires equality_comparable<_Base_iter> { #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Same_range(_Right); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -2121,7 +2122,7 @@ namespace ranges { // clang-format off _NODISCARD_FRIEND constexpr auto operator<=>(const _Iterator& _Left, const _Iterator& _Right) noexcept( noexcept(_Left._Current <=> _Right._Current)) /* strengthened */ - requires random_access_range<_Base> && three_way_comparable> { + requires random_access_range<_Base> && three_way_comparable<_Base_iter> { // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Same_range(_Right); @@ -2157,12 +2158,41 @@ namespace ranges { _NODISCARD_FRIEND constexpr difference_type operator-(const _Iterator& _Left, const _Iterator& _Right) noexcept(noexcept(_Left._Current - _Right._Current)) /* strengthened */ - requires sized_sentinel_for, iterator_t<_Base>> { + requires sized_sentinel_for<_Base_iter, _Base_iter> { #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Same_range(_Right); #endif // _ITERATOR_DEBUG_LEVEL != 0 return _Left._Current - _Right._Current; } + + using _Prevent_inheriting_unwrap = + conditional_t, _Iterator, void>; + + // clang-format off + _Iterator<_Const, true> _Unwrapped() const& + noexcept(noexcept(_Iterator<_Const, true>{*_Parent, _Current._Unwrapped()})) + requires _Unwrappable_v + { + // clang-format on + return _Iterator<_Const, true>{*_Parent, _Current._Unwrapped()}; + } + // clang-format off + _Iterator<_Const, true> _Unwrapped() && + noexcept(noexcept(_Iterator<_Const, true>{*_Parent, _STD move(_Current)._Unwrapped()})) + requires _Unwrappable_v<_Base_iter> + { + // clang-format on + return _Iterator<_Const, true>{*_Parent, _STD move(_Current)._Unwrapped()}; + } + + // clang-format off + void _Seek_to(_Iterator<_Const, true> _Other) + noexcept(noexcept(_Current._Seek_to(_Other._Current))) + requires _Unwrappable_v<_Base_iter> + { + // clang-format on + _Current._Seek_to(_Other._Current); + } }; template diff --git a/stl/inc/xutility b/stl/inc/xutility index 7c35cd1d736..a51a09ac936 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -919,7 +919,8 @@ template _INLINE_VAR constexpr bool _Is_nothrow_unwrappable_v<_Iter, true> = noexcept(declval<_Iter>()._Unwrapped()); template -_NODISCARD constexpr decltype(auto) _Get_unwrapped(_Iter&& _It) noexcept(!_Unwrappable_v<_Iter> || _Is_nothrow_unwrappable_v<_Iter>) { +_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; @@ -932,6 +933,8 @@ _NODISCARD constexpr decltype(auto) _Get_unwrapped(_Iter&& _It) noexcept(!_Unwra template using _Unwrapped_t = _Remove_cvref_t()))>; +template +using _Maybe_unwrapped_t = conditional_t<_Is_unwrapped, _Unwrapped_t<_Iter>, _Remove_cvref_t<_Iter>>; template _INLINE_VAR constexpr bool _Do_unwrap_when_unverified_v = false; @@ -1740,6 +1743,10 @@ namespace ranges { template using iterator_t = decltype(_RANGES begin(_STD declval<_Ty&>())); + template + using _Unwrapped_iterator_t = _Unwrapped_t>; + template + using _Maybe_unwrapped_iterator_t = _Maybe_unwrapped_t, _Is_unwrapped>; namespace _Unchecked_begin { template diff --git a/tests/std/test.lst b/tests/std/test.lst index 4c422039995..bcdc71fa7d0 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -210,6 +210,7 @@ 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\GH_002997_unwrapped_view_iterators tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function tests\LWG3121_constrained_tuple_forwarding_ctor diff --git a/tests/std/tests/GH_002997_unwrapped_view_iterators/env.lst b/tests/std/tests/GH_002997_unwrapped_view_iterators/env.lst new file mode 100644 index 00000000000..7b6bcff4830 --- /dev/null +++ b/tests/std/tests/GH_002997_unwrapped_view_iterators/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_20_matrix.lst diff --git a/tests/std/tests/GH_002997_unwrapped_view_iterators/test.cpp b/tests/std/tests/GH_002997_unwrapped_view_iterators/test.cpp new file mode 100644 index 00000000000..42bc81d2ab2 --- /dev/null +++ b/tests/std/tests/GH_002997_unwrapped_view_iterators/test.cpp @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +using namespace std; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +template +struct UnwrappableBase : vector::iterator { + using _Base = vector::iterator; + + using _Base::_Base; + + Derived& operator++() { + _Base::operator++(); + return static_cast(*this); + } + Derived operator++(int) { + auto res = static_cast(*this); + _Base::operator++(); + return res; + } + Derived& operator--() { + _Base::operator--(); + return static_cast(*this); + } + Derived operator--(int) { + auto res = static_cast(*this); + _Base::operator--(); + return res; + } + + using _Prevent_inheriting_unwrap = Derived; + int* _Unwrapped() const& noexcept(false) requires !MoveOnly { + return _Base::_Unwrapped(); + } + int* _Unwrapped() && noexcept { + return _Base::_Unwrapped(); + } + void _Seek_to(int* _It) noexcept { + _Base::_Seek_to(_It); + } + + bool operator==(const UnwrappableBase&) const = default; + auto operator<=>(const UnwrappableBase&) const = default; +}; + +struct ThrowingUnwrappable : UnwrappableBase { + using UnwrappableBase::UnwrappableBase; + + bool operator==(const ThrowingUnwrappable&) const = default; + auto operator<=>(const ThrowingUnwrappable&) const = default; +}; +struct MoveUnwrappable : UnwrappableBase { + using UnwrappableBase::UnwrappableBase; + + bool operator==(const MoveUnwrappable&) const = default; + auto operator<=>(const MoveUnwrappable&) const = default; +}; + +template