Skip to content

Commit 2b10638

Browse files
committed
[libc++] Implement P3567R2 flat_meow fixes
1 parent 718ef34 commit 2b10638

20 files changed

+600
-60
lines changed

libcxx/include/__flat_map/flat_map.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,15 @@ class flat_map {
587587
__append_sort_merge_unique</*WasSorted = */ false>(ranges::begin(__range), ranges::end(__range));
588588
}
589589

590+
template <_ContainerCompatibleRange<value_type> _Range>
591+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(sorted_unique_t, _Range&& __range) {
592+
if constexpr (ranges::sized_range<_Range>) {
593+
__reserve(ranges::size(__range));
594+
}
595+
596+
__append_sort_merge_unique</*WasSorted = */ true>(ranges::begin(__range), ranges::end(__range));
597+
}
598+
590599
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
591600
insert(__il.begin(), __il.end());
592601
}
@@ -736,14 +745,17 @@ class flat_map {
736745
return iterator(std::move(__key_it), std::move(__mapped_it));
737746
}
738747

739-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(flat_map& __y) noexcept {
740-
// warning: The spec has unconditional noexcept, which means that
741-
// if any of the following functions throw an exception,
742-
// std::terminate will be called.
743-
// This is discussed in P2767, which hasn't been voted on yet.
748+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
749+
swap(flat_map& __y) noexcept(is_nothrow_swappable_v<key_container_type> &&
750+
is_nothrow_swappable_v<mapped_container_type> && is_nothrow_swappable_v<key_compare>) {
751+
auto __on_failure = std::__make_exception_guard([&]() noexcept {
752+
clear() /* noexcept */;
753+
__y.clear() /* noexcept */;
754+
});
744755
ranges::swap(__compare_, __y.__compare_);
745756
ranges::swap(__containers_.keys, __y.__containers_.keys);
746757
ranges::swap(__containers_.values, __y.__containers_.values);
758+
__on_failure.__complete();
747759
}
748760

749761
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() noexcept {
@@ -876,7 +888,8 @@ class flat_map {
876888
__x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way);
877889
}
878890

879-
friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(flat_map& __x, flat_map& __y) noexcept {
891+
friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
892+
swap(flat_map& __x, flat_map& __y) noexcept(noexcept(__x.swap(__y))) {
880893
__x.swap(__y);
881894
}
882895

libcxx/include/__flat_map/flat_multimap.h

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,15 @@ class flat_multimap {
568568
__append_sort_merge</*WasSorted = */ false>(ranges::begin(__range), ranges::end(__range));
569569
}
570570

571+
template <_ContainerCompatibleRange<value_type> _Range>
572+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(sorted_equivalent_t, _Range&& __range) {
573+
if constexpr (ranges::sized_range<_Range>) {
574+
__reserve(ranges::size(__range));
575+
}
576+
577+
__append_sort_merge</*WasSorted = */ true>(ranges::begin(__range), ranges::end(__range));
578+
}
579+
571580
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
572581
insert(__il.begin(), __il.end());
573582
}
@@ -628,13 +637,17 @@ class flat_multimap {
628637
return iterator(std::move(__key_it), std::move(__mapped_it));
629638
}
630639

631-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(flat_multimap& __y) noexcept {
632-
// warning: The spec has unconditional noexcept, which means that
633-
// if any of the following functions throw an exception,
634-
// std::terminate will be called
640+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(flat_multimap& __y) noexcept(
641+
is_nothrow_swappable_v<key_container_type> && is_nothrow_swappable_v<mapped_container_type> &&
642+
is_nothrow_swappable_v<key_compare>) {
643+
auto __on_failure = std::__make_exception_guard([&]() noexcept {
644+
clear() /* noexcept */;
645+
__y.clear() /* noexcept */;
646+
});
635647
ranges::swap(__compare_, __y.__compare_);
636648
ranges::swap(__containers_.keys, __y.__containers_.keys);
637649
ranges::swap(__containers_.values, __y.__containers_.values);
650+
__on_failure.__complete();
638651
}
639652

640653
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() noexcept {
@@ -771,7 +784,7 @@ class flat_multimap {
771784
}
772785

773786
friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
774-
swap(flat_multimap& __x, flat_multimap& __y) noexcept {
787+
swap(flat_multimap& __x, flat_multimap& __y) noexcept(noexcept(__x.swap(__y))) {
775788
__x.swap(__y);
776789
}
777790

libcxx/include/__flat_set/flat_multiset.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,15 @@ class flat_multiset {
401401
__append_sort_merge</*WasSorted = */ false>(std::forward<_Range>(__range));
402402
}
403403

404+
template <_ContainerCompatibleRange<value_type> _Range>
405+
_LIBCPP_HIDE_FROM_ABI void insert_range(sorted_equivalent_t, _Range&& __range) {
406+
if constexpr (ranges::sized_range<_Range>) {
407+
__reserve(ranges::size(__range));
408+
}
409+
410+
__append_sort_merge</*WasSorted = */ true>(std::forward<_Range>(__range));
411+
}
412+
404413
_LIBCPP_HIDE_FROM_ABI void insert(initializer_list<value_type> __il) { insert(__il.begin(), __il.end()); }
405414

406415
_LIBCPP_HIDE_FROM_ABI void insert(sorted_equivalent_t, initializer_list<value_type> __il) {
@@ -454,13 +463,15 @@ class flat_multiset {
454463
return iterator(std::move(__key_it));
455464
}
456465

457-
_LIBCPP_HIDE_FROM_ABI void swap(flat_multiset& __y) noexcept {
458-
// warning: The spec has unconditional noexcept, which means that
459-
// if any of the following functions throw an exception,
460-
// std::terminate will be called
461-
// This is discussed in P3567, which hasn't been voted on yet.
466+
_LIBCPP_HIDE_FROM_ABI void
467+
swap(flat_multiset& __y) noexcept(is_nothrow_swappable_v<container_type> && is_nothrow_swappable_v<key_compare>) {
468+
auto __on_failure = std::__make_exception_guard([&]() noexcept {
469+
clear() /* noexcept */;
470+
__y.clear() /* noexcept */;
471+
});
462472
ranges::swap(__compare_, __y.__compare_);
463473
ranges::swap(__keys_, __y.__keys_);
474+
__on_failure.__complete();
464475
}
465476

466477
_LIBCPP_HIDE_FROM_ABI void clear() noexcept { __keys_.clear(); }
@@ -578,7 +589,9 @@ class flat_multiset {
578589
__x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way);
579590
}
580591

581-
friend _LIBCPP_HIDE_FROM_ABI void swap(flat_multiset& __x, flat_multiset& __y) noexcept { __x.swap(__y); }
592+
friend _LIBCPP_HIDE_FROM_ABI void swap(flat_multiset& __x, flat_multiset& __y) noexcept(noexcept(__x.swap(__y))) {
593+
__x.swap(__y);
594+
}
582595

583596
private:
584597
template <bool _WasSorted, class... _Args>

libcxx/include/__flat_set/flat_set.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,15 @@ class flat_set {
458458
__append_sort_merge_unique</*WasSorted = */ false>(std::forward<_Range>(__range));
459459
}
460460

461+
template <_ContainerCompatibleRange<value_type> _Range>
462+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(std::sorted_unique_t, _Range&& __range) {
463+
if constexpr (ranges::sized_range<_Range>) {
464+
__reserve(ranges::size(__range));
465+
}
466+
467+
__append_sort_merge_unique</*WasSorted = */ true>(std::forward<_Range>(__range));
468+
}
469+
461470
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
462471
insert(__il.begin(), __il.end());
463472
}
@@ -516,13 +525,15 @@ class flat_set {
516525
return iterator(std::move(__key_it));
517526
}
518527

519-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(flat_set& __y) noexcept {
520-
// warning: The spec has unconditional noexcept, which means that
521-
// if any of the following functions throw an exception,
522-
// std::terminate will be called.
523-
// This is discussed in P2767, which hasn't been voted on yet.
528+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
529+
swap(flat_set& __y) noexcept(is_nothrow_swappable_v<container_type> && is_nothrow_swappable_v<key_compare>) {
530+
auto __on_failure = std::__make_exception_guard([&]() noexcept {
531+
clear() /* noexcept */;
532+
__y.clear() /* noexcept */;
533+
});
524534
ranges::swap(__compare_, __y.__compare_);
525535
ranges::swap(__keys_, __y.__keys_);
536+
__on_failure.__complete();
526537
}
527538

528539
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() noexcept { __keys_.clear(); }
@@ -647,7 +658,8 @@ class flat_set {
647658
__x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way);
648659
}
649660

650-
friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(flat_set& __x, flat_set& __y) noexcept {
661+
friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
662+
swap(flat_set& __x, flat_set& __y) noexcept(noexcept(__x.swap(__y))) {
651663
__x.swap(__y);
652664
}
653665

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10+
11+
// <flat_map>
12+
13+
// template<container-compatible-range<value_type> R>
14+
// void insert_range(sorted_unique, R&& rg);
15+
16+
#include <algorithm>
17+
#include <deque>
18+
#include <flat_map>
19+
#include <functional>
20+
#include <ranges>
21+
#include <vector>
22+
23+
#include "MinSequenceContainer.h"
24+
#include "../helpers.h"
25+
#include "MoveOnly.h"
26+
#include "test_macros.h"
27+
#include "test_iterators.h"
28+
#include "min_allocator.h"
29+
30+
// test constraint container-compatible-range
31+
template <class M, class R>
32+
concept CanInsertRangeSortedUnique = requires(M m, R&& r) { m.insert_range(std::sorted_unique, std::forward<R>(r)); };
33+
34+
using Map = std::flat_map<int, double>;
35+
36+
static_assert(CanInsertRangeSortedUnique<Map, std::ranges::subrange<std::pair<int, double>*>>);
37+
static_assert(CanInsertRangeSortedUnique<Map, std::ranges::subrange<std::pair<short, double>*>>);
38+
static_assert(!CanInsertRangeSortedUnique<Map, std::ranges::subrange<int*>>);
39+
static_assert(!CanInsertRangeSortedUnique<Map, std::ranges::subrange<double*>>);
40+
41+
template <class KeyContainer, class ValueContainer>
42+
constexpr void test() {
43+
using Key = typename KeyContainer::value_type;
44+
using Value = typename ValueContainer::value_type;
45+
46+
{
47+
using P = std::pair<int, int>;
48+
using M = std::flat_map<Key, Value, std::less<Key>, KeyContainer, ValueContainer>;
49+
using It = forward_iterator<const P*>;
50+
M m = {{10, 1}, {8, 2}, {5, 3}, {2, 4}, {1, 5}};
51+
P ar[] = {{1, 2}, {3, 1}, {4, 3}, {5, 5}, {9, 6}};
52+
std::ranges::subrange r = {It(ar), It(ar + 5)};
53+
static_assert(std::ranges::common_range<decltype(r)>);
54+
m.insert_range(std::sorted_unique, r);
55+
assert((m == M{{1, 5}, {2, 4}, {3, 1}, {4, 3}, {5, 3}, {8, 2}, {9, 6}, {10, 1}}));
56+
}
57+
{
58+
using P = std::pair<int, int>;
59+
using M = std::flat_map<Key, Value, std::greater<>, KeyContainer, ValueContainer>;
60+
using It = cpp20_input_iterator<const P*>;
61+
M m = {{8, 1}, {5, 2}, {3, 3}, {2, 4}};
62+
P ar[] = {{9, 6}, {5, 5}, {4, 3}, {3, 1}, {1, 2}};
63+
std::ranges::subrange r = {It(ar), sentinel_wrapper<It>(It(ar + 5))};
64+
static_assert(!std::ranges::common_range<decltype(r)>);
65+
m.insert_range(std::sorted_unique, r);
66+
assert((m == M{{1, 2}, {2, 4}, {3, 3}, {4, 3}, {5, 2}, {8, 1}, {9, 6}}));
67+
}
68+
{
69+
// was empty
70+
using P = std::pair<int, int>;
71+
using M = std::flat_map<Key, Value, std::less<Key>, KeyContainer, ValueContainer>;
72+
M m;
73+
P ar[] = {{1, 2}, {3, 1}, {4, 3}, {5, 5}, {9, 6}};
74+
m.insert_range(std::sorted_unique, ar);
75+
assert(std::ranges::equal(m, ar));
76+
}
77+
}
78+
79+
constexpr bool test() {
80+
test<std::vector<int>, std::vector<int>>();
81+
#ifndef __cpp_lib_constexpr_deque
82+
if (!TEST_IS_CONSTANT_EVALUATED)
83+
#endif
84+
{
85+
test<std::deque<int>, std::vector<int>>();
86+
}
87+
test<MinSequenceContainer<int>, MinSequenceContainer<int>>();
88+
test<std::vector<int, min_allocator<int>>, std::vector<int, min_allocator<int>>>();
89+
{
90+
// Items are forwarded correctly from the input range
91+
std::pair<MoveOnly, MoveOnly> a[] = {{1, 1}, {3, 3}, {4, 4}, {5, 5}};
92+
std::flat_map<MoveOnly, MoveOnly> m;
93+
m.insert_range(std::sorted_unique, a | std::views::as_rvalue);
94+
std::pair<MoveOnly, MoveOnly> expected[] = {{1, 1}, {3, 3}, {4, 4}, {5, 5}};
95+
assert(std::ranges::equal(m, expected));
96+
}
97+
{
98+
// The element type of the range doesn't need to be std::pair
99+
std::pair<int, int> pa[] = {{1, 1}, {3, 3}, {4, 4}, {5, 5}};
100+
std::vector<std::reference_wrapper<std::pair<int, int>>> a(pa, pa + 4);
101+
std::flat_map<int, int> m;
102+
m.insert_range(std::sorted_unique, a);
103+
std::pair<int, int> expected[] = {{1, 1}, {3, 3}, {4, 4}, {5, 5}};
104+
assert(std::ranges::equal(m, expected));
105+
}
106+
if (!TEST_IS_CONSTANT_EVALUATED) {
107+
auto insert_func = [](auto& m, const auto& newValues) { m.insert_range(std::sorted_unique, newValues); };
108+
test_insert_range_exception_guarantee(insert_func);
109+
}
110+
111+
return true;
112+
}
113+
114+
int main(int, char**) {
115+
test();
116+
#if TEST_STD_VER >= 26
117+
static_assert(test());
118+
#endif
119+
120+
return 0;
121+
}

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/swap_exception.pass.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,14 @@
77
//===----------------------------------------------------------------------===//
88

99
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10-
// `check_assertion.h` requires Unix headers and regex support.
11-
// REQUIRES: has-unix-headers
12-
// UNSUPPORTED: no-localization
1310
// UNSUPPORTED: no-exceptions
1411

1512
// <flat_map>
1613

1714
// void swap(flat_map& y) noexcept;
1815
// friend void swap(flat_map& x, flat_map& y) noexcept
1916

20-
// Test that std::terminate is called if any exception is thrown during swap
17+
// Test that the variants are hold if any exception is thrown during swap
2118

2219
#include <flat_map>
2320
#include <cassert>
@@ -42,8 +39,15 @@ void test_swap_exception_guarantee([[maybe_unused]] F&& swap_function) {
4239
m1.emplace(2, 2);
4340
m2.emplace(3, 3);
4441
m2.emplace(4, 4);
45-
// swap is noexcept
46-
EXPECT_STD_TERMINATE([&] { swap_function(m1, m2); });
42+
try {
43+
swap_function(m1, m2);
44+
assert(false);
45+
} catch (int) {
46+
check_invariant(m1);
47+
check_invariant(m2);
48+
LIBCPP_ASSERT(m1.size() == 0);
49+
LIBCPP_ASSERT(m2.size() == 0);
50+
}
4751
}
4852

4953
{
@@ -58,8 +62,15 @@ void test_swap_exception_guarantee([[maybe_unused]] F&& swap_function) {
5862
m2.emplace(3, 3);
5963
m2.emplace(4, 4);
6064

61-
// swap is noexcept
62-
EXPECT_STD_TERMINATE([&] { swap_function(m1, m2); });
65+
try {
66+
swap_function(m1, m2);
67+
assert(false);
68+
} catch (int) {
69+
check_invariant(m1);
70+
check_invariant(m2);
71+
LIBCPP_ASSERT(m1.size() == 0);
72+
LIBCPP_ASSERT(m2.size() == 0);
73+
}
6374
}
6475
}
6576

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/swap_free.pass.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,10 @@ concept NoExceptAdlSwap = requires(T t1, T t2) {
3131
{ swap(t1, t2) } noexcept;
3232
};
3333

34-
static_assert(NoExceptAdlSwap<std::flat_map<int, int>>);
35-
34+
static_assert(NoExceptAdlSwap<std::flat_map<int, int, std::less<int>, std::vector<int>, std::vector<int>>>);
3635
#ifndef TEST_HAS_NO_EXCEPTIONS
3736
static_assert(
38-
NoExceptAdlSwap<std::flat_map<int, int, std::less<int>, ThrowOnMoveContainer<int>, ThrowOnMoveContainer<int>>>);
37+
!NoExceptAdlSwap<std::flat_map<int, int, std::less<int>, ThrowOnMoveContainer<int>, ThrowOnMoveContainer<int>>>);
3938
#endif
4039

4140
template <class KeyContainer, class ValueContainer>

0 commit comments

Comments
 (0)