Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 35 additions & 21 deletions stl/inc/flat_map
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,27 @@ template <class _KeyIter, class _MappedIter, class _MappedConvIter>
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<_KeyIter>, iter_value_t<_MappedIter>>;
using reference = pair<iter_reference_t<_KeyIter>, iter_reference_t<_MappedIter>>;

_Iterator() = default;

private:
template <bool _IsUnique, class _Key, class _Mapped, class _Compare, class _KeyContainer,
class _MappedContainer>
friend class _Flat_map_base;
template <class _Key, class _Mapped, class _MappedConv>
friend struct _Pairing_iterator_provider;
Comment thread
StephanTLavavej marked this conversation as resolved.

_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<_KeyIter>, iter_value_t<_MappedIter>>;
using reference = pair<iter_reference_t<_KeyIter>, 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:
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -160,6 +166,7 @@ struct _Pairing_iterator_provider {
}

_NODISCARD difference_type operator-(const _Iterator& _Right) const {
_Compat(_Right);
return _Key_it - _Right._Key_it;
}

Expand All @@ -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
Expand All @@ -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>) {
Expand All @@ -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;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <cstddef>
#include <deque>
#include <filesystem>
#include <flat_map>
#include <flat_set>
#include <forward_list>
#include <iterator>
#include <list>
Expand Down Expand Up @@ -109,6 +111,14 @@ STATIC_ASSERT(stl_checked == _Range_verifiable_v<span<int>::iterator>);
STATIC_ASSERT(stl_checked == _Range_verifiable_v<span<int>::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<flat_set<int>::iterator>);
STATIC_ASSERT(stl_checked == _Range_verifiable_v<flat_set<int>::reverse_iterator>);
STATIC_ASSERT(stl_checked == _Range_verifiable_v<flat_map<int, int>::iterator>);
STATIC_ASSERT(stl_checked == _Range_verifiable_v<flat_map<int, int>::reverse_iterator>);
#endif // _HAS_CXX23

STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom<string::iterator>>);
STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom<string::const_iterator>>);
STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom<string::reverse_iterator>>);
Expand Down Expand Up @@ -181,6 +191,13 @@ STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom<span<int>::iterator>>);
STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom<span<int>::reverse_iterator>>);
#endif // _HAS_CXX20

#if _HAS_CXX23
STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom<flat_set<int>::iterator>>);
STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom<flat_set<int>::reverse_iterator>>);
STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom<flat_map<int, int>::iterator>>);
STATIC_ASSERT(!_Range_verifiable_v<::DerivedFrom<flat_map<int, int>::reverse_iterator>>);
#endif // _HAS_CXX23

template <class I, bool Expected>
constexpr bool test_unwrappable() {
STATIC_ASSERT(_Unwrappable_v<I&> == Expected);
Expand Down Expand Up @@ -394,6 +411,13 @@ STATIC_ASSERT(test_unwrappable_for_unverified<span<int>::iterator, !stl_checked>
STATIC_ASSERT(test_unwrappable_for_unverified<span<int>::reverse_iterator, !stl_checked>());
#endif // _HAS_CXX20

#if _HAS_CXX23
STATIC_ASSERT(test_unwrappable_for_unverified<flat_set<int>::iterator, !stl_checked>());
STATIC_ASSERT(test_unwrappable_for_unverified<flat_set<int>::reverse_iterator, !stl_checked>());
STATIC_ASSERT(test_unwrappable_for_unverified<flat_map<int, int>::iterator, !stl_checked>());
STATIC_ASSERT(test_unwrappable_for_unverified<flat_map<int, int>::reverse_iterator, !stl_checked>());
#endif // _HAS_CXX23

STATIC_ASSERT(test_unwrappable<string::iterator, true>());
STATIC_ASSERT(test_unwrappable<string::const_iterator, true>());
STATIC_ASSERT(test_unwrappable<string::reverse_iterator, true>());
Expand Down Expand Up @@ -467,6 +491,13 @@ STATIC_ASSERT(test_unwrappable<span<int>::iterator, true>());
STATIC_ASSERT(test_unwrappable<span<int>::reverse_iterator, true>());
#endif // _HAS_CXX20

#if _HAS_CXX23
STATIC_ASSERT(test_unwrappable<flat_set<int>::iterator, true>());
STATIC_ASSERT(test_unwrappable<flat_set<int>::reverse_iterator, true>());
STATIC_ASSERT(test_unwrappable<flat_map<int, int>::iterator, true>());
STATIC_ASSERT(test_unwrappable<flat_map<int, int>::reverse_iterator, true>());
#endif // _HAS_CXX23

STATIC_ASSERT(test_unwrappable_for_offset<string::iterator, true>());
STATIC_ASSERT(test_unwrappable_for_offset<string::const_iterator, true>());
STATIC_ASSERT(test_unwrappable_for_offset<string::reverse_iterator, true>());
Expand Down Expand Up @@ -540,6 +571,13 @@ STATIC_ASSERT(test_unwrappable_for_offset<span<int>::iterator, true>());
STATIC_ASSERT(test_unwrappable_for_offset<span<int>::reverse_iterator, true>());
#endif // _HAS_CXX20

#if _HAS_CXX23
STATIC_ASSERT(test_unwrappable_for_offset<flat_set<int>::iterator, true>());
STATIC_ASSERT(test_unwrappable_for_offset<flat_set<int>::reverse_iterator, true>());
STATIC_ASSERT(test_unwrappable_for_offset<flat_map<int, int>::iterator, true>());
STATIC_ASSERT(test_unwrappable_for_offset<flat_map<int, int>::reverse_iterator, true>());
#endif // _HAS_CXX23

STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom<string::iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom<string::const_iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom<string::reverse_iterator>, false>());
Expand Down Expand Up @@ -612,6 +650,13 @@ STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom<span<int>::iterator>
STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom<span<int>::reverse_iterator>, false>());
#endif // _HAS_CXX20

#if _HAS_CXX23
STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom<flat_set<int>::iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom<flat_set<int>::reverse_iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom<flat_map<int, int>::iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_unverified<::DerivedFrom<flat_map<int, int>::reverse_iterator>, false>());
#endif // _HAS_CXX23

STATIC_ASSERT(test_unwrappable<::DerivedFrom<string::iterator>, false>());
STATIC_ASSERT(test_unwrappable<::DerivedFrom<string::const_iterator>, false>());
STATIC_ASSERT(test_unwrappable<::DerivedFrom<string::reverse_iterator>, false>());
Expand Down Expand Up @@ -684,6 +729,13 @@ STATIC_ASSERT(test_unwrappable<::DerivedFrom<span<int>::iterator>, false>());
STATIC_ASSERT(test_unwrappable<::DerivedFrom<span<int>::reverse_iterator>, false>());
#endif // _HAS_CXX20

#if _HAS_CXX23
STATIC_ASSERT(test_unwrappable<::DerivedFrom<flat_set<int>::iterator>, false>());
STATIC_ASSERT(test_unwrappable<::DerivedFrom<flat_set<int>::reverse_iterator>, false>());
STATIC_ASSERT(test_unwrappable<::DerivedFrom<flat_map<int, int>::iterator>, false>());
STATIC_ASSERT(test_unwrappable<::DerivedFrom<flat_map<int, int>::reverse_iterator>, false>());
#endif // _HAS_CXX23

STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom<string::iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom<string::const_iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom<string::reverse_iterator>, false>());
Expand Down Expand Up @@ -755,3 +807,10 @@ STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom<std::filesystem::path::c
STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom<span<int>::iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom<span<int>::reverse_iterator>, false>());
#endif // _HAS_CXX20

#if _HAS_CXX23
STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom<flat_set<int>::iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom<flat_set<int>::reverse_iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom<flat_map<int, int>::iterator>, false>());
STATIC_ASSERT(test_unwrappable_for_offset<::DerivedFrom<flat_map<int, int>::reverse_iterator>, false>());
#endif // _HAS_CXX23
37 changes: 37 additions & 0 deletions tests/std/tests/P0429R9_flat_map/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <class Iter>
void vandalize_pairing_iterator(Iter& pairing_iterator) {
++pairing_iterator._Key_it;
}

enum class iter_test { op_equals, op_spaceship, op_minus };

template <cont_type type, iter_test mode>
void test_death_iterator_at_different_offset() {
using C = conditional_t<type == cont_type::unique, flat_map<int, char>, flat_multimap<int, char>>;
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(); });

Expand Down Expand Up @@ -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<cont_type::unique, iter_test::op_equals>,
test_death_iterator_at_different_offset<cont_type::multi, iter_test::op_equals>,
test_death_iterator_at_different_offset<cont_type::unique, iter_test::op_spaceship>,
test_death_iterator_at_different_offset<cont_type::multi, iter_test::op_spaceship>,
test_death_iterator_at_different_offset<cont_type::unique, iter_test::op_minus>,
test_death_iterator_at_different_offset<cont_type::multi, iter_test::op_minus>,
});
#endif // _ITERATOR_DEBUG_LEVEL != 0

return exec.run(argc, argv);
}