[libcxx-commits] [libcxx] [libc++] LWG4272: For `rank == 0`, `layout_stride` is atypically convertible (PR #191507)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Apr 10 12:59:58 PDT 2026
https://github.com/eiytoq created https://github.com/llvm/llvm-project/pull/191507
This PR implements the LWG fix for non-padded layouts (`layout_left` / `layout_right`).
Partially fixes #171327. The padded-layout part is handled in #187873.
>From a22bfb58204d749585e37b5e6cf98a56900aad09 Mon Sep 17 00:00:00 2001
From: eiytoq <eiytoq at outlook.com>
Date: Sat, 11 Apr 2026 03:42:02 +0800
Subject: [PATCH] fix
---
libcxx/include/__mdspan/layout_left.h | 3 ++-
libcxx/include/__mdspan/layout_right.h | 3 ++-
libcxx/include/mdspan | 4 ++--
.../views/mdspan/layout_left/ctor.layout_stride.pass.cpp | 5 +++--
.../views/mdspan/layout_right/ctor.layout_stride.pass.cpp | 5 +++--
5 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/__mdspan/layout_left.h b/libcxx/include/__mdspan/layout_left.h
index 2f515afb6c860..e56a0e589ffb6 100644
--- a/libcxx/include/__mdspan/layout_left.h
+++ b/libcxx/include/__mdspan/layout_left.h
@@ -111,7 +111,8 @@ class layout_left::mapping {
template <class _OtherExtents>
requires(is_constructible_v<extents_type, _OtherExtents>)
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(extents_type::rank() > 0)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(
+ !(extents_type::rank() == 0 && is_convertible_v<_OtherExtents, extents_type>))
mapping(const layout_stride::mapping<_OtherExtents>& __other) noexcept
: __extents_(__other.extents()) {
if constexpr (extents_type::rank() > 0) {
diff --git a/libcxx/include/__mdspan/layout_right.h b/libcxx/include/__mdspan/layout_right.h
index ccfbd23e28ad7..231513866f684 100644
--- a/libcxx/include/__mdspan/layout_right.h
+++ b/libcxx/include/__mdspan/layout_right.h
@@ -111,7 +111,8 @@ class layout_right::mapping {
template <class _OtherExtents>
requires(is_constructible_v<extents_type, _OtherExtents>)
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(extents_type::rank() > 0)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(
+ !(extents_type::rank() == 0 && is_convertible_v<_OtherExtents, extents_type>))
mapping(const layout_stride::mapping<_OtherExtents>& __other) noexcept
: __extents_(__other.extents()) {
if constexpr (extents_type::rank() > 0) {
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index 32468a128dc9a..95c0c330b821e 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -113,7 +113,7 @@ namespace std {
constexpr explicit(!is_convertible_v<OtherExtents, extents_type>)
mapping(const layout_right::mapping<OtherExtents>&) noexcept;
template<class OtherExtents>
- constexpr explicit(extents_type::rank() > 0)
+ constexpr explicit(see below)
mapping(const layout_stride::mapping<OtherExtents>&) noexcept;
constexpr mapping& operator=(const mapping&) noexcept = default;
@@ -167,7 +167,7 @@ namespace std {
constexpr explicit(!is_convertible_v<OtherExtents, extents_type>)
mapping(const layout_left::mapping<OtherExtents>&) noexcept;
template<class OtherExtents>
- constexpr explicit(extents_type::rank() > 0)
+ constexpr explicit(see below)
mapping(const layout_stride::mapping<OtherExtents>&) noexcept;
constexpr mapping& operator=(const mapping&) noexcept = default;
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_stride.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_stride.pass.cpp
index cbd759eaf72cb..3bd5127177893 100644
--- a/libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_stride.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_stride.pass.cpp
@@ -11,7 +11,7 @@
// <mdspan>
// template<class OtherExtents>
-// constexpr explicit(extents_type::rank() > 0)
+// constexpr explicit(see below)
// mapping(const layout_stride::mapping<OtherExtents>& other);
//
// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
@@ -58,9 +58,10 @@ constexpr void test_conversion(FromE src_exts) {
template <class T1, class T2>
constexpr void test_conversion() {
constexpr size_t D = std::dynamic_extent;
+ constexpr bool ext_convertible = std::is_convertible_v<std::extents<T2>, std::extents<T1>>;
// clang-format off
- test_conversion<true, std::extents<T1>>(std::extents<T2>());
+ test_conversion<ext_convertible, std::extents<T1>>(std::extents<T2>());
test_conversion<false, std::extents<T1, D>>(std::extents<T2, D>(5));
test_conversion<false, std::extents<T1, 5>>(std::extents<T2, D>(5));
test_conversion<false, std::extents<T1, 5>>(std::extents<T2, 5>());
diff --git a/libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_stride.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_stride.pass.cpp
index 5886eba9d15ee..d7b76277b6310 100644
--- a/libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_stride.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_stride.pass.cpp
@@ -11,7 +11,7 @@
// <mdspan>
// template<class OtherExtents>
-// constexpr explicit(extents_type::rank() > 0)
+// constexpr explicit(see below)
// mapping(const layout_stride::mapping<OtherExtents>& other);
//
// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
@@ -58,9 +58,10 @@ constexpr void test_conversion(FromE src_exts) {
template <class T1, class T2>
constexpr void test_conversion() {
constexpr size_t D = std::dynamic_extent;
+ constexpr bool ext_convertible = std::is_convertible_v<std::extents<T2>, std::extents<T1>>;
// clang-format off
- test_conversion<true, std::extents<T1>>(std::extents<T2>());
+ test_conversion<ext_convertible, std::extents<T1>>(std::extents<T2>());
test_conversion<false, std::extents<T1, D>>(std::extents<T2, D>(5));
test_conversion<false, std::extents<T1, 5>>(std::extents<T2, D>(5));
test_conversion<false, std::extents<T1, 5>>(std::extents<T2, 5>());
More information about the libcxx-commits
mailing list