From 356a73340f2e13ec929f0ab0796aaf2dcd581412 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 18 Sep 2024 12:55:16 -0700 Subject: [PATCH 01/12] Improve formatting of `_Default_allocate_traits` Define a new macro instead of conditionally inserting `_CONSTEXPR20`. Drive-by: Combine nested conditionals. --- stl/inc/xmemory | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 4dd52208f84..f2acc82c271 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -92,30 +92,24 @@ _NODISCARD constexpr size_t _Get_size_of_n(const size_t _Count) { template constexpr size_t _New_alignof = (_STD max)(alignof(_Ty), __STDCPP_DEFAULT_NEW_ALIGNMENT__); -struct _Default_allocate_traits { - __declspec(allocator) static #ifdef __clang__ // Clang and MSVC implement P0784R7 differently; see GH-1532 - _CONSTEXPR20 -#endif // defined(__clang__) - void* - _Allocate(const size_t _Bytes) { +#define _CLANG_CONSTEXPR20 _CONSTEXPR20 +#else // ^^^ Clang / Other vvv +#define _CLANG_CONSTEXPR20 +#endif // ^^^ Other ^^^ + +struct _Default_allocate_traits { + __declspec(allocator) static _CLANG_CONSTEXPR20 void* _Allocate(const size_t _Bytes) { return ::operator new(_Bytes); } #ifdef __cpp_aligned_new - __declspec(allocator) static -#ifdef __clang__ // Clang and MSVC implement P0784R7 differently; see GH-1532 - _CONSTEXPR20 -#endif // defined(__clang__) - void* - _Allocate_aligned(const size_t _Bytes, const size_t _Align) { -#ifdef __clang__ // Clang and MSVC implement P0784R7 differently; see GH-1532 -#if _HAS_CXX20 + __declspec(allocator) static _CLANG_CONSTEXPR20 void* _Allocate_aligned(const size_t _Bytes, const size_t _Align) { +#if defined(__clang__) && _HAS_CXX20 // Ditto, "Clang and MSVC implement P0784R7 differently" if (_STD is_constant_evaluated()) { return ::operator new(_Bytes); } else -#endif // _HAS_CXX20 -#endif // defined(__clang__) +#endif // ^^^ defined(__clang__) && _HAS_CXX20 ^^^ { return ::operator new(_Bytes, align_val_t{_Align}); } @@ -123,6 +117,8 @@ struct _Default_allocate_traits { #endif // defined(__cpp_aligned_new) }; +#undef _CLANG_CONSTEXPR20 + constexpr bool _Is_pow_2(const size_t _Value) noexcept { return _Value != 0 && (_Value & (_Value - 1)) == 0; } From 9b9746c76c71789ee0d3cc8429178008417dfd73 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 18 Sep 2024 16:24:52 -0700 Subject: [PATCH 02/12] Fix `TRANSITION` comment for disabled EDG config in `P1502R1_standard_library_header_units` --- tests/std/tests/P1502R1_standard_library_header_units/env.lst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P1502R1_standard_library_header_units/env.lst b/tests/std/tests/P1502R1_standard_library_header_units/env.lst index c2a7a19114b..90f83ac9b67 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/env.lst +++ b/tests/std/tests/P1502R1_standard_library_header_units/env.lst @@ -19,4 +19,4 @@ RUNALL_CROSSLIST PM_CL="" ASAN PM_CL="-fsanitize=address /Zi" PM_LINK="/debug" # PM_CL="/analyze:only /analyze:autolog-" # TRANSITION, works correctly but slowly -# PM_CL="/BE" # TRANSITION, VSO-1232145 "EDG ICEs when consuming Standard Library Header Units" +# PM_CL="/BE" # TRANSITION, GH-1621 (See Modules (Including Header Units)) From d7147220592bbf45325ccec45f79f9e912a8beed Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 18 Sep 2024 21:20:22 -0700 Subject: [PATCH 03/12] Annotate Clang workaround `TRANSITION, Clang 19` Discovered this has been fixed in 19.1.0 after reducing a repro to file. --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index bb5eca79ce3..37bb02261d7 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -7158,7 +7158,7 @@ namespace ranges { private: friend zip_view; -#ifdef __clang__ // TRANSITION, need to reduce and report (was thought to be LLVM-61763) +#if defined(__clang__) && __clang_major__ <= 18 // TRANSITION, Clang 19 (was thought to be LLVM-61763) public: #else // ^^^ workaround / no workaround vvv template @@ -7983,7 +7983,7 @@ namespace ranges { private: friend adjacent_view; -#ifdef __clang__ // TRANSITION, need to reduce and report (was thought to be LLVM-61763) +#if defined(__clang__) && __clang_major__ <= 18 // TRANSITION, Clang 19 (was thought to be LLVM-61763) public: #else // ^^^ workaround / no workaround vvv template From 8b66c5eea38b04950eb47328153700740812ae6a Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 18 Sep 2024 12:48:05 -0700 Subject: [PATCH 04/12] Remove extraneous `constexpr` in `P2502R2_generator` --- tests/std/tests/P2502R2_generator/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P2502R2_generator/test.cpp b/tests/std/tests/P2502R2_generator/test.cpp index 5ad637b4fb1..48c52412604 100644 --- a/tests/std/tests/P2502R2_generator/test.cpp +++ b/tests/std/tests/P2502R2_generator/test.cpp @@ -183,12 +183,12 @@ generator iota_repeater(const int hi, const int depth) { } void recursive_test() { - constexpr auto might_throw = []() -> generator { + auto might_throw = []() -> generator { co_yield 0; throw runtime_error{"error"}; }; - constexpr auto nested_ints = [=]() -> generator { + auto nested_ints = [=]() -> generator { try { co_yield ranges::elements_of(might_throw()); } catch (const runtime_error& e) { From 0a20ee865d9bd51aa0995df9671d4c4e34434b99 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 6 Oct 2024 10:53:29 -0700 Subject: [PATCH 05/12] Simplify `unreachable_sentinel_t` We had to workaround the fact that hidden friends aren't hidden in permissive mode by stuffing `unreachable_sentinel_t`'s `operator==` into a detail namespace. Now that the changes to C++20 relational expressions are implemented, we can use a member `operator==` instead of a `friend` to more simply achieve the same effect. See https://godbolt.org/z/x5ffjdxYf for a proof-of-concept. --- stl/inc/xutility | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 206b23557d1..6a7028b326e 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4523,16 +4523,11 @@ template requires (!sized_sentinel_for<_Iter1, _Iter2>) constexpr bool disable_sized_sentinel_for, move_iterator<_Iter2>> = true; -_EXPORT_STD struct unreachable_sentinel_t; -namespace _Unreachable_sentinel_detail { - struct _Base { - template - _NODISCARD friend constexpr bool operator==(const unreachable_sentinel_t&, const _Winc&) noexcept { - return false; - } - }; -} // namespace _Unreachable_sentinel_detail -_EXPORT_STD struct unreachable_sentinel_t : _Unreachable_sentinel_detail::_Base {}; // TRANSITION, /permissive- +_EXPORT_STD struct unreachable_sentinel_t { + _NODISCARD constexpr bool operator==(const weakly_incrementable auto&) const noexcept { + return false; + } +}; _EXPORT_STD inline constexpr unreachable_sentinel_t unreachable_sentinel{}; #endif // _HAS_CXX20 From 5d34647016cf40beeea158ceeaaf6c7341eceffc Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 6 Oct 2024 11:02:59 -0700 Subject: [PATCH 06/12] Uniformly annotate undefined members of fake iterators --- stl/inc/__msvc_ranges_to.hpp | 7 +++---- stl/inc/__msvc_ranges_tuple_formatter.hpp | 2 +- stl/inc/print | 6 ++++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/stl/inc/__msvc_ranges_to.hpp b/stl/inc/__msvc_ranges_to.hpp index e6c76ee1b13..dcd1482ccdb 100644 --- a/stl/inc/__msvc_ranges_to.hpp +++ b/stl/inc/__msvc_ranges_to.hpp @@ -1186,12 +1186,11 @@ namespace ranges { using pointer = add_pointer_t>; using reference = range_reference_t<_Rng>; - reference operator*() const; - pointer operator->() const; - + // These member functions are never defined: + range_reference_t<_Rng> operator*() const; + add_pointer_t> operator->() const; _Phony_input_iterator& operator++(); _Phony_input_iterator operator++(int); - bool operator==(const _Phony_input_iterator&) const; }; diff --git a/stl/inc/__msvc_ranges_tuple_formatter.hpp b/stl/inc/__msvc_ranges_tuple_formatter.hpp index ca03945e9da..2f1ea373b8b 100644 --- a/stl/inc/__msvc_ranges_tuple_formatter.hpp +++ b/stl/inc/__msvc_ranges_tuple_formatter.hpp @@ -880,8 +880,8 @@ template struct _Phony_fmt_iter_for { using difference_type = ptrdiff_t; + // These member functions are never defined: _CharT& operator*() const; - _Phony_fmt_iter_for& operator++(); _Phony_fmt_iter_for operator++(int); }; diff --git a/stl/inc/print b/stl/inc/print index 6e6b15b9e83..1941395f6c7 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -48,7 +48,8 @@ public: explicit _Print_to_stream_it(FILE* const _Stream_) noexcept : _Stream(_Stream_) {} - _Print_to_stream_it& operator=(char); // These members are intentionally not defined + // These member functions are never defined: + _Print_to_stream_it& operator=(char); _Print_to_stream_it& operator*(); _Print_to_stream_it& operator++(); _Print_to_stream_it operator++(int); @@ -88,7 +89,8 @@ public: explicit _Print_to_unicode_console_it(const __std_unicode_console_handle _Console_handle_) noexcept : _Console_handle(_Console_handle_) {} - _Print_to_unicode_console_it& operator=(char); // These members are intentionally not defined + // These member functions are never defined: + _Print_to_unicode_console_it& operator=(char); _Print_to_unicode_console_it& operator*(); _Print_to_unicode_console_it& operator++(); _Print_to_unicode_console_it operator++(int); From a1f56a68cfdea36ee30c6f54b416ec6d4b08e646 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 6 Oct 2024 11:49:42 -0700 Subject: [PATCH 07/12] Cleanup some iterator definitions * Remove nested type names that are extraneous in C++20 to help us avoid depending on the existence of said nested type names. * Always declare the "nested 5 iterator traits" (or the subset of them a given iterator provides) in canonical order at the top of the first public section in a class body. * When the "nested 5 iterator traits" _are_ available, use their names in the semantically appropriate places in member function declarations. For example, `reference operator*() const`. --- stl/inc/__msvc_ranges_to.hpp | 7 ++----- stl/inc/format | 18 +++++++++--------- stl/inc/print | 12 ++---------- tests/std/include/range_algorithm_support.hpp | 5 ++--- .../test.compile.pass.cpp | 16 ++++++++-------- 5 files changed, 23 insertions(+), 35 deletions(-) diff --git a/stl/inc/__msvc_ranges_to.hpp b/stl/inc/__msvc_ranges_to.hpp index dcd1482ccdb..ef3bac4ed67 100644 --- a/stl/inc/__msvc_ranges_to.hpp +++ b/stl/inc/__msvc_ranges_to.hpp @@ -1180,11 +1180,8 @@ namespace ranges { template struct _Phony_input_iterator { - using iterator_category = input_iterator_tag; - using value_type = range_value_t<_Rng>; - using difference_type = ptrdiff_t; - using pointer = add_pointer_t>; - using reference = range_reference_t<_Rng>; + using value_type = range_value_t<_Rng>; + using difference_type = ptrdiff_t; // These member functions are never defined: range_reference_t<_Rng> operator*() const; diff --git a/stl/inc/format b/stl/inc/format index 70429d4eefe..a7e917969d2 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -398,15 +398,15 @@ private: _GB11_LeftHand_regex _GB11_rx; public: + using value_type = iter_value_t<_Wrapped_iter_type>; + using difference_type = ptrdiff_t; + _NODISCARD constexpr bool operator==(default_sentinel_t) const noexcept { return _WrappedIter == default_sentinel; } _NODISCARD constexpr bool operator==(const _Grapheme_break_property_iterator&) const noexcept = default; - using difference_type = ptrdiff_t; - using value_type = iter_value_t<_Wrapped_iter_type>; - constexpr _Grapheme_break_property_iterator(const _CharT* _First, const _CharT* _Last) : _WrappedIter(_First, _Last) {} @@ -2388,15 +2388,15 @@ private: _Grapheme_break_property_iterator<_CharT> _WrappedIter; public: + using value_type = int; + using difference_type = ptrdiff_t; + _NODISCARD constexpr bool operator==(const _Measure_string_prefix_iterator_utf&) const noexcept = default; _NODISCARD constexpr bool operator==(default_sentinel_t) const noexcept { return _WrappedIter == default_sentinel; } - using difference_type = ptrdiff_t; - using value_type = int; - constexpr _Measure_string_prefix_iterator_utf(const _CharT* _First, const _CharT* _Last) : _WrappedIter(_First, _Last) {} @@ -2438,6 +2438,9 @@ private: } public: + using value_type = int; + using difference_type = ptrdiff_t; + _NODISCARD bool operator==(default_sentinel_t) const noexcept { return _First == _Last; } @@ -2445,9 +2448,6 @@ public: return _First == _Rhs._First && _Last == _Rhs._Last; } - using difference_type = ptrdiff_t; - using value_type = int; - _Measure_string_prefix_iterator_legacy(const char* _First_val, const char* _Last_val) : _First(_First_val), _Last(_Last_val) { _Update_units(); diff --git a/stl/inc/print b/stl/inc/print index 1941395f6c7..7111b5558d8 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -40,11 +40,7 @@ struct _NODISCARD _Stream_lock_guard { class _Print_to_stream_it { public: - using iterator_category = output_iterator_tag; - using value_type = void; - using difference_type = ptrdiff_t; - using pointer = void; - using reference = void; + using difference_type = ptrdiff_t; explicit _Print_to_stream_it(FILE* const _Stream_) noexcept : _Stream(_Stream_) {} @@ -80,11 +76,7 @@ struct _Fmt_iterator_flush<_Print_to_stream_it> { class _Print_to_unicode_console_it { public: - using iterator_category = output_iterator_tag; - using value_type = void; - using difference_type = ptrdiff_t; - using pointer = void; - using reference = void; + using difference_type = ptrdiff_t; explicit _Print_to_unicode_console_it(const __std_unicode_console_handle _Console_handle_) noexcept : _Console_handle(_Console_handle_) {} diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index a94ef4ba0bc..d4f430bfbbb 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -367,9 +367,8 @@ namespace test { template struct init_list_not_constructible_iterator { - using iterator_category = std::forward_iterator_tag; - using difference_type = int; - using value_type = T; + using value_type = T; + using difference_type = int; init_list_not_constructible_iterator() = default; init_list_not_constructible_iterator(T*) {} diff --git a/tests/std/tests/GH_004109_iter_value_t_direct_initialization/test.compile.pass.cpp b/tests/std/tests/GH_004109_iter_value_t_direct_initialization/test.compile.pass.cpp index 99a3593c6a9..bd105751006 100644 --- a/tests/std/tests/GH_004109_iter_value_t_direct_initialization/test.compile.pass.cpp +++ b/tests/std/tests/GH_004109_iter_value_t_direct_initialization/test.compile.pass.cpp @@ -40,12 +40,12 @@ struct Val { struct I { using iterator_category = random_access_iterator_tag; - using pointer = Val*; using value_type = Val; - using reference = Val&; using difference_type = int; - Val& operator*() const; - Val& operator[](int) const; + using pointer = void; + using reference = Val&; + reference operator*() const; + reference operator[](int) const; I& operator++(); I operator++(int); I& operator--(); @@ -58,10 +58,10 @@ struct I { friend bool operator<=(const I&, const I&); friend bool operator>(const I&, const I&); friend bool operator>=(const I&, const I&); - friend int operator-(I, I); - friend I operator+(I, int); - friend I operator-(I, int); - friend I operator+(int, I); + friend difference_type operator-(I, I); + friend I operator+(I, difference_type); + friend I operator-(I, difference_type); + friend I operator+(difference_type, I); friend RRef iter_move(const I&); #if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-1941943 From 5df19355508fccb3f6f866f452918cb5a2faa072 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sat, 12 Oct 2024 22:18:14 -0700 Subject: [PATCH 08/12] Rename `_Merge_move` to `_Merge_move_unchecked` For consistency with other algorithms that take unwrapped iterators. --- stl/inc/algorithm | 4 ++-- stl/inc/execution | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c16afa7d042..139dbcae8c0 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -8487,7 +8487,7 @@ _Ty* _Uninitialized_merge_move(_FwdIt _First, const _FwdIt _Mid, const _FwdIt _L } template -_OutIt _Merge_move(_InIt _First, const _InIt _Mid, const _InIt _Last, _OutIt _Dest, _Pr _Pred) { +_OutIt _Merge_move_unchecked(_InIt _First, const _InIt _Mid, const _InIt _Last, _OutIt _Dest, _Pr _Pred) { // move merging adjacent ranges [_First, _Mid) and [_Mid, _Last) to _Dest // pre: _First != _Mid && _Mid != _Last _InIt _Next = _Mid; @@ -8545,7 +8545,7 @@ void _Chunked_merge_unchecked(_BidIt _First, const _BidIt _Last, _OutIt _Dest, c const auto _Chunk2 = (_STD min)(_Chunk, _Count); _Count -= _Chunk2; const _BidIt _Mid2 = _STD next(_Mid1, _Chunk2); - _Dest = _STD _Merge_move(_First, _Mid1, _Mid2, _Dest, _Pred); + _Dest = _STD _Merge_move_unchecked(_First, _Mid1, _Mid2, _Dest, _Pred); _First = _Mid2; } diff --git a/stl/inc/execution b/stl/inc/execution index fe639431605..a761ef58ed6 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -2945,7 +2945,7 @@ struct _Static_partitioned_stable_sort3 { const size_t _Base = _Visitor._Base; const size_t _Mid = _Base + _Visitor._Shift; const size_t _End = _Mid + _Visitor._Shift; - _STD _Merge_move(_Temp_buf._Get_first(_Base), _Temp_buf._Get_first(_Mid), + _STD _Merge_move_unchecked(_Temp_buf._Get_first(_Base), _Temp_buf._Get_first(_Mid), _Temp_buf._Get_first(_End), _Basis._Get_first(_Base, _Team._Get_chunk_offset(_Base)), _Pred); } @@ -2963,7 +2963,7 @@ struct _Static_partitioned_stable_sort3 { const size_t _Base = _Visitor._Base; const size_t _Mid = _Base + _Visitor._Shift; const size_t _End = _Mid + _Visitor._Shift; - _STD _Merge_move(_Basis._Get_first(_Base, _Team._Get_chunk_offset(_Base)), + _STD _Merge_move_unchecked(_Basis._Get_first(_Base, _Team._Get_chunk_offset(_Base)), _Basis._Get_first(_Mid, _Team._Get_chunk_offset(_Mid)), _Basis._Get_first(_End, _Team._Get_chunk_offset(_End)), _Temp_buf._Get_first(_Base), _Pred); } From 9edaef96489f1ea807b0528686881934c3cb1e96 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 13 Oct 2024 00:36:50 -0700 Subject: [PATCH 09/12] Improve detection of library-defined `array` specializations `_Unchecked_begin`/`_Unchecked_end` are semi-documented, so their presence doesn't necessarily indicate a specialization is library-defined. Use our `_Is_from_primary` tech, and remove the now-unused `_Has_unchecked_begin_end`. --- stl/inc/array | 17 +++++++++++++---- stl/inc/xutility | 7 ------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/stl/inc/array b/stl/inc/array index 209bd9e95bd..b8eef281ba1 100644 --- a/stl/inc/array +++ b/stl/inc/array @@ -422,6 +422,8 @@ public: using reverse_iterator = _STD reverse_iterator; using const_reverse_iterator = _STD reverse_iterator; + using _Library_defined = array; + #if _HAS_TR1_NAMESPACE _DEPRECATE_TR1_NAMESPACE void assign(const _Ty& _Value) { _STD fill_n(_Elems, _Size, _Value); @@ -610,6 +612,8 @@ public: using reverse_iterator = _STD reverse_iterator; using const_reverse_iterator = _STD reverse_iterator; + using _Library_defined = array; + #if _HAS_TR1_NAMESPACE _DEPRECATE_TR1_NAMESPACE void assign(const _Ty&) {} #endif // _HAS_TR1_NAMESPACE @@ -844,10 +848,15 @@ _NODISCARD constexpr array, _Size> to_array(_Ty (&&_Array)[_Siz } #endif // _HAS_CXX20 +template +constexpr bool _Is_library_defined = false; +template +constexpr bool _Is_library_defined<_Ty, enable_if_t>> = true; + _EXPORT_STD template _NODISCARD constexpr _Ty& get(array<_Ty, _Size>& _Arr) noexcept { static_assert(_Idx < _Size, "array index out of bounds"); - if constexpr (_Has_unchecked_begin_end>) { + if constexpr (_Is_library_defined>) { return _Arr._Elems[_Idx]; } else { #if _HAS_CXX17 @@ -861,7 +870,7 @@ _NODISCARD constexpr _Ty& get(array<_Ty, _Size>& _Arr) noexcept { _EXPORT_STD template _NODISCARD constexpr const _Ty& get(const array<_Ty, _Size>& _Arr) noexcept { static_assert(_Idx < _Size, "array index out of bounds"); - if constexpr (_Has_unchecked_begin_end>) { + if constexpr (_Is_library_defined>) { return _Arr._Elems[_Idx]; } else { return _Arr[_Idx]; @@ -871,7 +880,7 @@ _NODISCARD constexpr const _Ty& get(const array<_Ty, _Size>& _Arr) noexcept { _EXPORT_STD template _NODISCARD constexpr _Ty&& get(array<_Ty, _Size>&& _Arr) noexcept { static_assert(_Idx < _Size, "array index out of bounds"); - if constexpr (_Has_unchecked_begin_end>) { + if constexpr (_Is_library_defined>) { return _STD move(_Arr._Elems[_Idx]); } else { #if _HAS_CXX17 @@ -885,7 +894,7 @@ _NODISCARD constexpr _Ty&& get(array<_Ty, _Size>&& _Arr) noexcept { _EXPORT_STD template _NODISCARD constexpr const _Ty&& get(const array<_Ty, _Size>&& _Arr) noexcept { static_assert(_Idx < _Size, "array index out of bounds"); - if constexpr (_Has_unchecked_begin_end>) { + if constexpr (_Is_library_defined>) { return _STD move(_Arr._Elems[_Idx]); } else { return _STD move(_Arr[_Idx]); diff --git a/stl/inc/xutility b/stl/inc/xutility index 6a7028b326e..512a90468d5 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -777,13 +777,6 @@ struct _Unused_parameter { // generic unused parameter struct constexpr _Unused_parameter(_Ty&&) noexcept {} }; -template // checks whether a container/view is a non-customized specialization -constexpr bool _Has_unchecked_begin_end = false; - -template -constexpr bool _Has_unchecked_begin_end<_Ty, - void_t()._Unchecked_begin()), decltype(_STD declval<_Ty&>()._Unchecked_end())>> = true; - template using _Algorithm_int_t = conditional_t, _Ty, ptrdiff_t>; From cd2726d324b39f6ec860a2b81e1ef1705d28d10c Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 13 Oct 2024 00:39:53 -0700 Subject: [PATCH 10/12] Add some internal checks to internal algorithms `_Uninitialized_copy`, `_Uninitialized_copy_n`, and `_Uninitialized_move` in `` are internal-only, and trust that the caller has passed arguments that denote a valid range. I am not so trusting. `_STL_INTERNAL_CHECK((_STD _Adl_verify_range(_First, _Last), true))` feels like a bit of a hack - I could be convinced to simply wrap the `_Adl_verify_range` call in `#ifdef _ENABLE_STL_INTERNAL_CHECK`. --- stl/inc/xmemory | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stl/inc/xmemory b/stl/inc/xmemory index f2acc82c271..d74df6acf37 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1793,6 +1793,8 @@ _CONSTEXPR20 _Alloc_ptr_t<_Alloc> _Uninitialized_copy( // note: only called internally from elsewhere in the STL using _Ptrval = typename _Alloc::value_type*; + _STL_INTERNAL_CHECK((_STD _Adl_verify_range(_First, _Last), true)); + #if _HAS_CXX20 auto _UFirst = _RANGES _Unwrap_iter<_Se>(_STD move(_First)); auto _ULast = _RANGES _Unwrap_sent<_InIt>(_STD move(_Last)); @@ -1838,7 +1840,11 @@ _CONSTEXPR20 _Alloc_ptr_t<_Alloc> _Uninitialized_copy_n( // note: only called internally from elsewhere in the STL using _Ptrval = typename _Alloc::value_type*; +#ifdef _ENABLE_STL_INTERNAL_CHECK + auto _UFirst = _STD _Get_unwrapped_n(_STD move(_First), _Count); +#else // ^^^ Checking / No checking ^^^ auto _UFirst = _STD _Get_unwrapped(_STD move(_First)); +#endif // ^^^ No checking ^^^ constexpr bool _Can_memmove = conjunction_v::_Bitcopy_constructible>, @@ -1899,6 +1905,7 @@ _CONSTEXPR20 _Alloc_ptr_t<_Alloc> _Uninitialized_move( const _InIt _First, const _InIt _Last, _Alloc_ptr_t<_Alloc> _Dest, _Alloc& _Al) { // move [_First, _Last) to raw _Dest, using _Al // note: only called internally from elsewhere in the STL + _STL_INTERNAL_CHECK((_STD _Adl_verify_range(_First, _Last), true)); using _Ptrval = typename _Alloc::value_type*; auto _UFirst = _STD _Get_unwrapped(_First); const auto _ULast = _STD _Get_unwrapped(_Last); From 503f161e6ddaf2dded81b08dfc60a9362ab9bc16 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 13 Oct 2024 10:54:32 -0700 Subject: [PATCH 11/12] Verify that arguments to `subrange`'s iter/sentinel constructors denote a range They must per N4988 [range.subrange.ctor] para 1 and 3. As a rule, we never verify that `[begin(meow), end(meow))` for a range `meow` actually denotes a range, so `subrange`'s constructors are the only chance we have to verify these user-supplied values. --- stl/inc/xutility | 5 ++++- .../P2165R4_tuple_like_operations/test.compile.pass.cpp | 7 +++---- .../test.compile.pass.cpp | 9 +++++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 512a90468d5..eca52803576 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3959,12 +3959,15 @@ namespace ranges { template <_Convertible_to_non_slicing<_It> _It2> constexpr subrange(_It2 _First_, _Se _Last_) requires (!_Store_size<_It, _Se, _Ki>) - : _First(_STD move(_First_)), _Last(_STD move(_Last_)) {} + : _First(_STD move(_First_)), _Last(_STD move(_Last_)) { + _STD _Adl_verify_range(_First, _Last); + } template <_Convertible_to_non_slicing<_It> _It2> constexpr subrange(_It2 _First_, _Se _Last_, const _Size_type _Size_) requires (_Ki == subrange_kind::sized) : _Subrange_base<_It, _Se, _Ki>(_Size_), _First(_STD move(_First_)), _Last(_STD move(_Last_)) { + _STD _Adl_verify_range(_First, _Last); if constexpr (sized_sentinel_for<_Se, _It>) { _STL_ASSERT(_Size_ == static_cast<_Size_type>(_Last - _First), "This constructor's third argument should be equal to the distance " diff --git a/tests/std/tests/P2165R4_tuple_like_operations/test.compile.pass.cpp b/tests/std/tests/P2165R4_tuple_like_operations/test.compile.pass.cpp index bbc422c22b6..e7df3c6834d 100644 --- a/tests/std/tests/P2165R4_tuple_like_operations/test.compile.pass.cpp +++ b/tests/std/tests/P2165R4_tuple_like_operations/test.compile.pass.cpp @@ -127,7 +127,7 @@ namespace test_make_from_tuple { assert(make_from_tuple(tuple{&a, &b}) == expected_val); assert(make_from_tuple(pair{&a, &b}) == expected_val); assert(make_from_tuple(array{&a, &b}) == expected_val); - assert(make_from_tuple(subrange{&a, &b}) == expected_val); + assert(make_from_tuple(subrange{&a, &a + 1}) == (S{&a, &a + 1})); } { // Test make_from_tuple with big tuple-like types @@ -180,9 +180,8 @@ namespace test_tuple_cat { { // Test tuple_cat with pair-like types int a = 0; - int b = 1; - assert( - (tuple_cat(tuple{1, 2}, array{3, 4}, pair{5, 6}, subrange{&a, &b}) == tuple{1, 2, 3, 4, 5, 6, &a, &b})); + assert((tuple_cat(tuple{1, 2}, array{3, 4}, pair{5, 6}, subrange{&a, &a + 1}) + == tuple{1, 2, 3, 4, 5, 6, &a, &a + 1})); } // Test tuple_cat with big tuple-like types diff --git a/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp b/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp index 52d7a2c3377..8ef96e004f0 100644 --- a/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp +++ b/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp @@ -76,8 +76,13 @@ constexpr bool test() { int a = 0; int b = 1; int c[2] = {2, 3}; - static_assert(tuple{&a, &b} == subrange{&a, &b}); - static_assert(tuple{&b, &a} != subrange{&a, &b}); +#ifdef __EDG__ // VSO-2283373 + assert((tuple{&c[0], &c[1]} == subrange{&c[0], &c[1]})); + assert((tuple{&c[1], &c[0]} != subrange{&c[0], &c[1]})); +#else // ^^^ workaround / no workaround vvv + static_assert(tuple{&c[0], &c[1]} == subrange{&c[0], &c[1]}); + static_assert(tuple{&c[1], &c[0]} != subrange{&c[0], &c[1]}); +#endif // ^^^ no workaround ^^^ static_assert(is_eq(tuple{&a, &b} <=> pair{&a, &b})); static_assert(is_lt(tuple{&c[0], &c[0]} <=> pair{&c[0], &c[1]})); static_assert(is_gt(tuple{&c[1], &c[0]} <=> pair{&c[0], &c[1]})); From 3a576af6679fe2d1026cfc32a778354b0f2166d2 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 14 Oct 2024 09:32:10 -0700 Subject: [PATCH 12/12] STL's review comments --- stl/inc/xmemory | 10 +++++++--- stl/inc/xutility | 3 ++- .../test.compile.pass.cpp | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/stl/inc/xmemory b/stl/inc/xmemory index d74df6acf37..9a08502964e 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1793,7 +1793,9 @@ _CONSTEXPR20 _Alloc_ptr_t<_Alloc> _Uninitialized_copy( // note: only called internally from elsewhere in the STL using _Ptrval = typename _Alloc::value_type*; - _STL_INTERNAL_CHECK((_STD _Adl_verify_range(_First, _Last), true)); +#ifdef _ENABLE_STL_INTERNAL_CHECK + _STD _Adl_verify_range(_First, _Last); +#endif // ^^^ defined(_ENABLE_STL_INTERNAL_CHECK) ^^^ #if _HAS_CXX20 auto _UFirst = _RANGES _Unwrap_iter<_Se>(_STD move(_First)); @@ -1842,7 +1844,7 @@ _CONSTEXPR20 _Alloc_ptr_t<_Alloc> _Uninitialized_copy_n( #ifdef _ENABLE_STL_INTERNAL_CHECK auto _UFirst = _STD _Get_unwrapped_n(_STD move(_First), _Count); -#else // ^^^ Checking / No checking ^^^ +#else // ^^^ Checking / No checking vvv auto _UFirst = _STD _Get_unwrapped(_STD move(_First)); #endif // ^^^ No checking ^^^ @@ -1905,7 +1907,9 @@ _CONSTEXPR20 _Alloc_ptr_t<_Alloc> _Uninitialized_move( const _InIt _First, const _InIt _Last, _Alloc_ptr_t<_Alloc> _Dest, _Alloc& _Al) { // move [_First, _Last) to raw _Dest, using _Al // note: only called internally from elsewhere in the STL - _STL_INTERNAL_CHECK((_STD _Adl_verify_range(_First, _Last), true)); +#ifdef _ENABLE_STL_INTERNAL_CHECK + _STD _Adl_verify_range(_First, _Last); +#endif // ^^^ defined(_ENABLE_STL_INTERNAL_CHECK) ^^^ using _Ptrval = typename _Alloc::value_type*; auto _UFirst = _STD _Get_unwrapped(_First); const auto _ULast = _STD _Get_unwrapped(_Last); diff --git a/stl/inc/xutility b/stl/inc/xutility index eca52803576..d963308aa53 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4520,7 +4520,8 @@ template constexpr bool disable_sized_sentinel_for, move_iterator<_Iter2>> = true; _EXPORT_STD struct unreachable_sentinel_t { - _NODISCARD constexpr bool operator==(const weakly_incrementable auto&) const noexcept { + template + _NODISCARD constexpr bool operator==(const _Ty&) const noexcept { return false; } }; diff --git a/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp b/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp index 8ef96e004f0..cd8e54c93f5 100644 --- a/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp +++ b/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp @@ -76,7 +76,7 @@ constexpr bool test() { int a = 0; int b = 1; int c[2] = {2, 3}; -#ifdef __EDG__ // VSO-2283373 +#ifdef __EDG__ // TRANSITION, VSO-2283373 assert((tuple{&c[0], &c[1]} == subrange{&c[0], &c[1]})); assert((tuple{&c[1], &c[0]} != subrange{&c[0], &c[1]})); #else // ^^^ workaround / no workaround vvv