diff --git a/stl/inc/flat_set b/stl/inc/flat_set index d860535aae..dd714ed1c5 100644 --- a/stl/inc/flat_set +++ b/stl/inc/flat_set @@ -637,7 +637,7 @@ private: _NODISCARD size_type _Erase(const _Ty& _Val) { _STL_INTERNAL_STATIC_ASSERT(_Transparent || is_same_v<_Ty, _Kty>); - if constexpr (_IsUnique) { + if constexpr (_IsUnique && is_same_v<_Ty, key_type>) { // Optimization restricted due to GH-5992 const const_iterator _Where = lower_bound(_Val); if (_Where != cend() && !_Compare(_Val, *_Where)) { _Mycont.erase(_Where); diff --git a/tests/std/include/test_container_requirements.hpp b/tests/std/include/test_container_requirements.hpp new file mode 100644 index 0000000000..cebe64d3ab --- /dev/null +++ b/tests/std/include/test_container_requirements.hpp @@ -0,0 +1,189 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +// Extracted common functionality from flat_map and flat_set tests. +// May be extended for other containers if needed. + +template +void assert_container_requirements(const T& s) { + T m = s; + assert(m == s); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_convertible_v); + static_assert(std::is_same_v m.end()), std::strong_ordering>); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v + || std::is_same_v); + static_assert(std::is_same_v); + + T my_moved = std::move(m); + assert(!(my_moved != s)); + + T my_empty{}; + assert(my_empty.empty()); + + T non_empty = s; + my_empty.swap(non_empty); + assert(non_empty.empty()); + assert(my_empty == s); + + std::swap(my_empty, non_empty); + assert(my_empty.empty()); + assert(non_empty == s); + + assert(s.cbegin() <= s.cend()); + assert((s.cbegin() < s.cend()) == !s.empty()); + + assert(m.begin() <= m.end()); + assert((m.begin() < m.end()) == !m.empty()); + + assert(static_cast(s.cend() - s.cbegin()) == s.size()); +} + +template +void assert_reversible_container_requirements(const T& s) { + static_assert(std::is_same_v, typename T::reverse_iterator>); + static_assert( + std::is_same_v, typename T::const_reverse_iterator>); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_convertible_v); +} + +template +concept map_has_nothrow_swappable_containers = requires { + typename T::key_container_type; + typename T::mapped_container_type; + requires std::is_nothrow_swappable_v; + requires std::is_nothrow_swappable_v; +}; + +template +concept set_has_nothrow_swappable_containers = requires { + typename T::container_type; + requires std::is_nothrow_swappable_v; +}; + +template +concept has_nothrow_swappable_containers = + set_has_nothrow_swappable_containers || map_has_nothrow_swappable_containers; + +template +void assert_noexcept_requirements(T& s) { + static_assert(noexcept(s.begin())); + static_assert(noexcept(s.end())); + static_assert(noexcept(s.cbegin())); + static_assert(noexcept(s.cend())); + static_assert(noexcept(s.rbegin())); + static_assert(noexcept(s.rend())); + static_assert(noexcept(s.crbegin())); + static_assert(noexcept(s.crend())); + + static_assert(noexcept(s.empty())); + static_assert(noexcept(s.size())); + static_assert(noexcept(s.max_size())); + + if constexpr (!std::is_const_v) { + constexpr bool is_noexcept = + has_nothrow_swappable_containers && std::is_nothrow_swappable_v; + static_assert(noexcept(s.swap(s)) == is_noexcept); + static_assert(noexcept(std::ranges::swap(s, s)) == is_noexcept); // using ADL-swap + static_assert(noexcept(s.clear())); + } +} + +template +void assert_is_sorted_maybe_unique(const T& s) { + const auto val_comp = s.value_comp(); + const auto begin_it = s.cbegin(); + const auto end_it = s.cend(); + + // internal check by the container itself + assert(s._Is_sorted_and_unique()); + + // external check observable by the user + assert(std::is_sorted(begin_it, end_it, val_comp)); + if constexpr (ExpectedUnique) { + const auto not_comp = [&](const auto& left, const auto& right) { return !val_comp(left, right); }; + const bool is_unique = std::adjacent_find(begin_it, end_it, not_comp) == end_it; + + if constexpr (std::formattable) { + if (!is_unique) { + std::println("Container {} is not unique", s); + } + } + assert(is_unique); + } +} + +template +void assert_set_requirements() { + using iterator = T::iterator; + using const_iterator = T::const_iterator; + using key_type = T::key_type; + using value_type = T::value_type; + using reference = T::reference; + using const_reference = T::const_reference; + + static_assert(std::same_as, const_iterator>); + static_assert(std::is_convertible_v); + + // additionally: + static_assert(std::is_same_v); + static_assert(std::same_as, iterator>); + static_assert(std::is_convertible_v); + static_assert(std::is_same_v().begin()), const value_type&>); + static_assert(std::is_same_v, value_type>); + static_assert(std::is_same_v); +} + +template +void assert_map_requirements() { + using iterator = T::iterator; + using const_iterator = T::const_iterator; + using key_type = T::key_type; + using value_type = T::value_type; + using mapped_type = T::mapped_type; + + static_assert(std::same_as, const_iterator>); + static_assert(std::is_convertible_v); + + // additionally: + static_assert(std::is_same_v>); +} + +template +void assert_three_way_comparability() { + using value_type = T::value_type; + if constexpr (std::three_way_comparable) { + static_assert(std::three_way_comparable); + } +} diff --git a/tests/std/tests/P0429R9_flat_map/test.cpp b/tests/std/tests/P0429R9_flat_map/test.cpp index 3cc8cff261..c7e0aba31d 100644 --- a/tests/std/tests/P0429R9_flat_map/test.cpp +++ b/tests/std/tests/P0429R9_flat_map/test.cpp @@ -6,14 +6,18 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include +#include +#include using namespace std; // See GH-5965: Speculative resolution of LWG-3963 "Different flat_(multi)map specializations @@ -31,45 +35,38 @@ concept IsFlatMap = is_specialization_v, flat_map> || is_specialization_v, flat_multimap>; template -bool check_container_requirements(T&&) { - return true; -} +void assert_all_requirements(const T& s) { + assert_container_requirements(s); + assert_reversible_container_requirements(s); + assert_three_way_comparability(); + assert_map_requirements(); -template -consteval bool check_reversible_container_requirements() { - using map_t = remove_cvref_t; - return is_same_v, typename map_t::reverse_iterator> - && is_same_v, typename map_t::const_reverse_iterator> - && is_same_v().begin()), typename map_t::iterator> - && is_same_v().end()), typename map_t::iterator> - && is_same_v().cbegin()), typename map_t::const_iterator> - && is_same_v().cend()), typename map_t::const_iterator> - && is_same_v().rbegin()), typename map_t::reverse_iterator> - && is_same_v().rend()), typename map_t::reverse_iterator> - && is_same_v().crbegin()), typename map_t::const_reverse_iterator> - && is_same_v().crend()), typename map_t::const_reverse_iterator> - && is_convertible_v - && is_convertible_v; -} + assert_noexcept_requirements(s); + assert_noexcept_requirements(const_cast(s)); -template -constexpr bool check_reversible_container_requirements(T&&) { - return check_reversible_container_requirements(); + assert_is_sorted_maybe_unique<_Is_specialization_v>(s); } template -bool check_requirements(T&& obj) { - return check_container_requirements(forward(obj)) && check_reversible_container_requirements(forward(obj)); +[[nodiscard]] bool check_key_content(const T& obj, const typename T::key_container_type& expected) { + assert_all_requirements(obj); + return ranges::equal(obj.keys(), expected); } template -bool check_key_content(const T& obj, const typename T::key_container_type& expected) { - return ranges::equal(obj.keys(), expected); +[[nodiscard]] bool check_value_content(const T& obj, const typename T::mapped_container_type& expected) { + return ranges::equal(obj.values(), expected); } template -bool check_value_content(const T& obj, const typename T::mapped_container_type& expected) { - return ranges::equal(obj.values(), expected); +[[nodiscard]] bool check_content(const T& obj, const type_identity_t& expected) { + assert_all_requirements(obj); + if (!ranges::equal(obj, expected)) { + println(stderr, "Unexpected content!\nExpected {}", expected); + println(stderr, "Actual {}", obj); + return false; + } + return true; } enum class subrange_type : bool { @@ -89,7 +86,7 @@ struct subrange_t { // represents a closed subrange [first_index, last_index] }; template -bool check_value_content( +[[nodiscard]] bool check_value_content( const T& obj, const typename T::mapped_container_type& expected, const vector& subranges) { const auto& actual = obj.values(); if (actual.size() != expected.size()) { @@ -152,6 +149,26 @@ class MyAllocator : public allocator { static inline atomic s_allocations{0}; }; +template +struct almost_pair { + using first_type = T; + using second_type = U; + + T first; + U second; + + constexpr operator pair() const { + return {first, second}; + } +}; + +namespace std { + template + struct tuple_element> { + using type = conditional_t; + }; +} // namespace std + template class Packaged { private: @@ -200,141 +217,525 @@ struct TransparentPackagedCompare : PackagedCompare { } }; +struct MyAllocatorCounter { + MyAllocatorCounter() : activeAllocations{MyAllocator::getActiveAllocationCount()} {} + + [[nodiscard]] bool check_then_reset() { + const bool allocated_some = MyAllocator::getActiveAllocationCount() > activeAllocations; + activeAllocations = MyAllocator::getActiveAllocationCount(); + return allocated_some; + } + + size_t activeAllocations; +}; + +struct key_comparer { + const auto& extract_key(const auto& obj) const { + if constexpr (requires { obj.key; }) { + return obj.key; + } else { + return obj; + } + } + + bool operator()(const auto& lhs, const auto& rhs) const { + return extract_key(lhs) < extract_key(rhs); + } + + using is_transparent = int; +}; + +template