Skip to content

[libc++] constexpr flat_map #137453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jun 21, 2025
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
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/21.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Implemented Papers
- P1222R4: A Standard ``flat_set`` (`Github <https://github.com/llvm/llvm-project/issues/105193>`__)
- P2897R7: ``aligned_accessor``: An mdspan accessor expressing pointer over-alignment (`Github <https://github.com/llvm/llvm-project/issues/118372>`__)
- P3247R2: Deprecate the notion of trivial types (`Github <https://github.com/llvm/llvm-project/issues/118387>`__)
- P3372R3: ``constexpr`` containers and adaptors (`Github <https://github.com/llvm/llvm-project/issues/128673>`__) (Only ``constexpr flat_map`` is implemented)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "only flat_map" part looks incorrect; #127876 shows that list, forward_list and priority_queue have already been implemented as well.

- P2441R2: ``views::join_with`` (`Github <https://github.com/llvm/llvm-project/issues/105185>`__)
- P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github <https://github.com/llvm/llvm-project/issues/105252>`__)
- P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github <https://github.com/llvm/llvm-project/issues/105250>`__)
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"`P3137R3 <https://wg21.link/P3137R3>`__","``views::to_input``","2025-02 (Hagenberg)","","",""
"`P0472R3 <https://wg21.link/P0472R3>`__","Put ``std::monostate`` in ``<utility>``","2025-02 (Hagenberg)","|Complete|","21",""
"`P3349R1 <https://wg21.link/P3349R1>`__","Converting contiguous iterators to pointers","2025-02 (Hagenberg)","","",""
"`P3372R3 <https://wg21.link/P3372R3>`__","constexpr containers and adaptors","2025-02 (Hagenberg)","","",""
"`P3372R3 <https://wg21.link/P3372R3>`__","constexpr containers and adaptors","2025-02 (Hagenberg)","|In Progress|","",""
"`P3378R2 <https://wg21.link/P3378R2>`__","constexpr exception types","2025-02 (Hagenberg)","","",""
"`P3441R2 <https://wg21.link/P3441R2>`__","Rename ``simd_split`` to ``simd_chunk``","2025-02 (Hagenberg)","","",""
"`P3287R3 <https://wg21.link/P3287R3>`__","Exploration of namespaces for ``std::simd``","2025-02 (Hagenberg)","","",""
Expand Down
391 changes: 234 additions & 157 deletions libcxx/include/__flat_map/flat_map.h

Large diffs are not rendered by default.

58 changes: 36 additions & 22 deletions libcxx/include/__flat_map/key_value_iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ struct __key_value_iterator {

struct __arrow_proxy {
__reference __ref_;
_LIBCPP_HIDE_FROM_ABI __reference* operator->() { return std::addressof(__ref_); }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __reference* operator->() { return std::addressof(__ref_); }
};

__key_iterator __key_iter_;
Expand All @@ -69,99 +69,113 @@ struct __key_value_iterator {

_LIBCPP_HIDE_FROM_ABI __key_value_iterator() = default;

_LIBCPP_HIDE_FROM_ABI __key_value_iterator(__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, !_Const> __i)
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
__key_value_iterator(__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, !_Const> __i)
requires _Const && convertible_to<typename _KeyContainer::iterator, __key_iterator> &&
convertible_to<typename _MappedContainer::iterator, __mapped_iterator>
: __key_iter_(std::move(__i.__key_iter_)), __mapped_iter_(std::move(__i.__mapped_iter_)) {}

_LIBCPP_HIDE_FROM_ABI __key_value_iterator(__key_iterator __key_iter, __mapped_iterator __mapped_iter)
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
__key_value_iterator(__key_iterator __key_iter, __mapped_iterator __mapped_iter)
: __key_iter_(std::move(__key_iter)), __mapped_iter_(std::move(__mapped_iter)) {}

_LIBCPP_HIDE_FROM_ABI __reference operator*() const { return __reference(*__key_iter_, *__mapped_iter_); }
_LIBCPP_HIDE_FROM_ABI __arrow_proxy operator->() const { return __arrow_proxy{**this}; }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __reference operator*() const {
return __reference(*__key_iter_, *__mapped_iter_);
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __arrow_proxy operator->() const { return __arrow_proxy{**this}; }

_LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator++() {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_value_iterator& operator++() {
++__key_iter_;
++__mapped_iter_;
return *this;
}

_LIBCPP_HIDE_FROM_ABI __key_value_iterator operator++(int) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_value_iterator operator++(int) {
__key_value_iterator __tmp(*this);
++*this;
return __tmp;
}

_LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator--() {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_value_iterator& operator--() {
--__key_iter_;
--__mapped_iter_;
return *this;
}

_LIBCPP_HIDE_FROM_ABI __key_value_iterator operator--(int) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_value_iterator operator--(int) {
__key_value_iterator __tmp(*this);
--*this;
return __tmp;
}

_LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator+=(difference_type __x) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_value_iterator& operator+=(difference_type __x) {
__key_iter_ += __x;
__mapped_iter_ += __x;
return *this;
}

_LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator-=(difference_type __x) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_value_iterator& operator-=(difference_type __x) {
__key_iter_ -= __x;
__mapped_iter_ -= __x;
return *this;
}

_LIBCPP_HIDE_FROM_ABI __reference operator[](difference_type __n) const { return *(*this + __n); }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __reference operator[](difference_type __n) const {
return *(*this + __n);
}

_LIBCPP_HIDE_FROM_ABI friend constexpr bool
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend bool
operator==(const __key_value_iterator& __x, const __key_value_iterator& __y) {
return __x.__key_iter_ == __y.__key_iter_;
}

_LIBCPP_HIDE_FROM_ABI friend bool operator<(const __key_value_iterator& __x, const __key_value_iterator& __y) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend bool
operator<(const __key_value_iterator& __x, const __key_value_iterator& __y) {
return __x.__key_iter_ < __y.__key_iter_;
}

_LIBCPP_HIDE_FROM_ABI friend bool operator>(const __key_value_iterator& __x, const __key_value_iterator& __y) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend bool
operator>(const __key_value_iterator& __x, const __key_value_iterator& __y) {
return __y < __x;
}

_LIBCPP_HIDE_FROM_ABI friend bool operator<=(const __key_value_iterator& __x, const __key_value_iterator& __y) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend bool
operator<=(const __key_value_iterator& __x, const __key_value_iterator& __y) {
return !(__y < __x);
}

_LIBCPP_HIDE_FROM_ABI friend bool operator>=(const __key_value_iterator& __x, const __key_value_iterator& __y) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend bool
operator>=(const __key_value_iterator& __x, const __key_value_iterator& __y) {
return !(__x < __y);
}

_LIBCPP_HIDE_FROM_ABI friend auto operator<=>(const __key_value_iterator& __x, const __key_value_iterator& __y)
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend auto
operator<=>(const __key_value_iterator& __x, const __key_value_iterator& __y)
requires three_way_comparable<__key_iterator>
{
return __x.__key_iter_ <=> __y.__key_iter_;
}

_LIBCPP_HIDE_FROM_ABI friend __key_value_iterator operator+(const __key_value_iterator& __i, difference_type __n) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend __key_value_iterator
operator+(const __key_value_iterator& __i, difference_type __n) {
auto __tmp = __i;
__tmp += __n;
return __tmp;
}

_LIBCPP_HIDE_FROM_ABI friend __key_value_iterator operator+(difference_type __n, const __key_value_iterator& __i) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend __key_value_iterator
operator+(difference_type __n, const __key_value_iterator& __i) {
return __i + __n;
}

_LIBCPP_HIDE_FROM_ABI friend __key_value_iterator operator-(const __key_value_iterator& __i, difference_type __n) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend __key_value_iterator
operator-(const __key_value_iterator& __i, difference_type __n) {
auto __tmp = __i;
__tmp -= __n;
return __tmp;
}

_LIBCPP_HIDE_FROM_ABI friend difference_type
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend difference_type
operator-(const __key_value_iterator& __x, const __key_value_iterator& __y) {
return difference_type(__x.__key_iter_ - __y.__key_iter_);
}
Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__flat_map/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct __flat_map_utils {
// roll back the changes it made to the map. If it cannot roll back the changes, it will
// clear the map.
template <class _Map, class _IterK, class _IterM, class _KeyArg, class... _MArgs>
_LIBCPP_HIDE_FROM_ABI static typename _Map::iterator __emplace_exact_pos(
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static typename _Map::iterator __emplace_exact_pos(
_Map& __map, _IterK&& __it_key, _IterM&& __it_mapped, _KeyArg&& __key, _MArgs&&... __mapped_args) {
auto __on_key_failed = std::__make_exception_guard([&]() noexcept {
using _KeyContainer = typename _Map::key_container_type;
Expand Down Expand Up @@ -82,7 +82,7 @@ struct __flat_map_utils {
// TODO: We could optimize this, see
// https://github.com/llvm/llvm-project/issues/108624
template <class _Map, class _InputIterator, class _Sentinel>
_LIBCPP_HIDE_FROM_ABI static typename _Map::size_type
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static typename _Map::size_type
__append(_Map& __map, _InputIterator __first, _Sentinel __last) {
typename _Map::size_type __num_appended = 0;
for (; __first != __last; ++__first) {
Expand Down
3 changes: 3 additions & 0 deletions libcxx/include/module.modulemap.in
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,9 @@ module std [system] {

header "flat_map"
export *
export std.algorithm.ranges_sort
export std.ranges.zip_view
export std.tuple
Comment on lines +1301 to +1303
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change requested. Do you have any idea why these missing exports were not caught before?

Copy link
Member Author

@huixie90 huixie90 May 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The short answer: I don't think I have any clue on how clang modules work exactly. I don't even know why exporting these are required at all. These things are all internal details of the implementation of flat_map and the users will not touch them at all. Their headers are properly included directly by flat_map.h.

However, as soon as I added constexpr to the member functions, I got loads of linker errors about the missing symbols like __intro_sort<.....>. The only way I find to fix these linker errors is to exporting these from the flat_map module

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite weird. Not sure whether this is related to that __intro_sort isn't constexpr yet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite weird. Not sure whether this is related to that __intro_sort isn't constexpr yet.

But how does this explain a linker error...

}

module flat_set {
Expand Down
16 changes: 8 additions & 8 deletions libcxx/test/std/containers/Emplaceable.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,34 @@ class Emplaceable {
double double_;

public:
TEST_CONSTEXPR Emplaceable() : int_(0), double_(0) {}
TEST_CONSTEXPR Emplaceable(int i, double d) : int_(i), double_(d) {}
TEST_CONSTEXPR_CXX14 Emplaceable(Emplaceable&& x) : int_(x.int_), double_(x.double_) {
TEST_CONSTEXPR_CXX20 Emplaceable() : int_(0), double_(0) {}
TEST_CONSTEXPR_CXX20 Emplaceable(int i, double d) : int_(i), double_(d) {}
TEST_CONSTEXPR_CXX20 Emplaceable(Emplaceable&& x) : int_(x.int_), double_(x.double_) {
x.int_ = 0;
x.double_ = 0;
}
TEST_CONSTEXPR_CXX14 Emplaceable& operator=(Emplaceable&& x) {
TEST_CONSTEXPR_CXX20 Emplaceable& operator=(Emplaceable&& x) {
int_ = x.int_;
x.int_ = 0;
double_ = x.double_;
x.double_ = 0;
return *this;
}

TEST_CONSTEXPR bool operator==(const Emplaceable& x) const { return int_ == x.int_ && double_ == x.double_; }
TEST_CONSTEXPR bool operator<(const Emplaceable& x) const {
TEST_CONSTEXPR_CXX20 bool operator==(const Emplaceable& x) const { return int_ == x.int_ && double_ == x.double_; }
TEST_CONSTEXPR_CXX20 bool operator<(const Emplaceable& x) const {
return int_ < x.int_ || (int_ == x.int_ && double_ < x.double_);
}

TEST_CONSTEXPR int get() const { return int_; }
TEST_CONSTEXPR_CXX20 int get() const { return int_; }
};

template <>
struct std::hash<Emplaceable> {
typedef Emplaceable argument_type;
typedef std::size_t result_type;

TEST_CONSTEXPR std::size_t operator()(const Emplaceable& x) const { return static_cast<std::size_t>(x.get()); }
TEST_CONSTEXPR_CXX20 std::size_t operator()(const Emplaceable& x) const { return static_cast<std::size_t>(x.get()); }
};

#endif // TEST_STD_VER >= 11
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
#include <flat_map>
#include <functional>
#include <stdexcept>
#include <type_traits>
#include <vector>

#include "MinSequenceContainer.h"
#include "min_allocator.h"
#include "test_macros.h"

template <class KeyContainer, class ValueContainer>
void test() {
constexpr void test() {
using P = std::pair<int, double>;
P ar[] = {
P(1, 1.5),
Expand All @@ -49,10 +50,12 @@ void test() {
assert(m.at(4) == 4.5);
assert(m.at(5) == 5.5);
#ifndef TEST_HAS_NO_EXCEPTIONS
try {
TEST_IGNORE_NODISCARD m.at(6);
assert(false);
} catch (std::out_of_range&) {
if (!TEST_IS_CONSTANT_EVALUATED) {
try {
TEST_IGNORE_NODISCARD m.at(6);
assert(false);
} catch (std::out_of_range&) {
}
}
#endif
assert(m.at(7) == 7.5);
Expand All @@ -70,10 +73,12 @@ void test() {
assert(m.at(4) == 4.5);
assert(m.at(5) == 5.5);
#ifndef TEST_HAS_NO_EXCEPTIONS
try {
TEST_IGNORE_NODISCARD m.at(6);
assert(false);
} catch (std::out_of_range&) {
if (!TEST_IS_CONSTANT_EVALUATED) {
try {
TEST_IGNORE_NODISCARD m.at(6);
assert(false);
} catch (std::out_of_range&) {
}
}
#endif
assert(m.at(7) == 7.5);
Expand All @@ -82,11 +87,25 @@ void test() {
}
}

int main(int, char**) {
constexpr bool test() {
test<std::vector<int>, std::vector<double>>();
test<std::deque<int>, std::vector<double>>();
#ifndef __cpp_lib_constexpr_deque
if (!TEST_IS_CONSTANT_EVALUATED)
#endif
{
test<std::deque<int>, std::vector<double>>();
}
test<MinSequenceContainer<int>, MinSequenceContainer<double>>();
test<std::vector<int, min_allocator<int>>, std::vector<double, min_allocator<double>>>();

return true;
}

int main(int, char**) {
test();
#if TEST_STD_VER >= 26
static_assert(test());
#endif

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static_assert(!CanAt<NonTransparentMap>);
static_assert(!CanAt<const NonTransparentMap>);

template <class KeyContainer, class ValueContainer>
void test() {
constexpr void test() {
using P = std::pair<int, double>;
P ar[] = {
P(1, 1.5),
Expand All @@ -60,10 +60,12 @@ void test() {
assert(m.at(Transparent<int>{4}) == 4.5);
assert(m.at(Transparent<int>{5}) == 5.5);
#ifndef TEST_HAS_NO_EXCEPTIONS
try {
TEST_IGNORE_NODISCARD m.at(Transparent<int>{6});
assert(false);
} catch (std::out_of_range&) {
if (!TEST_IS_CONSTANT_EVALUATED) {
try {
TEST_IGNORE_NODISCARD m.at(Transparent<int>{6});
assert(false);
} catch (std::out_of_range&) {
}
}
#endif
assert(m.at(Transparent<int>{7}) == 7.5);
Expand All @@ -81,10 +83,12 @@ void test() {
assert(m.at(Transparent<int>{4}) == 4.5);
assert(m.at(Transparent<int>{5}) == 5.5);
#ifndef TEST_HAS_NO_EXCEPTIONS
try {
TEST_IGNORE_NODISCARD m.at(Transparent<int>{6});
assert(false);
} catch (std::out_of_range&) {
if (!TEST_IS_CONSTANT_EVALUATED) {
try {
TEST_IGNORE_NODISCARD m.at(Transparent<int>{6});
assert(false);
} catch (std::out_of_range&) {
}
}
#endif
assert(m.at(Transparent<int>{7}) == 7.5);
Expand All @@ -93,9 +97,14 @@ void test() {
}
}

int main(int, char**) {
constexpr bool test() {
test<std::vector<int>, std::vector<double>>();
test<std::deque<int>, std::vector<double>>();
#ifndef __cpp_lib_constexpr_deque
if (!TEST_IS_CONSTANT_EVALUATED)
#endif
{
test<std::deque<int>, std::vector<double>>();
}
test<MinSequenceContainer<int>, MinSequenceContainer<double>>();
test<std::vector<int, min_allocator<int>>, std::vector<double, min_allocator<double>>>();
{
Expand All @@ -114,5 +123,14 @@ int main(int, char**) {
assert(x == 1);
}

return true;
}

int main(int, char**) {
test();
#if TEST_STD_VER >= 26
static_assert(test());
#endif

return 0;
}
Loading
Loading