Skip to content
15 changes: 11 additions & 4 deletions stl/inc/filesystem
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1600,8 +1602,13 @@ namespace filesystem {
using _Prevent_inheriting_unwrap = _Path_iterator;

template <class _Iter2 = _Base_iter, enable_if_t<_Unwrappable_v<const _Iter2&>, int> = 0>
_NODISCARD _Path_iterator<_Unwrapped_t<const _Iter2&>> _Unwrapped() const {
return {_Position._Unwrapped(), _Element.native(), _Mypath};
_NODISCARD _Path_iterator<_Unwrapped_t<const _Iter2&>> _Unwrapped() const& {
return {_Position._Unwrapped(), _Element, _Mypath};
}
template <class _Iter2 = _Base_iter, enable_if_t<_Unwrappable_v<_Iter2>, 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>;
Expand Down
13 changes: 10 additions & 3 deletions stl/inc/iterator
Original file line number Diff line number Diff line change
Expand Up @@ -1371,12 +1371,19 @@ public:

using _Prevent_inheriting_unwrap = counted_iterator;

_NODISCARD constexpr counted_iterator<_Unwrapped_t<const _Iter&>>
_Unwrapped() const& requires _Unwrappable_v<const _Iter&> {
// clang-format off
_NODISCARD constexpr counted_iterator<_Unwrapped_t<const _Iter&>> _Unwrapped() const&
noexcept(noexcept(counted_iterator<_Unwrapped_t<const _Iter&>>{_Current._Unwrapped(), _Length}))
requires _Unwrappable_v<const _Iter&> {
// clang-format on
return counted_iterator<_Unwrapped_t<const _Iter&>>{_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};
}

Expand Down
10 changes: 8 additions & 2 deletions stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -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<const iterator_t<_Base_t>&> {
// clang-format on
return _Sentinel<_Const, false>{_Get_unwrapped(_Last)};
}
// clang-format off
_NODISCARD constexpr auto _Unwrapped() && requires _Wrapped && _Unwrappable_v<iterator_t<_Base_t>> {
_NODISCARD constexpr auto _Unwrapped() &&
noexcept(noexcept(_Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last))}))
requires _Wrapped && _Unwrappable_v<iterator_t<_Base_t>> {
// clang-format on
return _Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last))};
}
Expand Down Expand Up @@ -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<const iterator_t<_Base_t>&> {
// clang-format on
return _Sentinel<_Const, false>{_Get_unwrapped(_Last), _Pred};
}
// clang-format off
_NODISCARD constexpr auto _Unwrapped() && requires _Wrapped && _Unwrappable_v<iterator_t<_Base_t>> {
_NODISCARD constexpr auto _Unwrapped() &&
noexcept(noexcept(_Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last)), _Pred}))
requires _Wrapped && _Unwrappable_v<iterator_t<_Base_t>> {
// clang-format on
return _Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last)), _Pred};
}
Expand Down
23 changes: 19 additions & 4 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -896,8 +896,15 @@ _INLINE_VAR constexpr bool _Unwrappable_v<_Iter,
void_t<decltype(_STD declval<_Remove_cvref_t<_Iter>&>()._Seek_to(_STD declval<_Iter>()._Unwrapped()))>> =
_Allow_inheriting_unwrap_v<_Remove_cvref_t<_Iter>>;

template <class _Iter, bool _Unwrappable = _Unwrappable_v<_Iter>>
_INLINE_VAR constexpr bool _Is_nothrow_unwrappable_v = noexcept(_STD declval<_Iter>()._Unwrapped());

template <class _Iter>
_INLINE_VAR constexpr bool _Is_nothrow_unwrappable_v<_Iter, false> = false;

template <class _Iter>
_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<decay_t<_Iter>>) { // special-case pointers and arrays
return _It + 0;
Expand Down Expand Up @@ -1347,9 +1354,15 @@ public:
}

template <class _BidIt2 = _BidIt, enable_if_t<_Unwrappable_v<const _BidIt2&>, int> = 0>
_NODISCARD constexpr reverse_iterator<_Unwrapped_t<const _BidIt2&>> _Unwrapped() const {
_NODISCARD constexpr reverse_iterator<_Unwrapped_t<const _BidIt2&>> _Unwrapped() const& noexcept(
noexcept(static_cast<reverse_iterator<_Unwrapped_t<const _BidIt2&>>>(current._Unwrapped()))) {
return static_cast<reverse_iterator<_Unwrapped_t<const _BidIt2&>>>(current._Unwrapped());
}
template <class _BidIt2 = _BidIt, enable_if_t<_Unwrappable_v<_BidIt2>, int> = 0>
_NODISCARD constexpr reverse_iterator<_Unwrapped_t<_BidIt2>> _Unwrapped() && noexcept(
noexcept(static_cast<reverse_iterator<_Unwrapped_t<_BidIt2>>>(_STD move(current)._Unwrapped()))) {
return static_cast<reverse_iterator<_Unwrapped_t<_BidIt2>>>(_STD move(current)._Unwrapped());
}

static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v<_BidIt>;

Expand Down Expand Up @@ -3404,11 +3417,13 @@ public:
}

template <class _Iter2 = iterator_type, enable_if_t<_Unwrappable_v<const _Iter2&>, int> = 0>
_NODISCARD constexpr move_iterator<_Unwrapped_t<const _Iter2&>> _Unwrapped() const& {
_NODISCARD constexpr move_iterator<_Unwrapped_t<const _Iter2&>> _Unwrapped() const& noexcept(
noexcept(static_cast<move_iterator<_Unwrapped_t<const _Iter2&>>>(_Current._Unwrapped()))) {
return static_cast<move_iterator<_Unwrapped_t<const _Iter2&>>>(_Current._Unwrapped());
}
template <class _Iter2 = iterator_type, enable_if_t<_Unwrappable_v<_Iter2>, int> = 0>
_NODISCARD constexpr move_iterator<_Unwrapped_t<_Iter2>> _Unwrapped() && {
_NODISCARD constexpr move_iterator<_Unwrapped_t<_Iter2>> _Unwrapped() && noexcept(
noexcept(static_cast<move_iterator<_Unwrapped_t<_Iter2>>>(_STD move(_Current)._Unwrapped()))) {
return static_cast<move_iterator<_Unwrapped_t<_Iter2>>>(_STD move(_Current)._Unwrapped());
}

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/GH_002989_nothrow_unwrappable/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_matrix.lst
120 changes: 120 additions & 0 deletions tests/std/tests/GH_002989_nothrow_unwrappable/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <list>
#include <type_traits>
#include <vector>
#include <xutility>

#if _HAS_CXX17
#include <filesystem>
#endif

#if _HAS_CXX20
#include <ranges>
#endif

using namespace std;

#if _HAS_CXX17
using filesystem::path;
#endif

#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)

struct Predicate {
template <class T>
bool operator()(const T&) const {
return true;
}
};

template <class It, bool CopyUnwrapNothrow = true>
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<It> == _Is_nothrow_unwrappable_v<It>);
STATIC_ASSERT(_Unwrappable_v<It> == _Is_nothrow_unwrappable_v<It&&>);
STATIC_ASSERT(noexcept(_Get_unwrapped(declval<It>())));

STATIC_ASSERT(!_Unwrappable_v<It> || _Is_nothrow_unwrappable_v<const It&> == CopyUnwrapNothrow);
Comment thread
strega-nil-ms marked this conversation as resolved.
STATIC_ASSERT(!_Unwrappable_v<It> || _Is_nothrow_unwrappable_v<const It&&> == CopyUnwrapNothrow);
Comment thread
strega-nil-ms marked this conversation as resolved.
STATIC_ASSERT(noexcept(_Get_unwrapped(declval<const It&>())) == CopyUnwrapNothrow);
STATIC_ASSERT(noexcept(_Get_unwrapped(declval<const It&&>())) == CopyUnwrapNothrow);
}

template <class It, bool CopyUnwrapNothrow = true>
void do_full_test() {
do_single_test<It, CopyUnwrapNothrow>();
do_single_test<reverse_iterator<It>, CopyUnwrapNothrow>();
do_single_test<move_iterator<It>, CopyUnwrapNothrow>();

#ifdef __cpp_lib_concepts // TRANSITION, GH-395
using R = ranges::subrange<It, It>;

// TRANSITION, GH-2997
do_single_test<ranges::iterator_t<ranges::filter_view<R, Predicate>>, true>();
// TRANSITION, GH-2997
do_single_test<ranges::iterator_t<ranges::transform_view<R, Predicate>>, true>();
if constexpr (bidirectional_iterator<It>) {
do_single_test<ranges::iterator_t<ranges::reverse_view<R>>, CopyUnwrapNothrow>();
}
#endif // __cpp_lib_concepts
}

struct BidiIterUnwrapThrowing : vector<int>::iterator {
Comment thread
strega-nil-ms marked this conversation as resolved.
using Base = vector<int>::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<int>();
do_full_test<int*>();
do_single_test<int[]>();

do_full_test<vector<int>::iterator>();
do_full_test<vector<int>::const_iterator>();
do_full_test<list<int>::iterator>();
do_full_test<list<int>::const_iterator>();

do_full_test<BidiIterUnwrapThrowing, false>();
#if _HAS_CXX17
do_full_test<path::iterator, false>();
#endif
}