From 1975b7791ce0c8f307718c75c858a2fef02603ab Mon Sep 17 00:00:00 2001 From: Christian Trott Date: Fri, 26 Jun 2026 15:34:05 -0600 Subject: [PATCH 1/3] Implement canonicalize slices for mdspan (P3663) Signed-off-by: Christian Trott --- .../experimental/__p0009_bits/config.hpp | 6 + .../experimental/__p0009_bits/extents.hpp | 31 +- .../experimental/__p0009_bits/layout_left.hpp | 40 +- .../__p0009_bits/layout_right.hpp | 26 +- .../__p0009_bits/layout_stride.hpp | 6 + .../experimental/__p0009_bits/utility.hpp | 69 +++ .../experimental/__p2389_bits/dims.hpp | 3 - .../__p2630_bits/constant_wrapper.hpp | 157 +++++++ .../__p2630_bits/integral_constant_like.hpp | 157 +++++++ .../__p2630_bits/strided_slice.hpp | 33 +- .../experimental/__p2630_bits/submdspan.hpp | 15 +- .../submdspan_canonicalize_slices.hpp | 397 ++++++++++++++++++ .../__p2630_bits/submdspan_extents.hpp | 133 +----- .../__p2630_bits/submdspan_mapping.hpp | 51 ++- .../__p2642_bits/layout_padded.hpp | 4 +- .../__p2642_bits/layout_padded_fwd.hpp | 2 - 16 files changed, 930 insertions(+), 200 deletions(-) create mode 100644 tpls/mdspan/include/experimental/__p2630_bits/constant_wrapper.hpp create mode 100644 tpls/mdspan/include/experimental/__p2630_bits/integral_constant_like.hpp create mode 100644 tpls/mdspan/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp diff --git a/tpls/mdspan/include/experimental/__p0009_bits/config.hpp b/tpls/mdspan/include/experimental/__p0009_bits/config.hpp index 9a00e42508d..785447a39ea 100644 --- a/tpls/mdspan/include/experimental/__p0009_bits/config.hpp +++ b/tpls/mdspan/include/experimental/__p0009_bits/config.hpp @@ -45,11 +45,13 @@ #else #define MDSPAN_CXX_STD_23 202100L #endif +#define MDSPAN_CXX_STD_26 202603L #define MDSPAN_HAS_CXX_14 (MDSPAN_IMPL_CPLUSPLUS >= MDSPAN_CXX_STD_14) #define MDSPAN_HAS_CXX_17 (MDSPAN_IMPL_CPLUSPLUS >= MDSPAN_CXX_STD_17) #define MDSPAN_HAS_CXX_20 (MDSPAN_IMPL_CPLUSPLUS >= MDSPAN_CXX_STD_20) #define MDSPAN_HAS_CXX_23 (MDSPAN_IMPL_CPLUSPLUS >= MDSPAN_CXX_STD_23) +#define MDSPAN_HAS_CXX_26 (MDSPAN_IMPL_CPLUSPLUS >= MDSPAN_CXX_STD_26) static_assert(MDSPAN_IMPL_CPLUSPLUS >= MDSPAN_CXX_STD_14, "mdspan requires C++14 or later."); @@ -303,3 +305,7 @@ static_assert(MDSPAN_IMPL_CPLUSPLUS >= MDSPAN_CXX_STD_14, "mdspan requires C++14 # define MDSPAN_IMPL_OP5(mds, a, b, c, d, e) mds(a,b,c,d,e) # define MDSPAN_IMPL_OP6(mds, a, b, c, d, e, f) mds(a,b,c,d,e,f) #endif + +#if ! defined(MDSPAN_IMPL_ENABLE_P3663) +# define MDSPAN_IMPL_ENABLE_P3663 1 +#endif diff --git a/tpls/mdspan/include/experimental/__p0009_bits/extents.hpp b/tpls/mdspan/include/experimental/__p0009_bits/extents.hpp index ab10ba32c18..9fb3981bf14 100644 --- a/tpls/mdspan/include/experimental/__p0009_bits/extents.hpp +++ b/tpls/mdspan/include/experimental/__p0009_bits/extents.hpp @@ -439,7 +439,12 @@ template class extents { sizeof...(OtherIndexTypes) == m_rank_dynamic))) MDSPAN_INLINE_FUNCTION constexpr explicit extents(OtherIndexTypes... dynvals) noexcept - : m_vals(static_cast(dynvals)...) {} + : m_vals(static_cast(dynvals)...) { +#if MDSPAN_HAS_CXX_17 + MDSPAN_IMPL_PRECONDITION( + detail::all_values_are_nonnegative_and_representable(dynvals...)); +#endif + } MDSPAN_TEMPLATE_REQUIRES( class OtherIndexType, size_t N, @@ -452,7 +457,13 @@ template class extents { MDSPAN_INLINE_FUNCTION MDSPAN_CONDITIONAL_EXPLICIT(N != m_rank_dynamic) constexpr extents(const std::array &exts) noexcept - : m_vals(std::move(exts)) {} + : m_vals(std::move(exts)) { +#if MDSPAN_HAS_CXX_17 + MDSPAN_IMPL_PRECONDITION( + detail::range_is_nonnegative_and_representable( + std::begin(exts), std::end(exts))); +#endif + } #ifdef __cpp_lib_span MDSPAN_TEMPLATE_REQUIRES( @@ -464,7 +475,11 @@ template class extents { MDSPAN_INLINE_FUNCTION MDSPAN_CONDITIONAL_EXPLICIT(N != m_rank_dynamic) constexpr extents(const std::span &exts) noexcept - : m_vals(std::move(exts)) {} + : m_vals(std::move(exts)) { + MDSPAN_IMPL_PRECONDITION( + detail::range_is_nonnegative_and_representable( + std::begin(exts), std::end(exts))); + } #endif private: @@ -536,10 +551,16 @@ template class extents { ...) || (std::numeric_limits::max() < std::numeric_limits::max())) - constexpr extents(const extents &other) noexcept + constexpr extents( + const extents &other) noexcept : m_vals(impl_construct_vals_from_extents( std::integral_constant(), - std::integral_constant(), other)) {} + std::integral_constant(), other)) { +#if MDSPAN_HAS_CXX_17 + MDSPAN_IMPL_PRECONDITION( + detail::extent_is_representable(other)); +#endif + } // Comparison operator template diff --git a/tpls/mdspan/include/experimental/__p0009_bits/layout_left.hpp b/tpls/mdspan/include/experimental/__p0009_bits/layout_left.hpp index e1198008bf3..5feb36f6c1a 100644 --- a/tpls/mdspan/include/experimental/__p0009_bits/layout_left.hpp +++ b/tpls/mdspan/include/experimental/__p0009_bits/layout_left.hpp @@ -118,30 +118,28 @@ class layout_left::mapping { /** * Converting constructor from `layout_left_padded::mapping`. * - * This overload participates in overload resolution only if Mapping is a layout_left_padded mapping and - * extents_type is constructible from Mapping::extents_type. + * This overload participates in overload resolution only if Mapping is a + * layout_left_padded mapping and extents_type is constructible from + * Mapping::extents_type. * - * \note There is currently a difference from p2642r2, where this function is specified as taking - * `layout_left_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. + * \note There is currently a difference from p2642r2, where this function + * is specified as taking `layout_left_padded< padding_value >::mapping< + * Extents>`. However, this makes `padding_value` non-deducible. */ MDSPAN_TEMPLATE_REQUIRES( - class Mapping, - /* requires */ ( - MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::is_layout_left_padded_mapping::value - && std::is_constructible_v - ) - ) - MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v)) - MDSPAN_INLINE_FUNCTION constexpr - mapping(const Mapping& other) noexcept - : m_extents(other.extents()) - { - MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: - check_padded_layout_converting_constructor_mandates< - extents_type, Mapping>(detail::with_rank{}); - MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: - check_padded_layout_converting_constructor_preconditions< - extents_type>(detail::with_rank{}, other); + class Mapping, + /* requires */ (detail::is_layout_left_padded_mapping::value + &&std::is_constructible_v< + extents_type, typename Mapping::extents_type>)) + MDSPAN_CONDITIONAL_EXPLICIT( + (!std::is_convertible_v)) + MDSPAN_INLINE_FUNCTION constexpr mapping(const Mapping &other) noexcept + : m_extents(other.extents()) { + detail::check_padded_layout_converting_constructor_mandates( + detail::with_rank{}); + detail::check_padded_layout_converting_constructor_preconditions< + extents_type>(detail::with_rank{}, other); } #endif diff --git a/tpls/mdspan/include/experimental/__p0009_bits/layout_right.hpp b/tpls/mdspan/include/experimental/__p0009_bits/layout_right.hpp index d95c180e983..f4487d217e6 100644 --- a/tpls/mdspan/include/experimental/__p0009_bits/layout_right.hpp +++ b/tpls/mdspan/include/experimental/__p0009_bits/layout_right.hpp @@ -128,20 +128,18 @@ class layout_right::mapping { #if MDSPAN_HAS_CXX_17 MDSPAN_TEMPLATE_REQUIRES( class Mapping, - /* requires */ ( - MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::is_layout_right_padded_mapping::value - && std::is_constructible_v)) - MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v)) - MDSPAN_INLINE_FUNCTION constexpr - mapping(const Mapping &other) noexcept - : m_extents(other.extents()) - { - MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: - check_padded_layout_converting_constructor_mandates< - extents_type, Mapping>(detail::with_rank{}); - MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: - check_padded_layout_converting_constructor_preconditions< - extents_type>(detail::with_rank{}, other); + /* requires */ (detail::is_layout_right_padded_mapping::value + &&std::is_constructible_v< + extents_type, typename Mapping::extents_type>)) + MDSPAN_CONDITIONAL_EXPLICIT( + (!std::is_convertible_v)) + MDSPAN_INLINE_FUNCTION constexpr mapping(const Mapping &other) noexcept + : m_extents(other.extents()) { + detail::check_padded_layout_converting_constructor_mandates( + detail::with_rank{}); + detail::check_padded_layout_converting_constructor_preconditions< + extents_type>(detail::with_rank{}, other); } #endif diff --git a/tpls/mdspan/include/experimental/__p0009_bits/layout_stride.hpp b/tpls/mdspan/include/experimental/__p0009_bits/layout_stride.hpp index c1a31513da6..0333547a972 100644 --- a/tpls/mdspan/include/experimental/__p0009_bits/layout_stride.hpp +++ b/tpls/mdspan/include/experimental/__p0009_bits/layout_stride.hpp @@ -21,6 +21,10 @@ #include "compressed_pair.hpp" #include "utility.hpp" +#if MDSPAN_HAS_CXX_17 +#include "../__p2642_bits/layout_padded_fwd.hpp" +#endif + #if !defined(MDSPAN_IMPL_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) # include "no_unique_address.hpp" #endif @@ -441,6 +445,8 @@ struct layout_stride { !(std::is_convertible::value && (detail::is_mapping_of || detail::is_mapping_of || + detail::is_layout_left_padded_mapping::value || // Don't need to guard for C++14 as this isn't compiled in < C++20 + detail::is_layout_right_padded_mapping::value || detail::is_mapping_of)) ) // needs two () due to comma MDSPAN_INLINE_FUNCTION MDSPAN_IMPL_CONSTEXPR_14 diff --git a/tpls/mdspan/include/experimental/__p0009_bits/utility.hpp b/tpls/mdspan/include/experimental/__p0009_bits/utility.hpp index 2d8a9384501..acadd6f739e 100644 --- a/tpls/mdspan/include/experimental/__p0009_bits/utility.hpp +++ b/tpls/mdspan/include/experimental/__p0009_bits/utility.hpp @@ -14,6 +14,14 @@ namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { +// Backport of std::remove_cvref / std::remove_cvref_t (C++20) +#if (__cplusplus >= 202002L) + using std::remove_cvref_t; +#else + template + using remove_cvref_t = std::remove_cv_t>; +#endif // __cplusplus >= 202002L + // type alias used for rank-based tag dispatch // // this is used to enable alternatives to constexpr if when building for C++14 @@ -203,6 +211,8 @@ MDSPAN_INLINE_FUNCTION constexpr bool cmp_greater_equal(T t, U u) noexcept { template MDSPAN_INLINE_FUNCTION constexpr bool in_range(T t) noexcept { + static_assert(std::is_integral_v && std::is_integral_v); + #if defined(MDSPAN_IMPL_HAS_CUDA) && defined(__NVCC__) && (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__ * 10 >= 1260) using cuda::std::numeric_limits; #else @@ -212,6 +222,65 @@ MDSPAN_INLINE_FUNCTION constexpr bool in_range(T t) noexcept { cmp_less_equal(t, numeric_limits::max()); } +template +MDSPAN_INLINE_FUNCTION constexpr bool is_nonnegative_and_representable(T t) noexcept { + // T might not be integral and thus invalid to pass to in_range + // Only check this if we can actually call in_range + if constexpr (std::is_integral_v) + { + if constexpr (std::is_signed_v) { + if (t < 0) + return false; + } + + return in_range(t); + } else + { + if constexpr (std::is_signed_v) { + if (static_cast(t) < 0) + return false; + } + + return true; + } +} + +template +MDSPAN_INLINE_FUNCTION constexpr bool +all_values_are_representable(Values... values) noexcept { + return ( in_range( values ) && ... ); +} + +template +MDSPAN_INLINE_FUNCTION constexpr bool +all_values_are_nonnegative_and_representable(Values... values) noexcept { + return ( is_nonnegative_and_representable( values ) && ... ); +} + +template +MDSPAN_INLINE_FUNCTION constexpr bool + range_is_nonnegative_and_representable(ContiguousIterator begin, ContiguousIterator end) noexcept { + for ( auto it = begin; it < end; ++it ) + { + if ( !is_nonnegative_and_representable( *it ) ) + return false; + } + + return true; +} + +template +MDSPAN_INLINE_FUNCTION constexpr bool +extent_is_representable(const Extents &exts) noexcept { + for ( std::size_t r = 0; r < Extents::rank(); ++r ) + { + if ( !is_nonnegative_and_representable( exts.extent(r) ) ) + return false; + } + + return true; +} + template MDSPAN_INLINE_FUNCTION constexpr bool check_mul_result_is_nonnegative_and_representable(T a, T b) { diff --git a/tpls/mdspan/include/experimental/__p2389_bits/dims.hpp b/tpls/mdspan/include/experimental/__p2389_bits/dims.hpp index 00045215c48..943facda7c9 100644 --- a/tpls/mdspan/include/experimental/__p2389_bits/dims.hpp +++ b/tpls/mdspan/include/experimental/__p2389_bits/dims.hpp @@ -16,13 +16,10 @@ #pragma once -// backward compatibility import into experimental namespace MDSPAN_IMPL_STANDARD_NAMESPACE { -namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { template< ::std::size_t Rank, class IndexType = std::size_t> using dims = :: MDSPAN_IMPL_STANDARD_NAMESPACE :: dextents; -} // namespace MDSPAN_IMPL_PROPOSED_NAMESPACE } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tpls/mdspan/include/experimental/__p2630_bits/constant_wrapper.hpp b/tpls/mdspan/include/experimental/__p2630_bits/constant_wrapper.hpp new file mode 100644 index 00000000000..e2ff6bfd75e --- /dev/null +++ b/tpls/mdspan/include/experimental/__p2630_bits/constant_wrapper.hpp @@ -0,0 +1,157 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#include "../__p0009_bits/utility.hpp" +#include + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +// ============================================================ +// constant_wrapper, cw, increment, is_constant_wrapper +// ============================================================ + +#if defined(__cpp_lib_constant_wrapper) + +using std::constant_wrapper; +using std::cw; + +template +constexpr bool is_constant_wrapper = false; + +template +constexpr bool is_constant_wrapper = is_constant_wrapper; + +template +constexpr bool is_constant_wrapper> = true; + +#else // back-port: constant_wrapper = detail::integral_constant + +template +using constant_wrapper = integral_constant; + +template + constexpr auto cw = constant_wrapper{}; + +template +constexpr bool is_constant_wrapper = false; + +template +constexpr bool is_constant_wrapper = is_constant_wrapper; + +// integral_constant is the underlying type of the back-port constant_wrapper +// (alias templates can't be used in partial specialization patterns) +template +constexpr bool is_constant_wrapper> = true; + +#endif // __cpp_lib_constant_wrapper + +// ============================================================ +// increment function for constant wrapper +// ============================================================ + +template +MDSPAN_INLINE_FUNCTION +constexpr auto +increment([[maybe_unused]] constant_wrapper x) { + using value_type = typename decltype(x)::value_type; + return cw< value_type(Value) + value_type(1) >; +} + + +// ============================================================ +// Generic divide / multiply (scalar fall-through) +// ============================================================ + +template +MDSPAN_INLINE_FUNCTION +constexpr auto divide(const T0 &v0, const T1 &v1) { + return IndexT(v0) / IndexT(v1); +} + +template +MDSPAN_INLINE_FUNCTION +constexpr auto multiply(const T0 &v0, const T1 &v1) { + return IndexT(v0) * IndexT(v1); +} + +// ============================================================ +// Compile-time-preserving overloads for std::integral_constant +// (used when strided_slice template parameters are std::integral_constant) +// ============================================================ + +template +MDSPAN_INLINE_FUNCTION +constexpr auto divide(const std::integral_constant &, + const std::integral_constant &) { + // Short-circuit division by zero + // (used for strided_slice with zero extent/stride) + return integral_constant(); +} + +template +MDSPAN_INLINE_FUNCTION +constexpr auto multiply(const std::integral_constant &, + const std::integral_constant &) { + return integral_constant(); +} + +// ============================================================ +// Compile-time-preserving overloads for constant_wrapper +// ============================================================ + +#if defined(__cpp_lib_constant_wrapper) + +// std::constant_wrapper takes a single NTTP +template +MDSPAN_INLINE_FUNCTION +constexpr auto divide(const constant_wrapper &, + const constant_wrapper &) { + constexpr IndexT result = + IndexT(v0) == IndexT(0) ? IndexT(0) : IndexT(v0) / IndexT(v1); + return cw; +} + +template +MDSPAN_INLINE_FUNCTION +constexpr auto multiply(const constant_wrapper &, + const constant_wrapper &) { + constexpr IndexT result = IndexT(v0) * IndexT(v1); + return cw; +} + +#else // back-port: constant_wrapper = integral_constant + +template +MDSPAN_INLINE_FUNCTION +constexpr auto divide(const constant_wrapper &, + const constant_wrapper &) { + return integral_constant(); +} + +template +MDSPAN_INLINE_FUNCTION +constexpr auto multiply(const constant_wrapper &, + const constant_wrapper &) { + return integral_constant(); +} + +#endif // __cpp_lib_constant_wrapper + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tpls/mdspan/include/experimental/__p2630_bits/integral_constant_like.hpp b/tpls/mdspan/include/experimental/__p2630_bits/integral_constant_like.hpp new file mode 100644 index 00000000000..ab69f66a17c --- /dev/null +++ b/tpls/mdspan/include/experimental/__p2630_bits/integral_constant_like.hpp @@ -0,0 +1,157 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#include "../__p0009_bits/utility.hpp" +#include +#if defined(__cpp_lib_concepts) +# include +#endif // __cpp_lib_concepts + +// ============================================================ +// equality_comparable back-port (used only by integral_constant_like) +// ============================================================ + +#if defined(__cpp_lib_concepts) + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + namespace detail { + template + struct is_equality_comparable : std::bool_constant> {}; + + template + struct is_equality_comparable_with : std::bool_constant> {}; + } // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#else + +#include + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + + template + struct is_equality_comparable : std::false_type {}; + + template + struct is_equality_comparable< + T, + std::void_t< + decltype(std::declval() == std::declval()), + decltype(std::declval() != std::declval()) + > + > : std::bool_constant< + std::is_convertible_v< + decltype(std::declval() == std::declval()), + bool + > && + std::is_convertible_v< + decltype(std::declval() != std::declval()), + bool + > + > {}; + + template + struct is_equality_comparable_with : std::false_type {}; + + template + struct is_equality_comparable_with< + T, U, + std::void_t< + decltype(std::declval() == std::declval()), + decltype(std::declval() != std::declval()), + decltype(std::declval() == std::declval()), + decltype(std::declval() != std::declval()) + > + > : std::bool_constant< + is_equality_comparable::value && + is_equality_comparable::value && + std::is_convertible_v< + decltype(std::declval() == std::declval()), + bool + > && + std::is_convertible_v< + decltype(std::declval() != std::declval()), + bool + > && + std::is_convertible_v< + decltype(std::declval() == std::declval()), + bool + > && + std::is_convertible_v< + decltype(std::declval() != std::declval()), + bool + > + > {}; + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#endif // defined(__cpp_lib_concepts) + +// ============================================================ +// integral_constant_like concept / trait +// ============================================================ + +#if defined(__cpp_lib_concepts) + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + namespace detail { + + template + concept integral_constant_like = + std::is_integral_v> && + !std::is_same_v> && + std::convertible_to && + std::equality_comparable_with && + std::bool_constant::value && + std::bool_constant(T()) == T::value>::value; + + template + constexpr bool is_integral_constant_like_v = integral_constant_like; + + } // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#else + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + namespace detail { + + template + struct is_integral_constant_like_impl : std::false_type {}; + + template + struct is_integral_constant_like_impl> : + std::bool_constant< + std::is_integral_v> && + ! std::is_same_v> && + std::is_convertible_v && + is_equality_comparable_with::value && + std::bool_constant::value && + std::bool_constant(T()) == T::value>::value + > + {}; + + template + constexpr bool is_integral_constant_like_v = is_integral_constant_like_impl::value; + + } // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#endif // __cpp_lib_concepts diff --git a/tpls/mdspan/include/experimental/__p2630_bits/strided_slice.hpp b/tpls/mdspan/include/experimental/__p2630_bits/strided_slice.hpp index 7f4a018801f..f1fa6d1ae5d 100644 --- a/tpls/mdspan/include/experimental/__p2630_bits/strided_slice.hpp +++ b/tpls/mdspan/include/experimental/__p2630_bits/strided_slice.hpp @@ -17,17 +17,36 @@ #pragma once +#include "../__p0009_bits/config.hpp" +#include "constant_wrapper.hpp" +#include "integral_constant_like.hpp" + #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { + template + struct is_signed_or_unsigned_integral_constant_like : std::false_type {}; + template - struct mdspan_is_integral_constant: std::false_type {}; + struct is_signed_or_unsigned_integral_constant_like< + T, std::enable_if_t> + > : std::bool_constant< + std::is_integral_v> && + ! std::is_same_v> + > + {}; - template - struct mdspan_is_integral_constant>: std::true_type {}; -} + template + constexpr bool is_signed_or_unsigned_integral_constant_like_v = + is_signed_or_unsigned_integral_constant_like::value; + + template + constexpr bool mdspan_is_index_like_v = + (std::is_integral_v && ! std::is_same_v) || + is_signed_or_unsigned_integral_constant_like_v; +} // namespace detail // Slice Specifier allowing for strides and compile time extent template @@ -40,9 +59,9 @@ struct strided_slice { MDSPAN_IMPL_NO_UNIQUE_ADDRESS ExtentType extent{}; MDSPAN_IMPL_NO_UNIQUE_ADDRESS StrideType stride{}; - static_assert(std::is_integral_v || detail::mdspan_is_integral_constant::value); - static_assert(std::is_integral_v || detail::mdspan_is_integral_constant::value); - static_assert(std::is_integral_v || detail::mdspan_is_integral_constant::value); + static_assert(detail::mdspan_is_index_like_v); + static_assert(detail::mdspan_is_index_like_v); + static_assert(detail::mdspan_is_index_like_v); }; } // MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tpls/mdspan/include/experimental/__p2630_bits/submdspan.hpp b/tpls/mdspan/include/experimental/__p2630_bits/submdspan.hpp index abddd0b59df..3f78f8d9377 100644 --- a/tpls/mdspan/include/experimental/__p2630_bits/submdspan.hpp +++ b/tpls/mdspan/include/experimental/__p2630_bits/submdspan.hpp @@ -17,6 +17,7 @@ #pragma once #include "submdspan_extents.hpp" +#include "submdspan_canonicalize_slices.hpp" #include "submdspan_mapping.hpp" namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -26,7 +27,19 @@ MDSPAN_INLINE_FUNCTION constexpr auto submdspan(const mdspan &src, SliceSpecifiers... slices) { - const auto sub_submdspan_mapping_result = submdspan_mapping(src.mapping(), slices...); + + // Avoid instantiating expensive slice mandate check here for known layouts + // These layouts will check again anyway + if constexpr ( + !(std::is_same_v || + std::is_same_v || + std::is_same_v || + detail::is_layout_left_padded::value || + detail::is_layout_right_padded::value + )) detail::check_submdspan_slice_mandates(std::make_index_sequence(), slices...); + + const auto sub_submdspan_mapping_result = submdspan_mapping(src.mapping(), + detail::canonical_slice(slices)...); // NVCC has a problem with the deduction so lets figure out the type using sub_mapping_t = std::remove_cv_t; using sub_extents_t = typename sub_mapping_t::extents_type; diff --git a/tpls/mdspan/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp b/tpls/mdspan/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp new file mode 100644 index 00000000000..d9de78c6dac --- /dev/null +++ b/tpls/mdspan/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp @@ -0,0 +1,397 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#include "submdspan_extents.hpp" +#include + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +#if MDSPAN_HAS_CXX_17 + +namespace detail { + +// ============================================================ +// de_ice: extract the value of an integral-constant-like type +// ============================================================ + +MDSPAN_TEMPLATE_REQUIRES( + class T, + /* requires */ (std::is_integral_v>) +) +MDSPAN_INLINE_FUNCTION +constexpr T de_ice(T val) { + return val; +} + +MDSPAN_TEMPLATE_REQUIRES( + class T, + /* requires */ (is_integral_constant_like_v>) +) +MDSPAN_INLINE_FUNCTION +constexpr decltype(T::value) de_ice([[maybe_unused]] T) { + return T::value; +} + +// ============================================================ +// index_cast: cast to IndexType, preserving integral-constant nature +// ============================================================ + +MDSPAN_TEMPLATE_REQUIRES( + class IndexType, + class OtherIndexType, + /* requires */ ( + std::is_signed_v> || + std::is_unsigned_v> + ) +) +MDSPAN_INLINE_FUNCTION +constexpr auto index_cast(OtherIndexType&& i) noexcept { + return i; +} + +MDSPAN_TEMPLATE_REQUIRES( + class IndexType, + class OtherIndexType, + /* requires */ ( + ! std::is_signed_v> && + ! std::is_unsigned_v> + ) +) +MDSPAN_INLINE_FUNCTION +constexpr auto index_cast(OtherIndexType&& i) noexcept { + return static_cast(std::forward(i)); +} + +// ============================================================ +// canonical_index: canonicalize a value to IndexType, +// preserving integral-constant nature when possible +// ============================================================ + +MDSPAN_TEMPLATE_REQUIRES( + class IndexType, + class S, + /* requires */ (std::is_convertible_v) +) +MDSPAN_INLINE_FUNCTION +constexpr auto canonical_index([[maybe_unused]] S s) { + // TODO: might move to public semi/public only to get error earlier, and + // don't duplicate check + // TODO: add mandate for integral-constant-like representable as IndexType + // TODO: add precondition check that index-cast is representable as IndexType + static_assert(std::is_signed_v || std::is_unsigned_v); + if constexpr (is_integral_constant_like_v) { + return cw(index_cast(S::value))>; + } + else { + return static_cast(index_cast(std::move(s))); + } +} + +// ============================================================ +// subtract_ice: subtract two values, preserving integral-constant +// nature when both inputs are integral-constant-like +// ============================================================ + +template +MDSPAN_INLINE_FUNCTION +constexpr auto subtract_ice([[maybe_unused]] X x, [[maybe_unused]] Y y) { + if constexpr ( + is_integral_constant_like_v> && + is_integral_constant_like_v>) + { + return cw(Y::value) - canonical_index(X::value))>; + } + else { + return canonical_index(y) - canonical_index(x); + } +} + +// ============================================================ +// check_static_bounds: compile-time bounds check for a slice +// +// Returns false if the slice is statically out of bounds. +// +// This function is called only in static_assert contexts. +// ============================================================ + +template +constexpr bool check_static_bounds() +{ + if constexpr (std::is_convertible_v) { + return true; + } + else if constexpr (std::is_convertible_v) { + if constexpr (is_integral_constant_like_v) { + if constexpr (de_ice(S_k{}) < 0) { + return false; + } + else if constexpr ( + Exts_k != dynamic_extent && + Exts_k <= static_cast(de_ice(S_k{}))) + { + return false; + } + else { return true; } + } else { + return true; + } + } + else if constexpr (is_strided_slice::value) { + using offset_type = typename S_k::offset_type; + + if constexpr (is_integral_constant_like_v) { + if constexpr (de_ice(offset_type{}) < 0) { + return false; + } + else if constexpr ( + Exts_k != dynamic_extent && + Exts_k < static_cast(de_ice(offset_type{}))) + { + return false; + } + else if constexpr (is_integral_constant_like_v) { + using extent_type = typename S_k::extent_type; + + if constexpr (de_ice(offset_type{}) + de_ice(extent_type{}) < 0) { + return false; + } + else if constexpr ( + Exts_k != dynamic_extent && + Exts_k < + static_cast(de_ice(offset_type{}) + de_ice(extent_type{}))) + { + return false; + } + else if constexpr ( + Exts_k != dynamic_extent && + 0 <= de_ice(offset_type{}) && + de_ice(offset_type{}) <= + de_ice(offset_type{}) + de_ice(extent_type{}) && + static_cast( + de_ice(offset_type{}) + de_ice(extent_type{})) <= Exts_k) + { + return true; + } + else { + return true; + } + } + else { + return true; + } + } + else { + return true; + } + } else { + // General pair-like case: attempt to get the first and second elements. + // If S_k cannot be structured-bound into two elements, this is ill-formed, + // which implements the Mandates clause. + // Doing this via these lambdas since we can do the declval only in a + // non-evaluated context + auto get_first = [] (S_k s_k) { + auto [s_k0, _x] = s_k; + return s_k0; + }; + auto get_second = [] (S_k s_k) { + auto [_x, s_k1] = s_k; + return s_k1; + }; + using S_k0 = decltype(get_first(std::declval())); + using S_k1 = decltype(get_second(std::declval())); + + if constexpr (is_integral_constant_like_v) { + if constexpr (de_ice(S_k0{}) < 0) { + return false; + } + else if constexpr ( + Exts_k != dynamic_extent && + Exts_k < static_cast(de_ice(S_k0{}))) + { + return false; + } + else if constexpr (is_integral_constant_like_v) { + if constexpr (de_ice(S_k1{}) < de_ice(S_k0{})) { + return false; + } + else if constexpr ( + Exts_k != dynamic_extent && + Exts_k < static_cast(de_ice(S_k1{}))) + { + return false; + } + else if constexpr ( + Exts_k != dynamic_extent && + 0 <= de_ice(S_k0{}) && + de_ice(S_k0{}) <= de_ice(S_k1{}) && + static_cast(de_ice(S_k1{})) <= Exts_k) + { + return true; + } + else { + return true; + } + } + else { + return true; + } + } + else { + return true; + } + } +} + +// ============================================================ +// check_submdspan_slice_mandate: mandate check for the k-th slice +// +// Contains only static_asserts; no actual computation. +// Separated from canonical_slice so that +// mandate checking and canonicalization are distinct concerns. +// ============================================================ + +template +MDSPAN_INLINE_FUNCTION +constexpr bool check_submdspan_slice_mandate( + [[maybe_unused]] const Slice&) +{ + static_assert(check_static_bounds()); + return true; +} + +template +MDSPAN_INLINE_FUNCTION +constexpr bool check_submdspan_slice_mandates( + const std::index_sequence& , + [[maybe_unused]] const Slices& ... slices) +{ + return (check_submdspan_slice_mandate(slices) && ... && true); +} + +// ============================================================ +// canonical_slice: canonicalize a single slice +// +// This function performs ONLY the conversion to canonical form. +// Mandate checking (static_asserts) is NOT done here; it is +// done separately by check_submdspan_slice_mandates. +// +// Templated only on IndexType (the extents index type) and Slice. +// Neither k nor the extents are needed for the actual conversion. +// ============================================================ + +template +MDSPAN_INLINE_FUNCTION +constexpr auto canonical_slice([[maybe_unused]] Slice s) +{ + if constexpr (std::is_convertible_v) { + return full_extent; // canonical full-extent slice + } + else if constexpr (std::is_convertible_v) { + return canonical_index(std::move(s)); // canonical integer index + } + else if constexpr (is_strided_slice::value) { + // Canonicalize each component of the strided_slice + auto offset = canonical_index(s.offset); + auto extent = canonical_index(s.extent); + auto stride = canonical_index(s.stride); + return strided_slice{ + /* .offset = */ offset, + /* .extent = */ extent, + /* .stride = */ stride + }; + } else { + // General pair-like case: structured binding into [first, last) + auto [s_k0, s_k1] = std::move(s); + using S_k0 = decltype(s_k0); + using S_k1 = decltype(s_k1); + static_assert(std::is_convertible_v); + static_assert(std::is_convertible_v); + + auto offset = canonical_index(s_k0); + auto extent = subtract_ice(s_k0, s_k1); + auto stride = cw; + return strided_slice{ + /* .offset = */ offset, + /* .extent = */ extent, + /* .stride = */ stride + }; + } +} + +// ============================================================ +// canonical_slices_impl: implementation helper +// +// First performs mandate checks (static_asserts), then +// returns a detail::tuple of canonical slices. +// Using detail::tuple instead of std::tuple ensures device +// code compatibility (e.g., CUDA). +// ============================================================ + +MDSPAN_TEMPLATE_REQUIRES( + size_t... Inds, + class Extents, + class... Slices, + /* requires */ (sizeof...(Slices) == Extents::rank()) +) +MDSPAN_INLINE_FUNCTION +constexpr auto canonical_slices_impl( + std::index_sequence, + const Extents&, + Slices... slices) +{ + // Mandate checks (static_asserts only, no computation). + // Separated from canonicalization for clarity. + (void)(check_submdspan_slice_mandate(slices) && ... && true); + + // Actual canonicalization: returns detail::tuple for device compatibility. + return detail::tuple{ + canonical_slice(slices)... + }; +} + +} // namespace detail + +// ============================================================ +// canonicalize_slices: public API +// +// Given an extents object and a pack of slice specifiers, +// returns a detail::tuple of canonical slice specifiers. +// Each canonical slice is one of: +// - full_extent_t (for full-extent slices) +// - IndexType (for integer index slices) +// - strided_slice<...> (for range and strided-range slices) +// ============================================================ + +MDSPAN_TEMPLATE_REQUIRES( + class IndexType, + size_t... Extents, + class... Slices, + /* requires */ (sizeof...(Slices) == sizeof...(Extents)) +) +MDSPAN_INLINE_FUNCTION +constexpr auto canonical_slices( + const extents& exts, + Slices... slices) +{ + return detail::canonical_slices_impl( + std::make_index_sequence(), exts, slices...); +} + +#endif // MDSPAN_HAS_CXX_17 + +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tpls/mdspan/include/experimental/__p2630_bits/submdspan_extents.hpp b/tpls/mdspan/include/experimental/__p2630_bits/submdspan_extents.hpp index 4fe5dc6e29a..abada9f09b8 100644 --- a/tpls/mdspan/include/experimental/__p2630_bits/submdspan_extents.hpp +++ b/tpls/mdspan/include/experimental/__p2630_bits/submdspan_extents.hpp @@ -18,6 +18,7 @@ #include +#include "constant_wrapper.hpp" #include "strided_slice.hpp" #include "../__p0009_bits/utility.hpp" @@ -54,37 +55,6 @@ template struct is_strided_slice< strided_slice> : std::true_type {}; -// Helper for identifying valid pair like things -template struct index_pair_like : std::false_type {}; - -template -struct index_pair_like, IndexType> { - static constexpr bool value = std::is_convertible_v && - std::is_convertible_v; -}; - -template -struct index_pair_like, IndexType> { - static constexpr bool value = std::is_convertible_v && - std::is_convertible_v; -}; - -template -struct index_pair_like, IndexType> { - static constexpr bool value = std::is_convertible_v && - std::is_convertible_v; -}; - -template -struct index_pair_like, IndexType> { - static constexpr bool value = std::is_convertible_v; -}; - -template -struct index_pair_like, IndexType> { - static constexpr bool value = std::is_convertible_v; -}; - // first_of(slice): getting begin of slice specifier range MDSPAN_TEMPLATE_REQUIRES( class Integral, @@ -97,8 +67,8 @@ constexpr Integral first_of(const Integral &i) { template MDSPAN_INLINE_FUNCTION -constexpr Integral first_of(const std::integral_constant&) { - return integral_constant(); +constexpr auto first_of(const constant_wrapper&) { + return constant_wrapper(); } MDSPAN_INLINE_FUNCTION @@ -107,38 +77,6 @@ first_of(const ::MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent_t &) { return integral_constant(); } -MDSPAN_TEMPLATE_REQUIRES( - class Slice, - /* requires */(index_pair_like::value) -) -MDSPAN_INLINE_FUNCTION -constexpr auto first_of(const Slice &i) { - return get<0>(i); -} - -MDSPAN_TEMPLATE_REQUIRES( - class IdxT1, class IdxT2, - /* requires */ (index_pair_like, size_t>::value) - ) -constexpr auto first_of(const std::tuple& i) { - return get<0>(i); -} - -MDSPAN_TEMPLATE_REQUIRES( - class IdxT1, class IdxT2, - /* requires */ (index_pair_like, size_t>::value) - ) -MDSPAN_INLINE_FUNCTION -constexpr auto first_of(const std::pair& i) { - return i.first; -} - -template -MDSPAN_INLINE_FUNCTION -constexpr auto first_of(const std::complex &i) { - return i.real(); -} - template MDSPAN_INLINE_FUNCTION constexpr OffsetType @@ -160,39 +98,6 @@ constexpr Integral return i; } -MDSPAN_TEMPLATE_REQUIRES( - size_t k, class Extents, class Slice, - /* requires */(index_pair_like::value) -) -MDSPAN_INLINE_FUNCTION -constexpr auto last_of(std::integral_constant, const Extents &, - const Slice &i) { - return get<1>(i); -} - -MDSPAN_TEMPLATE_REQUIRES( - size_t k, class Extents, class IdxT1, class IdxT2, - /* requires */ (index_pair_like, size_t>::value) - ) -constexpr auto last_of(std::integral_constant, const Extents &, const std::tuple& i) { - return get<1>(i); -} - -MDSPAN_TEMPLATE_REQUIRES( - size_t k, class Extents, class IdxT1, class IdxT2, - /* requires */ (index_pair_like, size_t>::value) - ) -MDSPAN_INLINE_FUNCTION -constexpr auto last_of(std::integral_constant, const Extents &, const std::pair& i) { - return i.second; -} - -template -MDSPAN_INLINE_FUNCTION -constexpr auto last_of(std::integral_constant, const Extents &, const std::complex &i) { - return i.imag(); -} - // Suppress spurious warning with NVCC about no return statement. // This is a known issue in NVCC and NVC++ // Depending on the CUDA and GCC version we need both the builtin @@ -219,7 +124,7 @@ constexpr auto last_of(std::integral_constant, const Extents &ext, if constexpr (Extents::static_extent(k) == dynamic_extent) { return ext.extent(k); } else { - return integral_constant(); + return constant_wrapper(); } #if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) // Even with CUDA_ARCH protection this thing warns about calling host function @@ -261,36 +166,6 @@ stride_of(const strided_slice &r) { return r.stride; } -// divide which can deal with integral constant preservation -template -MDSPAN_INLINE_FUNCTION -constexpr auto divide(const T0 &v0, const T1 &v1) { - return IndexT(v0) / IndexT(v1); -} - -template -MDSPAN_INLINE_FUNCTION -constexpr auto divide(const std::integral_constant &, - const std::integral_constant &) { - // cutting short division by zero - // this is used for strided_slice with zero extent/stride - return integral_constant(); -} - -// multiply which can deal with integral constant preservation -template -MDSPAN_INLINE_FUNCTION -constexpr auto multiply(const T0 &v0, const T1 &v1) { - return IndexT(v0) * IndexT(v1); -} - -template -MDSPAN_INLINE_FUNCTION -constexpr auto multiply(const std::integral_constant &, - const std::integral_constant &) { - return integral_constant(); -} - // compute new static extent from range, preserving static knowledge template struct StaticExtentFromRange { constexpr static size_t value = dynamic_extent; diff --git a/tpls/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp b/tpls/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp index 3a87dff9c2b..be7fcfb231c 100644 --- a/tpls/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp +++ b/tpls/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp @@ -102,14 +102,19 @@ MDSPAN_INLINE_FUNCTION constexpr auto construct_sub_strides( } template -struct is_range_slice { - constexpr static bool value = - std::is_same_v || - index_pair_like::value; -}; +constexpr bool is_range_slice_v = false; -template -constexpr bool is_range_slice_v = is_range_slice::value; +template +constexpr bool is_range_slice_v = true; + +template +constexpr bool is_range_slice_v< + strided_slice< + OffsetType, + ExtentType, + constant_wrapper>, + IndexType + > = (constant_wrapper::value == IndexType(1)); template struct is_index_slice { @@ -209,6 +214,9 @@ MDSPAN_INLINE_FUNCTION constexpr auto layout_left::mapping::submdspan_mapping_impl( SliceSpecifiers... slices) const { + // Implements mandate check + detail::check_submdspan_slice_mandates(std::make_index_sequence(), slices...); + // compute sub extents using src_ext_t = Extents; auto dst_ext = submdspan_extents(extents(), slices...); @@ -235,7 +243,7 @@ layout_left::mapping::submdspan_mapping_impl( offset}; } else if constexpr (deduce_layout::layout_left_padded_value()) { constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_left(std::make_index_sequence()); - using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::template mapping; + using dst_mapping_t = typename layout_left_padded::template mapping; return submdspan_mapping_result{ dst_mapping_t(dst_ext, stride(1 + deduce_layout::gap_len)), offset}; } else { @@ -269,16 +277,19 @@ template template template MDSPAN_INLINE_FUNCTION constexpr auto -MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::mapping::submdspan_mapping_impl( +layout_left_padded::mapping::submdspan_mapping_impl( SliceSpecifiers... slices) const { + // Implements mandate check + detail::check_submdspan_slice_mandates(std::make_index_sequence(), slices...); + // compute sub extents using src_ext_t = Extents; auto dst_ext = submdspan_extents(extents(), slices...); using dst_ext_t = decltype(dst_ext); if constexpr (Extents::rank() == 0) { // rank-0 case - using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::template mapping; + using dst_mapping_t = typename layout_left_padded::template mapping; return submdspan_mapping_result{*this, 0}; } else { const bool out_of_bounds = @@ -314,7 +325,7 @@ MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::mapping{dst_mapping_t{dst_ext}, offset}; } else if constexpr (deduce_layout::layout_left_padded_value()) { // can keep layout_left_padded constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_left(std::make_index_sequence()); - using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::template mapping; + using dst_mapping_t = typename layout_left_padded::template mapping; return submdspan_mapping_result{ dst_mapping_t(dst_ext, stride(1 + deduce_layout::gap_len)), offset}; } else { // layout_stride @@ -437,6 +448,9 @@ MDSPAN_INLINE_FUNCTION constexpr auto layout_right::mapping::submdspan_mapping_impl( SliceSpecifiers... slices) const { + // Implements mandate check + detail::check_submdspan_slice_mandates(std::make_index_sequence(), slices...); + // compute sub extents using src_ext_t = Extents; auto dst_ext = submdspan_extents(extents(), slices...); @@ -463,7 +477,7 @@ layout_right::mapping::submdspan_mapping_impl( offset}; } else if constexpr (deduce_layout::layout_right_padded_value()) { constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_left(std::make_index_sequence()); - using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::template mapping; + using dst_mapping_t = typename layout_right_padded::template mapping; return submdspan_mapping_result{ dst_mapping_t(dst_ext, stride(src_ext_t::rank() - 2 - deduce_layout::gap_len)), @@ -499,16 +513,19 @@ template template template MDSPAN_INLINE_FUNCTION constexpr auto -MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::mapping::submdspan_mapping_impl( +layout_right_padded::mapping::submdspan_mapping_impl( SliceSpecifiers... slices) const { + // Implements mandate check + detail::check_submdspan_slice_mandates(std::make_index_sequence(), slices...); + // compute sub extents using src_ext_t = Extents; auto dst_ext = submdspan_extents(extents(), slices...); using dst_ext_t = decltype(dst_ext); if constexpr (Extents::rank() == 0) { // rank-0 case - using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::template mapping; + using dst_mapping_t = typename layout_right_padded::template mapping; return submdspan_mapping_result{*this, 0}; } else { // Figure out if any slice's lower bound equals the corresponding extent. @@ -536,7 +553,7 @@ MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::mapping{dst_mapping_t{dst_ext}, offset}; } else if constexpr (deduce_layout::layout_right_padded_value()) { // can keep layout_right_padded constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_right(std::make_index_sequence()); - using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::template mapping; + using dst_mapping_t = typename layout_right_padded::template mapping; return submdspan_mapping_result{ dst_mapping_t(dst_ext, stride(Extents::rank() - 2 - deduce_layout::gap_len)), offset}; } else { // layout_stride @@ -577,6 +594,10 @@ template MDSPAN_INLINE_FUNCTION constexpr auto layout_stride::mapping::submdspan_mapping_impl( SliceSpecifiers... slices) const { + + // Implements mandate check + detail::check_submdspan_slice_mandates(std::make_index_sequence(), slices...); + auto dst_ext = submdspan_extents(extents(), slices...); using dst_ext_t = decltype(dst_ext); auto inv_map = detail::inv_map_rank(std::integral_constant(), diff --git a/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp b/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp index d91313b2847..e57d30051a3 100644 --- a/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp +++ b/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp @@ -26,7 +26,6 @@ #include "../__p0009_bits/utility.hpp" namespace MDSPAN_IMPL_STANDARD_NAMESPACE { -namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { namespace detail { template MDSPAN_INLINE_FUNCTION @@ -81,7 +80,7 @@ struct static_array_type_for_padded_extent { using extents_type = Extents; using type = ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::maybe_static_array< index_type, size_t, dynamic_extent, - ::MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: + ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail:: get_actual_static_padding_value()>; }; @@ -1086,4 +1085,3 @@ class layout_right_padded::mapping { } }; } -} diff --git a/tpls/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp b/tpls/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp index 481b1d5c7a0..38e783f9984 100644 --- a/tpls/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp +++ b/tpls/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp @@ -20,7 +20,6 @@ #include "../__p0009_bits/utility.hpp" namespace MDSPAN_IMPL_STANDARD_NAMESPACE { -namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { template struct layout_left_padded { @@ -134,4 +133,3 @@ constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_I } } -} From a062b379b0cfa972b4a660955a797f572e8f7931 Mon Sep 17 00:00:00 2001 From: Christian Trott Date: Fri, 26 Jun 2026 15:44:38 -0600 Subject: [PATCH 2/3] Reimport layout_left/right_padded to experimental Signed-off-by: Christian Trott --- core/src/View/MDSpan/Kokkos_MDSpan_Layout.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/View/MDSpan/Kokkos_MDSpan_Layout.hpp b/core/src/View/MDSpan/Kokkos_MDSpan_Layout.hpp index 85656b8008e..0e5f56872ad 100644 --- a/core/src/View/MDSpan/Kokkos_MDSpan_Layout.hpp +++ b/core/src/View/MDSpan/Kokkos_MDSpan_Layout.hpp @@ -20,6 +20,13 @@ static_assert(false, // mdspan layout is that the array layouts can have state, but don't have the // nested mapping. This file provides interoperability helpers. +namespace Kokkos::Experimental { +template +using layout_left_padded = Kokkos::layout_left_padded; +template +using layout_right_padded = Kokkos::layout_right_padded; +} // namespace Kokkos::Experimental + namespace Kokkos::Impl { // We do have implementation detail versions of these in our mdspan impl // However they are not part of the public standard interface From 3790bf41231954355fcb72f232c1afcc1c4f84c5 Mon Sep 17 00:00:00 2001 From: Christian Trott Date: Fri, 26 Jun 2026 15:59:04 -0600 Subject: [PATCH 3/3] Fix padded layouts required_span_size Signed-off-by: Christian Trott --- tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp b/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp index e57d30051a3..dd02291f667 100644 --- a/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp +++ b/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp @@ -571,6 +571,7 @@ class layout_left_padded::mapping { } else if constexpr (extents_type::rank() == 1) { return exts.extent(0); } else { + if (exts.extent(0) == 0) return 0; index_type value = padded_stride.value(0); for (rank_type r = 1; r < extents_type::rank(); ++r) { value *= exts.extent(r); @@ -964,6 +965,7 @@ class layout_right_padded::mapping { } else if constexpr (extents_type::rank() == 1) { return exts.extent(0); } else { + if (exts.extent(extents_type::rank()-1) == 0) return 0; index_type value = padded_stride.value(0); for (rank_type r = 0; r < extent_to_pad_idx; ++r) { value *= exts.extent(r);