diff --git a/include/experimental/__p0009_bits/extents.hpp b/include/experimental/__p0009_bits/extents.hpp index ab10ba32..9fb3981b 100644 --- a/include/experimental/__p0009_bits/extents.hpp +++ b/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/include/experimental/__p0009_bits/utility.hpp b/include/experimental/__p0009_bits/utility.hpp index 2d8a9384..33e5d0f1 100644 --- a/include/experimental/__p0009_bits/utility.hpp +++ b/include/experimental/__p0009_bits/utility.hpp @@ -203,6 +203,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 +214,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/tests/CMakeLists.txt b/tests/CMakeLists.txt index b038ebcc..97c7fdb9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -78,7 +78,7 @@ mdspan_add_test(test_macros ENABLE_PRECONDITIONS) mdspan_add_test(test_layout_preconditions ENABLE_PRECONDITIONS) mdspan_add_test(test_dims) -mdspan_add_test(test_extents) +mdspan_add_test(test_extents ENABLE_PRECONDITIONS) mdspan_add_test(test_mdspan_at) mdspan_add_test(test_mdspan_ctors) mdspan_add_test(test_mdspan_swap) diff --git a/tests/test_extents.cpp b/tests/test_extents.cpp index 718fc4a4..af9dd333 100644 --- a/tests/test_extents.cpp +++ b/tests/test_extents.cpp @@ -481,3 +481,81 @@ TEST(TestExtentsCTADStdArray, test_extents_ctad_std_array) { } */ #endif + +#if MDSPAN_HAS_CXX_17 +TEST(TestExtentsConstructorPreconditions, test_extents_construct_indices) { + auto test_precondition_indices_not_representable = [] { + [[maybe_unused]] auto exts = Kokkos::dextents< std::int8_t, 2 >{ 500, 500 }; + }; +#if defined(MDSPAN_IMPL_HAS_CUDA) || defined(MDSPAN_IMPL_HAS_HIP) || defined(MDSPAN_IMPL_HAS_SYCL) + EXPECT_DEATH(test_precondition_indices_not_representable(), ""); +#else + EXPECT_DEATH(test_precondition_indices_not_representable(), "all_values_are_nonnegative_and_representable"); +#endif + + auto test_precondition_indices_are_negative = [] { + [[maybe_unused]] auto exts = Kokkos::dextents< int, 2 >{ 500, -500 }; + }; +#if defined(MDSPAN_IMPL_HAS_CUDA) || defined(MDSPAN_IMPL_HAS_HIP) || defined(MDSPAN_IMPL_HAS_SYCL) + EXPECT_DEATH(test_precondition_indices_are_negative(), ""); +#else + EXPECT_DEATH(test_precondition_indices_are_negative(), "all_values_are_nonnegative_and_representable"); +#endif +} + +TEST(TestExtentsConstructorPreconditions, test_extents_construct_array) { + auto test_precondition_array_elements_not_representable = [] { + [[maybe_unused]] auto exts = Kokkos::dextents< std::int8_t, 2 >{ std::array{ 500, 500 } }; + }; +#if defined(MDSPAN_IMPL_HAS_CUDA) || defined(MDSPAN_IMPL_HAS_HIP) || defined(MDSPAN_IMPL_HAS_SYCL) + EXPECT_DEATH(test_precondition_array_elements_not_representable(), ""); +#else + EXPECT_DEATH(test_precondition_array_elements_not_representable(), "range_is_nonnegative_and_representable"); +#endif + + auto test_precondition_array_elements_are_negative = [] { + [[maybe_unused]] auto exts = Kokkos::dextents< int, 2 >{ std::array{ 500, -500 } }; + }; +#if defined(MDSPAN_IMPL_HAS_CUDA) || defined(MDSPAN_IMPL_HAS_HIP) || defined(MDSPAN_IMPL_HAS_SYCL) + EXPECT_DEATH(test_precondition_array_elements_are_negative(), ""); +#else + EXPECT_DEATH(test_precondition_array_elements_are_negative(), "range_is_nonnegative_and_representable"); +#endif +} + +#ifdef __cpp_lib_span +TEST(TestExtentsConstructorPreconditions, test_extents_construct_span) { + auto test_precondition_span_elements_not_representable = [] { + auto indices = std::array{ 500, 500 }; + [[maybe_unused]] auto exts = Kokkos::dextents< std::int8_t, 2 >{ std::span{ indices } }; + }; +#if defined(MDSPAN_IMPL_HAS_CUDA) || defined(MDSPAN_IMPL_HAS_HIP) || defined(MDSPAN_IMPL_HAS_SYCL) + EXPECT_DEATH(test_precondition_span_elements_not_representable(), ""); +#else + EXPECT_DEATH(test_precondition_span_elements_not_representable(), "range_is_nonnegative_and_representable"); +#endif + + auto test_precondition_span_elements_are_negative = [] { + auto indices = std::array{ 500, -500 }; + [[maybe_unused]] auto exts = Kokkos::dextents< int, 2 >{ std::span{ indices } }; + }; +#if defined(MDSPAN_IMPL_HAS_CUDA) || defined(MDSPAN_IMPL_HAS_HIP) || defined(MDSPAN_IMPL_HAS_SYCL) + EXPECT_DEATH(test_precondition_span_elements_are_negative(), ""); +#else + EXPECT_DEATH(test_precondition_span_elements_are_negative(), "range_is_nonnegative_and_representable"); +#endif +} +#endif + +TEST(TestExtentsConstructorPreconditions, test_extents_construct_other_extents) { + auto test_precondition_extent_ranks_not_representable = [] { + auto first = Kokkos::dextents< int, 2 >{ 500, 500 }; + [[maybe_unused]] auto exts = Kokkos::dextents< std::int8_t, 2 >{ first }; + }; +#if defined(MDSPAN_IMPL_HAS_CUDA) || defined(MDSPAN_IMPL_HAS_HIP) || defined(MDSPAN_IMPL_HAS_SYCL) + EXPECT_DEATH(test_precondition_extent_ranks_not_representable(), ""); +#else + EXPECT_DEATH(test_precondition_extent_ranks_not_representable(), "extent_is_representable"); +#endif +} +#endif