[libc++] LWG4266: layout_stride::mapping should treat empty mappings as exhaustive (#191629)

Fixes: #171325

---------

Co-authored-by: S. B. Tam <cpplearner@outlook.com>
This commit is contained in:
eiytoq 2026-05-15 15:46:44 +08:00 committed by GitHub
parent 54789b90c5
commit 4aeb7e386b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 35 additions and 40 deletions

View file

@ -165,7 +165,7 @@
"`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
"`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
"`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
"`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","","","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
"`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
"`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
"`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""

1 Issue # Issue Name Meeting Status First released version GitHub issue Notes
165 `LWG4257 <https://wg21.link/LWG4257>`__ Stream insertion for ``chrono::local_time`` should be constrained 2025-11 (Kona) `#171322 <https://github.com/llvm/llvm-project/issues/171322>`__
166 `LWG4260 <https://wg21.link/LWG4260>`__ Query objects must be default constructible 2025-11 (Kona) `#171323 <https://github.com/llvm/llvm-project/issues/171323>`__
167 `LWG4265 <https://wg21.link/LWG4265>`__ ``std::midpoint`` should not accept ``const bool`` 2025-11 (Kona) |Complete| 22 `#171324 <https://github.com/llvm/llvm-project/issues/171324>`__
168 `LWG4266 <https://wg21.link/LWG4266>`__ ``layout_stride::mapping`` should treat empty mappings as exhaustive 2025-11 (Kona) |Complete| 23 `#171325 <https://github.com/llvm/llvm-project/issues/171325>`__
169 `LWG4269 <https://wg21.link/LWG4269>`__ ``unique_copy`` passes arguments to its predicate backwards 2025-11 (Kona) `#171326 <https://github.com/llvm/llvm-project/issues/171326>`__
170 `LWG4272 <https://wg21.link/LWG4272>`__ For ``rank == 0``, ``layout_stride`` is atypically convertible 2025-11 (Kona) `#171327 <https://github.com/llvm/llvm-project/issues/171327>`__
171 `LWG4274 <https://wg21.link/LWG4274>`__ The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations 2025-11 (Kona) `#171328 <https://github.com/llvm/llvm-project/issues/171328>`__

View file

@ -296,38 +296,26 @@ public:
}
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() noexcept { return true; }
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept { return false; }
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept {
if constexpr (__rank_ == 0)
return true;
for (size_t __r = 0; __r < __rank_; ++__r)
if (extents_type::static_extent(__r) == 0)
return true;
return false;
}
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept { return true; }
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_unique() noexcept { return true; }
// The answer of this function is fairly complex in the case where one or more
// extents are zero.
// Technically it is meaningless to query is_exhaustive() in that case, but unfortunately
// the way the standard defines this function, we can't give a simple true or false then.
_LIBCPP_HIDE_FROM_ABI constexpr bool is_exhaustive() const noexcept {
if constexpr (__rank_ == 0)
if constexpr (is_always_exhaustive())
return true;
else {
index_type __span_size = required_span_size();
if (__span_size == static_cast<index_type>(0)) {
if constexpr (__rank_ == 1)
return __strides_[0] == 1;
else {
rank_type __r_largest = 0;
for (rank_type __r = 1; __r < __rank_; __r++)
if (__strides_[__r] > __strides_[__r_largest])
__r_largest = __r;
for (rank_type __r = 0; __r < __rank_; __r++)
if (__extents_.extent(__r) == 0 && __r != __r_largest)
return false;
return true;
}
} else {
return required_span_size() == [&]<size_t... _Pos>(index_sequence<_Pos...>) {
return (__extents_.extent(_Pos) * ... * static_cast<index_type>(1));
}(make_index_sequence<__rank_>());
}
}
index_type __span_size = required_span_size();
if (__span_size == static_cast<index_type>(0))
return true;
return __span_size == [&]<size_t... _Pos>(index_sequence<_Pos...>) {
return (__extents_.extent(_Pos) * ... * static_cast<index_type>(1));
}(make_index_sequence<__rank_>());
}
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_strided() noexcept { return true; }

View file

@ -237,7 +237,7 @@ namespace std {
constexpr index_type operator()(Indices...) const noexcept;
static constexpr bool is_always_unique() noexcept { return true; }
static constexpr bool is_always_exhaustive() noexcept { return false; }
static constexpr bool is_always_exhaustive() noexcept;
static constexpr bool is_always_strided() noexcept { return true; }
static constexpr bool is_unique() noexcept { return true; }

View file

@ -13,7 +13,7 @@
// constexpr bool is_exhaustive() const noexcept;
//
// Returns:
// - true if rank_ is 0.
// - true if rank_ or the size of the multidimensional index space m.extents() is 0.
// - Otherwise, true if there is a permutation P of the integers in the range [0, rank_) such that
// stride(p0) equals 1, and stride(pi) equals stride(pi_1) * extents().extent(pi_1) for i in the
// range [1, rank_), where pi is the ith element of P.
@ -35,13 +35,13 @@ test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()>
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
test_layout_mapping_stride(std::extents<int, 0>(), std::array<int, 1>{1}, true);
test_layout_mapping_stride(std::extents<unsigned, D>(0), std::array<unsigned, 1>{3}, false);
test_layout_mapping_stride(std::extents<unsigned, D>(0), std::array<unsigned, 1>{3}, true);
test_layout_mapping_stride(std::extents<int, 0, 3>(), std::array<int, 2>{6, 2}, true);
test_layout_mapping_stride(std::extents<int, D, D>(3, 0), std::array<int, 2>{6, 2}, false);
test_layout_mapping_stride(std::extents<int, D, D>(0, 0), std::array<int, 2>{6, 2}, false);
test_layout_mapping_stride(std::extents<int, D, D>(3, 0), std::array<int, 2>{6, 2}, true);
test_layout_mapping_stride(std::extents<int, D, D>(0, 0), std::array<int, 2>{6, 2}, true);
test_layout_mapping_stride(
std::extents<unsigned, D, D, D, D>(3, 3, 0, 3), std::array<unsigned, 4>{3, 1, 27, 9}, true);
test_layout_mapping_stride(std::extents<int, D, D, D, D>(0, 3, 3, 3), std::array<int, 4>{3, 1, 27, 9}, false);
test_layout_mapping_stride(std::extents<int, D, D, D, D>(0, 3, 3, 3), std::array<int, 4>{3, 1, 27, 9}, true);
return true;
}

View file

@ -16,11 +16,11 @@
//
// ...
// static constexpr bool is_always_unique() noexcept { return true; }
// static constexpr bool is_always_exhaustive() noexcept { return false; }
// static constexpr bool is_always_exhaustive() noexcept;
// static constexpr bool is_always_strided() noexcept { return true; }
//
// static constexpr bool is_unique() noexcept { return true; }
// static constexpr bool is_exhaustive() noexcept;
// constexpr bool is_exhaustive() noexcept;
// static constexpr bool is_strided() noexcept { return true; }
// ...
// };
@ -29,6 +29,12 @@
//
// layout_stride::mapping<E> is a trivially copyable type that models regular for each E.
//
// static constexpr bool is_always_exhaustive() noexcept;
//
// Returns:
// - true if rank_ is 0 or if there is a rank index r of extents() such that
// extents_type::static_extent(r) is 0, otherwise false.
//
// constexpr bool is_exhaustive() const noexcept;
//
// Returns:
@ -48,8 +54,8 @@
#include "test_macros.h"
template <class E>
constexpr void
test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()> strides, bool exhaustive) {
constexpr void test_layout_mapping_stride(
E ext, std::array<typename E::index_type, E::rank()> strides, bool exhaustive, bool always_exhaustive = false) {
using M = std::layout_stride::mapping<E>;
M m(ext, strides);
const M c_m = m;
@ -62,7 +68,7 @@ test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()>
assert(c_m.is_exhaustive() == exhaustive);
assert(M::is_strided() == true);
assert(M::is_always_unique() == true);
assert(M::is_always_exhaustive() == false);
assert(M::is_always_exhaustive() == always_exhaustive);
assert(M::is_always_strided() == true);
ASSERT_NOEXCEPT(m.strides());
@ -103,7 +109,8 @@ test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()>
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
test_layout_mapping_stride(std::extents<int>(), std::array<int, 0>{}, true);
test_layout_mapping_stride(std::extents<int>(), std::array<int, 0>{}, true, true);
test_layout_mapping_stride(std::extents<int, 2, 0>(), std::array<int, 2>{1, 2}, true, true);
test_layout_mapping_stride(std::extents<signed char, 4, 5>(), std::array<signed char, 2>{1, 4}, true);
test_layout_mapping_stride(std::extents<signed char, 4, 5>(), std::array<signed char, 2>{1, 5}, false);
test_layout_mapping_stride(std::extents<unsigned, D, 4>(7), std::array<unsigned, 2>{20, 2}, false);