From f7f07ade1a365a461a30d81772b68f59a9f407a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 21 Jan 2026 17:16:48 +0100 Subject: [PATCH 01/13] Make flat_map::iterator unwrapping and _Verify_range behave consistently with other iterator types. --- stl/inc/flat_map | 6 ++ .../test.compile.pass.cpp | 59 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/stl/inc/flat_map b/stl/inc/flat_map index 2e294d5c822..4cbb9ee2103 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -183,6 +183,7 @@ struct _Pairing_iterator_provider { 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 @@ -195,6 +196,7 @@ struct _Pairing_iterator_provider { _STL_VERIFY(_Last._Key_it - _First._Key_it == _Last._Mapped_it - _First._Mapped_it, "iterators from inconsistent ranges"); } +#endif // _ITERATOR_DEBUG_LEVEL != 0 void _Verify_offset(const difference_type _Off) const noexcept { if constexpr (_Offset_verifiable_v<_KeyIter>) { @@ -217,6 +219,10 @@ struct _Pairing_iterator_provider { return _Unwrapped_iterator{_STD _Get_unwrapped(_Key_it), _STD _Get_unwrapped(_Mapped_it)}; } + 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_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter>& _Dst) requires _Can_unwrap_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter> { 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 From 2a04b21a4aec0e2ccb8a54ddc56536e07eaa409f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 21 Jan 2026 18:40:02 +0100 Subject: [PATCH 02/13] Perform stricter checks in pairing iterator member functions when _ITERATOR_DEBUG_LEVEL != 0 --- stl/inc/flat_map | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/stl/inc/flat_map b/stl/inc/flat_map index 4cbb9ee2103..7bfa609414a 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -112,10 +112,16 @@ struct _Pairing_iterator_provider { } _NODISCARD bool operator==(const _Iterator& _Right) const { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Compat(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 return _Key_it == _Right._Key_it; } _NODISCARD auto operator<=>(const _Iterator& _Right) const { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Compat(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 return _Synth_three_way{}(_Key_it, _Right._Key_it); } @@ -160,6 +166,9 @@ struct _Pairing_iterator_provider { } _NODISCARD difference_type operator-(const _Iterator& _Right) const { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Compat(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 return _Key_it - _Right._Key_it; } @@ -181,6 +190,13 @@ struct _Pairing_iterator_provider { 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 @@ -193,8 +209,7 @@ 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 From 035f0ad4bc40184fe6afdb459cc16d13c8d16431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 21 Jan 2026 22:18:08 +0100 Subject: [PATCH 03/13] Make pairing iterator constructor private (only constructible by _Flat_map_base and other friends). --- stl/inc/flat_map | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/stl/inc/flat_map b/stl/inc/flat_map index 7bfa609414a..ad8bfccc731 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -58,10 +58,13 @@ struct _Pairing_iterator_provider { friend class _Flat_map_base; _Iterator() = default; + + private: _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)) {} + public: using iterator_category = input_iterator_tag; using iterator_concept = random_access_iterator_tag; using difference_type = ptrdiff_t; @@ -69,7 +72,11 @@ struct _Pairing_iterator_provider { 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>; + template + friend struct _Pairing_iterator_provider; + class _Arrow_proxy { public: @@ -230,7 +237,6 @@ 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)}; } From c4d98239f539827727e5d66d261b6c9a06c9e709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 21 Jan 2026 22:33:17 +0100 Subject: [PATCH 04/13] Add death tests for detection of pairing iterators with broken invariants. --- tests/std/tests/P0429R9_flat_map/test.cpp | 54 +++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/std/tests/P0429R9_flat_map/test.cpp b/tests/std/tests/P0429R9_flat_map/test.cpp index e2a4a9b8733..9124f34534e 100644 --- a/tests/std/tests/P0429R9_flat_map/test.cpp +++ b/tests/std/tests/P0429R9_flat_map/test.cpp @@ -1502,6 +1502,47 @@ 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) { + using key_iterator_type = Cont::key_container_type::const_iterator; + // FIXME static_assert fails for _Vector_const_iterator. Do we have a more elegant way + // to break invariants? Droping these tests is the last option.. + // static_assert(is_trivially_copyable_v); + key_iterator_type key_iterator_copy; + memcpy(static_cast(&key_iterator_copy), static_cast(&pairing_iterator), sizeof(key_iterator_type)); + ++key_iterator_copy; + memcpy(static_cast(&pairing_iterator), static_cast(&key_iterator_copy), sizeof(key_iterator_type)); +} + +template +void test_death_iterator_at_different_offset_operator_equals() { + 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); + (void) (cont.end() == broken_iter); +} + +template +void test_death_iterator_at_different_offset_three_way() { + 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); + (void) (cont.end() <=> broken_iter); +} + +template +void test_death_iterator_at_different_offset_operator_minus() { + 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); + (void) (cont.end() - broken_iter); +} + + int main(int argc, char* argv[]) { std_testing::death_test_executive exec([] { run_normal_tests(); }); @@ -1540,8 +1581,21 @@ int main(int argc, char* argv[]) { test_death_different_size_ctor_sorted, test_death_different_size_replace, test_death_different_size_replace, + }); #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_operator_equals, + test_death_iterator_at_different_offset_operator_equals, + test_death_iterator_at_different_offset_three_way, + test_death_iterator_at_different_offset_three_way, + test_death_iterator_at_different_offset_operator_minus, + test_death_iterator_at_different_offset_operator_minus, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return exec.run(argc, argv); } From 2fe6b46b4f80d48033d7a71ae240a8e7028d4473 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 21 Jan 2026 15:38:47 -0800 Subject: [PATCH 05/13] Group friendships with ctor. --- stl/inc/flat_map | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/stl/inc/flat_map b/stl/inc/flat_map index ad8bfccc731..be7d0f3d0ef 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -53,13 +53,15 @@ template struct _Pairing_iterator_provider { class _Iterator { public: + _Iterator() = default; + + private: template friend class _Flat_map_base; + template + friend struct _Pairing_iterator_provider; - _Iterator() = default; - - private: _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)) {} @@ -74,9 +76,6 @@ struct _Pairing_iterator_provider { private: using _Const_iterator = _Pairing_iterator_provider<_KeyIter, _MappedConvIter, _MappedConvIter>::_Iterator; using _Unwrapped_iterator = _Unwrapped_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter>; - template - friend struct _Pairing_iterator_provider; - class _Arrow_proxy { public: From 351ab46c6b35e953f2ec3131067b91178fced49f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 21 Jan 2026 15:45:49 -0800 Subject: [PATCH 06/13] Consolidate access control. --- stl/inc/flat_map | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/stl/inc/flat_map b/stl/inc/flat_map index be7d0f3d0ef..c40505b7aca 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -53,6 +53,12 @@ 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: @@ -66,14 +72,6 @@ struct _Pairing_iterator_provider { 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)) {} - 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>>; - - private: using _Const_iterator = _Pairing_iterator_provider<_KeyIter, _MappedConvIter, _MappedConvIter>::_Iterator; using _Unwrapped_iterator = _Unwrapped_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter>; From c6ca84a4c1b03d253f418b9c38536b7e53f423fe Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 21 Jan 2026 15:55:02 -0800 Subject: [PATCH 07/13] Use `_Unwrapped_iterator` in `_Seek_to()`. --- stl/inc/flat_map | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/flat_map b/stl/inc/flat_map index c40505b7aca..c4e05472112 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -241,7 +241,7 @@ struct _Pairing_iterator_provider { && _Do_unwrap_when_unverified_v<_MappedIter> && _Do_unwrap_when_unverified_v<_MappedConvIter>; - void _Seek_to(const _Unwrapped_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter>& _Dst) + void _Seek_to(const _Unwrapped_iterator& _Dst) requires _Can_unwrap_pairing_iterator<_KeyIter, _MappedIter, _MappedConvIter> { _STD _Seek_wrapped(_Key_it, _Dst._Key_iterator()); From a6c1012b9fcb1e4b1a0c0cab4835654f910c7199 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 21 Jan 2026 16:01:11 -0800 Subject: [PATCH 08/13] Follow `vector`'s example, don't guard `_Compat()` calls. --- stl/inc/flat_map | 6 ------ 1 file changed, 6 deletions(-) diff --git a/stl/inc/flat_map b/stl/inc/flat_map index c4e05472112..6a5add1906d 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -116,16 +116,12 @@ struct _Pairing_iterator_provider { } _NODISCARD bool operator==(const _Iterator& _Right) const { -#if _ITERATOR_DEBUG_LEVEL != 0 _Compat(_Right); -#endif // _ITERATOR_DEBUG_LEVEL != 0 return _Key_it == _Right._Key_it; } _NODISCARD auto operator<=>(const _Iterator& _Right) const { -#if _ITERATOR_DEBUG_LEVEL != 0 _Compat(_Right); -#endif // _ITERATOR_DEBUG_LEVEL != 0 return _Synth_three_way{}(_Key_it, _Right._Key_it); } @@ -170,9 +166,7 @@ struct _Pairing_iterator_provider { } _NODISCARD difference_type operator-(const _Iterator& _Right) const { -#if _ITERATOR_DEBUG_LEVEL != 0 _Compat(_Right); -#endif // _ITERATOR_DEBUG_LEVEL != 0 return _Key_it - _Right._Key_it; } From ea6274bfdfddb055f1ca7fab0cef4b6e90d1b9b2 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 21 Jan 2026 16:04:38 -0800 Subject: [PATCH 09/13] Friendship makes public `_Key_iterator()`, `_Mapped_iterator()` unnecessary. --- stl/inc/flat_map | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/stl/inc/flat_map b/stl/inc/flat_map index 6a5add1906d..f19743f7ca7 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -180,14 +180,6 @@ 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( @@ -238,8 +230,8 @@ struct _Pairing_iterator_provider { 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); } private: From 442a741711294996bfa14fc9b9661d2ab86ab072 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 21 Jan 2026 16:10:55 -0800 Subject: [PATCH 10/13] Undo stray newline. --- tests/std/tests/P0429R9_flat_map/test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/std/tests/P0429R9_flat_map/test.cpp b/tests/std/tests/P0429R9_flat_map/test.cpp index 9124f34534e..1630daa0ce0 100644 --- a/tests/std/tests/P0429R9_flat_map/test.cpp +++ b/tests/std/tests/P0429R9_flat_map/test.cpp @@ -1581,7 +1581,6 @@ int main(int argc, char* argv[]) { test_death_different_size_ctor_sorted, test_death_different_size_replace, test_death_different_size_replace, - }); #endif // defined(_DEBUG) From efc86c5ac3a8162b60f7087b8970329875ee0c72 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 21 Jan 2026 16:12:52 -0800 Subject: [PATCH 11/13] three_way => operator_spaceship --- tests/std/tests/P0429R9_flat_map/test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P0429R9_flat_map/test.cpp b/tests/std/tests/P0429R9_flat_map/test.cpp index 1630daa0ce0..84aa5b9752a 100644 --- a/tests/std/tests/P0429R9_flat_map/test.cpp +++ b/tests/std/tests/P0429R9_flat_map/test.cpp @@ -1525,7 +1525,7 @@ void test_death_iterator_at_different_offset_operator_equals() { } template -void test_death_iterator_at_different_offset_three_way() { +void test_death_iterator_at_different_offset_operator_spaceship() { using C = conditional_t, flat_multimap>; C cont{{42, 'a'}, {137, 'g'}, {137, 'r'}, {3337, 'f'}, {7755, 'z'}}; auto broken_iter = cont.begin(); @@ -1589,8 +1589,8 @@ int main(int argc, char* argv[]) { // Tests specific to flat_map - incompatible key and mapped component of pairing iterator test_death_iterator_at_different_offset_operator_equals, test_death_iterator_at_different_offset_operator_equals, - test_death_iterator_at_different_offset_three_way, - test_death_iterator_at_different_offset_three_way, + test_death_iterator_at_different_offset_operator_spaceship, + test_death_iterator_at_different_offset_operator_spaceship, test_death_iterator_at_different_offset_operator_minus, test_death_iterator_at_different_offset_operator_minus, }); From 1652e713d719b6f057c695c1d9f57cfd88d18b2e Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 21 Jan 2026 21:58:43 -0800 Subject: [PATCH 12/13] Unify with `enum class iter_test`. --- tests/std/tests/P0429R9_flat_map/test.cpp | 45 +++++++++-------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/tests/std/tests/P0429R9_flat_map/test.cpp b/tests/std/tests/P0429R9_flat_map/test.cpp index 84aa5b9752a..852b76c9e7b 100644 --- a/tests/std/tests/P0429R9_flat_map/test.cpp +++ b/tests/std/tests/P0429R9_flat_map/test.cpp @@ -1515,34 +1515,25 @@ void vandalize_pairing_iterator(Iter& pairing_iterator) { memcpy(static_cast(&pairing_iterator), static_cast(&key_iterator_copy), sizeof(key_iterator_type)); } -template -void test_death_iterator_at_different_offset_operator_equals() { - 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); - (void) (cont.end() == broken_iter); -} +enum class iter_test { op_equals, op_spaceship, op_minus }; -template -void test_death_iterator_at_different_offset_operator_spaceship() { +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); - (void) (cont.end() <=> broken_iter); -} - -template -void test_death_iterator_at_different_offset_operator_minus() { - 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); - (void) (cont.end() - 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(); }); @@ -1587,12 +1578,12 @@ int main(int argc, char* argv[]) { #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_operator_equals, - test_death_iterator_at_different_offset_operator_equals, - test_death_iterator_at_different_offset_operator_spaceship, - test_death_iterator_at_different_offset_operator_spaceship, - test_death_iterator_at_different_offset_operator_minus, - test_death_iterator_at_different_offset_operator_minus, + 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 From ad9bcc2c2cad87301cfe96c31981504accbfeaef Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 21 Jan 2026 22:06:10 -0800 Subject: [PATCH 13/13] Smashy smashy! --- stl/inc/flat_map | 4 ++++ tests/std/tests/P0429R9_flat_map/test.cpp | 13 +++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/stl/inc/flat_map b/stl/inc/flat_map index f19743f7ca7..b3e4e72c543 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -234,7 +234,11 @@ struct _Pairing_iterator_provider { _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/P0429R9_flat_map/test.cpp b/tests/std/tests/P0429R9_flat_map/test.cpp index 852b76c9e7b..9b2625a1b0d 100644 --- a/tests/std/tests/P0429R9_flat_map/test.cpp +++ b/tests/std/tests/P0429R9_flat_map/test.cpp @@ -1503,16 +1503,9 @@ void test_death_different_size_replace() { } // Reach into the pairing iterator and modify one of the underlying iterators to break invariants -template +template void vandalize_pairing_iterator(Iter& pairing_iterator) { - using key_iterator_type = Cont::key_container_type::const_iterator; - // FIXME static_assert fails for _Vector_const_iterator. Do we have a more elegant way - // to break invariants? Droping these tests is the last option.. - // static_assert(is_trivially_copyable_v); - key_iterator_type key_iterator_copy; - memcpy(static_cast(&key_iterator_copy), static_cast(&pairing_iterator), sizeof(key_iterator_type)); - ++key_iterator_copy; - memcpy(static_cast(&pairing_iterator), static_cast(&key_iterator_copy), sizeof(key_iterator_type)); + ++pairing_iterator._Key_it; } enum class iter_test { op_equals, op_spaceship, op_minus }; @@ -1522,7 +1515,7 @@ 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); + 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) {