diff --git a/stl/inc/flat_map b/stl/inc/flat_map index 2e294d5c822..b3e4e72c543 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -53,23 +53,27 @@ template struct _Pairing_iterator_provider { class _Iterator { public: + using iterator_category = input_iterator_tag; + using iterator_concept = random_access_iterator_tag; + using difference_type = ptrdiff_t; + using value_type = pair, iter_value_t<_MappedIter>>; + using reference = pair, iter_reference_t<_MappedIter>>; + + _Iterator() = default; + + private: template friend class _Flat_map_base; + template + friend struct _Pairing_iterator_provider; - _Iterator() = default; _Iterator(_KeyIter _Key_iter, _MappedIter _Mapped_iter) noexcept(is_nothrow_move_constructible_v<_KeyIter> && is_nothrow_move_constructible_v<_MappedIter>) : _Key_it(_STD move(_Key_iter)), _Mapped_it(_STD move(_Mapped_iter)) {} - using iterator_category = input_iterator_tag; - using iterator_concept = random_access_iterator_tag; - using difference_type = ptrdiff_t; - using value_type = pair, iter_value_t<_MappedIter>>; - using reference = pair, iter_reference_t<_MappedIter>>; - - private: - using _Const_iterator = _Pairing_iterator_provider<_KeyIter, _MappedConvIter, _MappedConvIter>::_Iterator; + using _Const_iterator = _Pairing_iterator_provider<_KeyIter, _MappedConvIter, _MappedConvIter>::_Iterator; + using _Unwrapped_iterator = _Unwrapped_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter>; class _Arrow_proxy { public: @@ -112,10 +116,12 @@ struct _Pairing_iterator_provider { } _NODISCARD bool operator==(const _Iterator& _Right) const { + _Compat(_Right); return _Key_it == _Right._Key_it; } _NODISCARD auto operator<=>(const _Iterator& _Right) const { + _Compat(_Right); return _Synth_three_way{}(_Key_it, _Right._Key_it); } @@ -160,6 +166,7 @@ struct _Pairing_iterator_provider { } _NODISCARD difference_type operator-(const _Iterator& _Right) const { + _Compat(_Right); return _Key_it - _Right._Key_it; } @@ -173,16 +180,16 @@ struct _Pairing_iterator_provider { return _Const_iterator{_Key_it, _Mapped_it}; } - _NODISCARD const _KeyIter& _Key_iterator() const noexcept { - return _Key_it; - } - - _NODISCARD const _MappedIter& _Mapped_iterator() const noexcept { - return _Mapped_it; + void _Compat([[maybe_unused]] const _Iterator& _Right) const noexcept { // test for compatible iterator pair +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY( + _Key_it - _Right._Key_it == _Mapped_it - _Right._Mapped_it, "iterators from inconsistent ranges"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 } using _Prevent_inheriting_unwrap = _Iterator; +#if _ITERATOR_DEBUG_LEVEL != 0 friend void _Verify_range(const _Iterator& _First, const _Iterator& _Last) noexcept { if constexpr (_Range_verifiable_v<_KeyIter>) { _Verify_range(_First._Key_it, _Last._Key_it); // intentional ADL @@ -192,9 +199,9 @@ struct _Pairing_iterator_provider { _Verify_range(_First._Mapped_it, _Last._Mapped_it); // intentional ADL } - _STL_VERIFY(_Last._Key_it - _First._Key_it == _Last._Mapped_it - _First._Mapped_it, - "iterators from inconsistent ranges"); + _First._Compat(_Last); } +#endif // _ITERATOR_DEBUG_LEVEL != 0 void _Verify_offset(const difference_type _Off) const noexcept { if constexpr (_Offset_verifiable_v<_KeyIter>) { @@ -213,18 +220,25 @@ struct _Pairing_iterator_provider { _NODISCARD auto _Unwrapped() const requires _Can_unwrap_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter> { - using _Unwrapped_iterator = _Unwrapped_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter>; return _Unwrapped_iterator{_STD _Get_unwrapped(_Key_it), _STD _Get_unwrapped(_Mapped_it)}; } - void _Seek_to(const _Unwrapped_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter>& _Dst) + static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v<_KeyIter> + && _Do_unwrap_when_unverified_v<_MappedIter> + && _Do_unwrap_when_unverified_v<_MappedConvIter>; + + void _Seek_to(const _Unwrapped_iterator& _Dst) requires _Can_unwrap_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter> { - _STD _Seek_wrapped(_Key_it, _Dst._Key_iterator()); - _STD _Seek_wrapped(_Mapped_it, _Dst._Mapped_iterator()); + _STD _Seek_wrapped(_Key_it, _Dst._Key_it); + _STD _Seek_wrapped(_Mapped_it, _Dst._Mapped_it); } +#ifdef _ENABLE_STL_INTERNAL_CHECK + public: +#else private: +#endif _KeyIter _Key_it; _MappedIter _Mapped_it; }; diff --git a/tests/std/tests/Dev10_709168_marking_iterators_as_checked/test.compile.pass.cpp b/tests/std/tests/Dev10_709168_marking_iterators_as_checked/test.compile.pass.cpp index 4a4b9435c9a..e137800b970 100644 --- a/tests/std/tests/Dev10_709168_marking_iterators_as_checked/test.compile.pass.cpp +++ b/tests/std/tests/Dev10_709168_marking_iterators_as_checked/test.compile.pass.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include #include @@ -109,6 +111,14 @@ STATIC_ASSERT(stl_checked == _Range_verifiable_v::iterator>); STATIC_ASSERT(stl_checked == _Range_verifiable_v::reverse_iterator>); #endif // _HAS_CXX20 +#if _HAS_CXX23 +// Checks for flat_set should trivially pass since it directly uses the underlying container's iterator +STATIC_ASSERT(stl_checked == _Range_verifiable_v::iterator>); +STATIC_ASSERT(stl_checked == _Range_verifiable_v::reverse_iterator>); +STATIC_ASSERT(stl_checked == _Range_verifiable_v::iterator>); +STATIC_ASSERT(stl_checked == _Range_verifiable_v::reverse_iterator>); +#endif // _HAS_CXX23 + STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom>); STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom>); STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom>); @@ -181,6 +191,13 @@ STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom::iterator>>); STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom::reverse_iterator>>); #endif // _HAS_CXX20 +#if _HAS_CXX23 +STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom::iterator>>); +STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom::reverse_iterator>>); +STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom::iterator>>); +STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom::reverse_iterator>>); +#endif // _HAS_CXX23 + template constexpr bool test_unwrappable() { STATIC_ASSERT(_Unwrappable_v == Expected); @@ -394,6 +411,13 @@ STATIC_ASSERT(test_unwrappable_for_unverified::iterator, !stl_checked> STATIC_ASSERT(test_unwrappable_for_unverified::reverse_iterator, !stl_checked>()); #endif // _HAS_CXX20 +#if _HAS_CXX23 +STATIC_ASSERT(test_unwrappable_for_unverified::iterator, !stl_checked>()); +STATIC_ASSERT(test_unwrappable_for_unverified::reverse_iterator, !stl_checked>()); +STATIC_ASSERT(test_unwrappable_for_unverified::iterator, !stl_checked>()); +STATIC_ASSERT(test_unwrappable_for_unverified::reverse_iterator, !stl_checked>()); +#endif // _HAS_CXX23 + STATIC_ASSERT(test_unwrappable()); STATIC_ASSERT(test_unwrappable()); STATIC_ASSERT(test_unwrappable()); @@ -467,6 +491,13 @@ STATIC_ASSERT(test_unwrappable::iterator, true>()); STATIC_ASSERT(test_unwrappable::reverse_iterator, true>()); #endif // _HAS_CXX20 +#if _HAS_CXX23 +STATIC_ASSERT(test_unwrappable::iterator, true>()); +STATIC_ASSERT(test_unwrappable::reverse_iterator, true>()); +STATIC_ASSERT(test_unwrappable::iterator, true>()); +STATIC_ASSERT(test_unwrappable::reverse_iterator, true>()); +#endif // _HAS_CXX23 + STATIC_ASSERT(test_unwrappable_for_offset()); STATIC_ASSERT(test_unwrappable_for_offset()); STATIC_ASSERT(test_unwrappable_for_offset()); @@ -540,6 +571,13 @@ STATIC_ASSERT(test_unwrappable_for_offset::iterator, true>()); STATIC_ASSERT(test_unwrappable_for_offset::reverse_iterator, true>()); #endif // _HAS_CXX20 +#if _HAS_CXX23 +STATIC_ASSERT(test_unwrappable_for_offset::iterator, true>()); +STATIC_ASSERT(test_unwrappable_for_offset::reverse_iterator, true>()); +STATIC_ASSERT(test_unwrappable_for_offset::iterator, true>()); +STATIC_ASSERT(test_unwrappable_for_offset::reverse_iterator, true>()); +#endif // _HAS_CXX23 + STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom, false>()); STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom, false>()); STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom, false>()); @@ -612,6 +650,13 @@ STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom::iterator> STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom::reverse_iterator>, false>()); #endif // _HAS_CXX20 +#if _HAS_CXX23 +STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom::iterator>, false>()); +STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom::reverse_iterator>, false>()); +STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom::iterator>, false>()); +STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom::reverse_iterator>, false>()); +#endif // _HAS_CXX23 + STATIC_ASSERT(test_unwrappable<::DerivedFrom, false>()); STATIC_ASSERT(test_unwrappable<::DerivedFrom, false>()); STATIC_ASSERT(test_unwrappable<::DerivedFrom, false>()); @@ -684,6 +729,13 @@ STATIC_ASSERT(test_unwrappable<::DerivedFrom::iterator>, false>()); STATIC_ASSERT(test_unwrappable<::DerivedFrom::reverse_iterator>, false>()); #endif // _HAS_CXX20 +#if _HAS_CXX23 +STATIC_ASSERT(test_unwrappable<::DerivedFrom::iterator>, false>()); +STATIC_ASSERT(test_unwrappable<::DerivedFrom::reverse_iterator>, false>()); +STATIC_ASSERT(test_unwrappable<::DerivedFrom::iterator>, false>()); +STATIC_ASSERT(test_unwrappable<::DerivedFrom::reverse_iterator>, false>()); +#endif // _HAS_CXX23 + STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom, false>()); STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom, false>()); STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom, false>()); @@ -755,3 +807,10 @@ STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom::iterator>, false>()); STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom::reverse_iterator>, false>()); #endif // _HAS_CXX20 + +#if _HAS_CXX23 +STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom::iterator>, false>()); +STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom::reverse_iterator>, false>()); +STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom::iterator>, false>()); +STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom::reverse_iterator>, false>()); +#endif // _HAS_CXX23 diff --git a/tests/std/tests/P0429R9_flat_map/test.cpp b/tests/std/tests/P0429R9_flat_map/test.cpp index e2a4a9b8733..9b2625a1b0d 100644 --- a/tests/std/tests/P0429R9_flat_map/test.cpp +++ b/tests/std/tests/P0429R9_flat_map/test.cpp @@ -1502,6 +1502,31 @@ void test_death_different_size_replace() { cont.replace({42, 137, 137, 3337, 0}, {'a'}); } +// Reach into the pairing iterator and modify one of the underlying iterators to break invariants +template +void vandalize_pairing_iterator(Iter& pairing_iterator) { + ++pairing_iterator._Key_it; +} + +enum class iter_test { op_equals, op_spaceship, op_minus }; + +template +void test_death_iterator_at_different_offset() { + using C = conditional_t, flat_multimap>; + C cont{{42, 'a'}, {137, 'g'}, {137, 'r'}, {3337, 'f'}, {7755, 'z'}}; + auto broken_iter = cont.begin(); + vandalize_pairing_iterator(broken_iter); + if constexpr (mode == iter_test::op_equals) { + (void) (cont.end() == broken_iter); + } else if constexpr (mode == iter_test::op_spaceship) { + (void) (cont.end() <=> broken_iter); + } else if constexpr (mode == iter_test::op_minus) { + (void) (cont.end() - broken_iter); + } else { + static_assert(false, "unknown iter_test mode"); + } +} + int main(int argc, char* argv[]) { std_testing::death_test_executive exec([] { run_normal_tests(); }); @@ -1543,5 +1568,17 @@ int main(int argc, char* argv[]) { }); #endif // defined(_DEBUG) +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + // Tests specific to flat_map - incompatible key and mapped component of pairing iterator + test_death_iterator_at_different_offset, + test_death_iterator_at_different_offset, + test_death_iterator_at_different_offset, + test_death_iterator_at_different_offset, + test_death_iterator_at_different_offset, + test_death_iterator_at_different_offset, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return exec.run(argc, argv); }