diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 697a5a11..308ef2eb 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false matrix: compiler_driver: [g++-9, g++-10, clang++, icpx] - stdcxx: [17, 20, 23] + stdcxx: [14, 17, 20, 23] include: - compiler_driver: g++-9 compiler_prefix: /usr/bin @@ -27,6 +27,8 @@ jobs: # https://www.intel.com/content/www/us/en/developer/articles/tool/oneapi-standalone-components.html#inpage-nav-6-undefined compiler_url: https://registrationcenter-download.intel.com/akdlm/IRC_NAS/ebf5d9aa-17a7-46a4-b5df-ace004227c0e/l_dpcpp-cpp-compiler_p_2023.2.1.8_offline.sh - enable_benchmark: ON + - stdcxx: 14 + enable_benchmark: OFF - stdcxx: 23 enable_benchmark: OFF exclude: diff --git a/include/experimental/__p0009_bits/extents.hpp b/include/experimental/__p0009_bits/extents.hpp index 22e9c071..98a57e34 100644 --- a/include/experimental/__p0009_bits/extents.hpp +++ b/include/experimental/__p0009_bits/extents.hpp @@ -16,11 +16,13 @@ #pragma once #include "dynamic_extent.hpp" +#include "utility.hpp" #ifdef __cpp_lib_span #include #endif #include +#include #include #include @@ -60,8 +62,8 @@ template MDSPAN_INLINE_FUNCTION static constexpr bool are_valid_indices() { return - (std::is_convertible::value && ... && true) && - (std::is_nothrow_constructible::value && ... && true); + _MDSPAN_FOLD_AND(std::is_convertible::value) && + _MDSPAN_FOLD_AND(std::is_nothrow_constructible::value); } // ------------------------------------------------------------------ @@ -539,14 +541,9 @@ template class extents { MDSPAN_INLINE_FUNCTION friend constexpr bool operator==(const extents &lhs, const extents &rhs) noexcept { - if constexpr (rank() != extents::rank()) { - return false; - } else { - using common_t = std::common_type_t; - for (size_type r = 0; r < m_rank; r++) - if(static_cast(rhs.extent(r)) != static_cast(lhs.extent(r))) return false; - } - return true; + return + rank() == extents::rank() && + detail::rankwise_equal(detail::with_rank{}, rhs, lhs, detail::extent); } #if !(MDSPAN_HAS_CXX_20) @@ -649,17 +646,21 @@ check_upper_bound(InputIndexType user_index, #endif } +// Returning true to use AND fold instead of comma +// CPP14 mode doesn't like the use of void expressions +// with the way the _MDSPAN_FOLD_AND is set up template MDSPAN_INLINE_FUNCTION -constexpr void +constexpr bool check_one_index(InputIndex user_index, ExtentsIndexType current_extent) { check_lower_bound(user_index, current_extent, - std::integral_constant>{}); + std::integral_constant::value>{}); check_upper_bound(user_index, current_extent); + return true; } - + template @@ -669,7 +670,8 @@ check_all_indices_helper(std::index_sequence, const extents& exts, Indices... indices) { - _MDSPAN_FOLD_COMMA( + // Suppress warning about statement has no effect + (void) _MDSPAN_FOLD_AND( (check_one_index(indices, exts.extent(RankIndices))) ); } @@ -684,6 +686,6 @@ check_all_indices(const extents& exts, check_all_indices_helper(std::make_index_sequence(), exts, indices...); } - + } // namespace detail } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/include/experimental/__p0009_bits/layout_left.hpp b/include/experimental/__p0009_bits/layout_left.hpp index 81a1c37e..222fba7a 100644 --- a/include/experimental/__p0009_bits/layout_left.hpp +++ b/include/experimental/__p0009_bits/layout_left.hpp @@ -18,8 +18,11 @@ #include "macros.hpp" #include "trait_backports.hpp" #include "extents.hpp" +#include "layout_stride.hpp" +#include "utility.hpp" +#if MDSPAN_HAS_CXX_17 #include "../__p2642_bits/layout_padded_fwd.hpp" -#include +#endif #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -133,11 +136,11 @@ class layout_left::mapping { : __extents(__other.extents()) { MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: - check_padded_layout_converting_constructor_mandates(); + 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>(__other); + extents_type>(detail::with_rank{}, __other); } #endif @@ -156,17 +159,7 @@ class layout_left::mapping { * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ - #if !defined(_MDSPAN_HAS_CUDA) && !defined(_MDSPAN_HAS_HIP) && !defined(NDEBUG) - if constexpr (extents_type::rank() > 0) { - index_type stride = 1; - using common_t = std::common_type_t; - for(rank_type r=0; r<__extents.rank(); r++) { - if(static_cast(stride) != static_cast(other.stride(r))) - std::abort(); // ("Assigning layout_stride to layout_left with invalid strides."); - stride *= __extents.extent(r); - } - } - #endif + detail::validate_strides(detail::with_rank{}, layout_left{}, __extents, other); } MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mapping& operator=(mapping const&) noexcept = default; diff --git a/include/experimental/__p0009_bits/layout_right.hpp b/include/experimental/__p0009_bits/layout_right.hpp index b45aead9..284569f6 100644 --- a/include/experimental/__p0009_bits/layout_right.hpp +++ b/include/experimental/__p0009_bits/layout_right.hpp @@ -18,9 +18,11 @@ #include "macros.hpp" #include "trait_backports.hpp" #include "extents.hpp" -#include #include "layout_stride.hpp" +#include "utility.hpp" +#if MDSPAN_HAS_CXX_17 #include "../__p2642_bits/layout_padded_fwd.hpp" +#endif namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -134,11 +136,11 @@ class layout_right::mapping { : __extents(__other.extents()) { MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: - check_padded_layout_converting_constructor_mandates(); + 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>(__other); + extents_type>(detail::with_rank{}, __other); } #endif @@ -157,17 +159,7 @@ class layout_right::mapping { * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ - #if !defined(_MDSPAN_HAS_CUDA) && !defined(_MDSPAN_HAS_HIP) && !defined(NDEBUG) - if constexpr (extents_type::rank() > 0) { - index_type stride = 1; - using common_t = std::common_type_t; - for(rank_type r=__extents.rank(); r>0; r--) { - if(static_cast(stride) != static_cast(other.stride(r-1))) - std::abort(); // ("Assigning layout_stride to layout_right with invalid strides."); - stride *= __extents.extent(r-1); - } - } - #endif + detail::validate_strides(detail::with_rank{}, layout_right{}, __extents, other); } MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mapping& operator=(mapping const&) noexcept = default; diff --git a/include/experimental/__p0009_bits/layout_stride.hpp b/include/experimental/__p0009_bits/layout_stride.hpp index 3c0b7962..2e5a745a 100644 --- a/include/experimental/__p0009_bits/layout_stride.hpp +++ b/include/experimental/__p0009_bits/layout_stride.hpp @@ -19,14 +19,16 @@ #include "extents.hpp" #include "trait_backports.hpp" #include "compressed_pair.hpp" +#include "utility.hpp" #if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) # include "no_unique_address.hpp" #endif -#include -#include #include +#include +#include + #ifdef __cpp_lib_span #include #endif @@ -38,11 +40,11 @@ namespace MDSPAN_IMPL_STANDARD_NAMESPACE { struct layout_left { template - class mapping; + class mapping; }; struct layout_right { template - class mapping; + class mapping; }; namespace detail { @@ -79,6 +81,7 @@ namespace detail { std::bool_constant::value; }; #endif + } // namespace detail struct layout_stride { @@ -225,7 +228,11 @@ struct layout_stride { // Can't use defaulted parameter in the __deduction_workaround template because of a bug in MSVC warning C4348. using __impl = __deduction_workaround>; - static constexpr __strides_storage_t strides_storage(std::true_type) { + static constexpr __strides_storage_t strides_storage(detail::with_rank<0>) { + return {}; + } + template + static constexpr __strides_storage_t strides_storage(detail::with_rank) { __strides_storage_t s{}; extents_type e; @@ -237,9 +244,6 @@ struct layout_stride { return s; } - static constexpr __strides_storage_t strides_storage(std::false_type) { - return {}; - } //---------------------------------------------------------------------------- @@ -262,7 +266,7 @@ struct layout_stride { : __base_t(__base_t{__member_pair_t( #endif extents_type(), - __strides_storage_t(strides_storage(std::integral_constant 0)>{})) + __strides_storage_t(strides_storage(detail::with_rank{})) #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) } #else @@ -447,32 +451,48 @@ struct layout_stride { MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; } - MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 bool is_exhaustive() const noexcept { - if constexpr (extents_type::rank() == 0) - return true; - else { - index_type span_size = required_span_size(); - if (span_size == static_cast(0)) { - if constexpr (extents_type::rank() == 1) { - return stride(0) == 1; - } else { - rank_type r_largest = 0; - for (rank_type r = 1; r < extents_type::rank(); r++) { - if (stride(r) > stride(r_largest)) { - r_largest = r; - } - } - for (rank_type r = 0; r < extents_type::rank(); r++) { - if (extents().extent(r) == 0 && r != r_largest) { - return false; - } - } - return true; - } - } else { - return required_span_size() == __get_size(extents(), std::make_index_sequence()); + + private: + constexpr bool exhaustive_for_nonzero_span_size() const + { + return required_span_size() == __get_size(extents(), std::make_index_sequence()); + } + + constexpr bool is_exhaustive_impl(detail::with_rank<0>) const + { + return true; + } + constexpr bool is_exhaustive_impl(detail::with_rank<1>) const + { + if (required_span_size() != static_cast(0)) { + return exhaustive_for_nonzero_span_size(); + } + return stride(0) == 1; + } + template + constexpr bool is_exhaustive_impl(detail::with_rank) const + { + if (required_span_size() != static_cast(0)) { + return exhaustive_for_nonzero_span_size(); + } + + rank_type r_largest = 0; + for (rank_type r = 1; r < extents_type::rank(); r++) { + if (stride(r) > stride(r_largest)) { + r_largest = r; + } + } + for (rank_type r = 0; r < extents_type::rank(); r++) { + if (extents().extent(r) == 0 && r != r_largest) { + return false; } } + return true; + } + + public: + MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 bool is_exhaustive() const noexcept { + return is_exhaustive_impl(detail::with_rank{}); } MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; } @@ -501,15 +521,9 @@ struct layout_stride { #endif MDSPAN_INLINE_FUNCTION friend constexpr bool operator==(const mapping& x, const StridedLayoutMapping& y) noexcept { - bool strides_match = true; - if constexpr (extents_type::rank() > 0) { - using common_t = std::common_type_t; - for(rank_type r = 0; r < extents_type::rank(); r++) - strides_match = strides_match && (static_cast(x.stride(r)) == static_cast(y.stride(r))); - } return (x.extents() == y.extents()) && (__impl::__OFFSET(y) == static_cast(0)) && - strides_match; + detail::rankwise_equal(detail::with_rank{}, x, y, detail::stride); } // This one is not technically part of the proposal. Just here to make implementation a bit more optimal hopefully @@ -564,4 +578,34 @@ struct layout_stride { }; }; +namespace detail { + +template +constexpr void validate_strides(with_rank<0>, Layout, const Extents&, const Mapping&) +{} + +template +constexpr void validate_strides(with_rank, Layout, const Extents& ext, const Mapping& other) +{ + static_assert(std::is_same::value and + (std::is_same::value or + std::is_same::value) + , "This function is only intended to validate construction of " + "a layout_left or layout_right mapping from a layout_stride mapping."); + + constexpr auto is_left = std::is_same::value; + + typename Extents::index_type stride = 1; + + for (std::size_t r = 0; r < N; r++) { + const std::size_t s = is_left ? r : N - 1 - r; + + MDSPAN_IMPL_PRECONDITION(common_integral_compare(stride, other.stride(s)) + and "invalid strides for layout_{left,right}"); + + stride *= ext.extent(s); + } +} + +} // namespace detail } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/include/experimental/__p0009_bits/macros.hpp b/include/experimental/__p0009_bits/macros.hpp index 3eeb3975..e87997a8 100644 --- a/include/experimental/__p0009_bits/macros.hpp +++ b/include/experimental/__p0009_bits/macros.hpp @@ -18,7 +18,12 @@ #include "config.hpp" +#include +#include #include // std::is_void +#if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_SYCL) +#include "assert.h" +#endif #ifndef _MDSPAN_HOST_DEVICE # if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) @@ -101,6 +106,69 @@ #define MDSPAN_IMPL_STANDARD_NAMESPACE_STRING MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_STANDARD_NAMESPACE) #define MDSPAN_IMPL_PROPOSED_NAMESPACE_STRING MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_STANDARD_NAMESPACE) "::" MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_PROPOSED_NAMESPACE) +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +#if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) +MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) +{ + printf("%s:%u: precondition failure: `%s`\n", file, line, cond); + assert(0); +} +#elif defined(_MDSPAN_HAS_SYCL) +MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) +{ + sycl::ext::oneapi::experimental::printf("%s:%u: precondition failure: `%s`\n", file, line, cond); + assert(0); +} +#else +MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) +{ + std::fprintf(::stderr, "%s:%u: precondition failure: `%s`\n", file, line, cond); + std::abort(); +} +#endif + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#ifndef MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER +#define MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER(cond, file, line) \ + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::default_precondition_violation_handler(cond, file, line) +#endif + +#ifndef MDSPAN_IMPL_CHECK_PRECONDITION + #ifndef NDEBUG + #define MDSPAN_IMPL_CHECK_PRECONDITION 0 + #else + #define MDSPAN_IMPL_CHECK_PRECONDITION 1 + #endif +#endif + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +template +MDSPAN_FUNCTION constexpr void precondition(const char* cond, const char* file, unsigned line) +{ + if (not check) { return; } + // in case the macro doesn't use the arguments for custom macros + (void) cond; + (void) file; + (void) line; + MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER(cond, file, line); +} + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#define MDSPAN_IMPL_PRECONDITION(...) \ + do { \ + if (not (__VA_ARGS__)) { \ + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::precondition(#__VA_ARGS__, __FILE__, __LINE__); \ + } \ + } while (0) + // end Preprocessor helpers }}}1 //============================================================================== diff --git a/include/experimental/__p0009_bits/utility.hpp b/include/experimental/__p0009_bits/utility.hpp new file mode 100644 index 00000000..2f66eea9 --- /dev/null +++ b/include/experimental/__p0009_bits/utility.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +// type alias used for rank-based tag dispatch +// +// this is used to enable alternatives to constexpr if when building for C++14 +// +template +using with_rank = std::integral_constant; + +template +constexpr bool common_integral_compare(I1 x, I2 y) +{ + static_assert(std::is_integral::value and + std::is_integral::value, ""); + + using I = std::common_type_t; + return static_cast(x) == static_cast(y); +} + +template +constexpr bool rankwise_equal(with_rank<0>, const T1&, const T2&, F) +{ + return true; +} +template +constexpr bool rankwise_equal(with_rank, const T1& x, const T2& y, F func) +{ + bool match = true; + + for (std::size_t r = 0; r < N; r++) { + match = match && common_integral_compare(func(x, r), func(y, r)); + } + + return match; +} + +constexpr struct +{ + template + constexpr auto operator()(const T& x, I i) const + { + return x.extent(i); + } +} extent; + +constexpr struct +{ + template + constexpr auto operator()(const T& x, I i) const + { + return x.stride(i); + } +} stride; + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/include/experimental/__p2642_bits/layout_padded_fwd.hpp b/include/experimental/__p2642_bits/layout_padded_fwd.hpp index 945f091a..b5eaac95 100644 --- a/include/experimental/__p2642_bits/layout_padded_fwd.hpp +++ b/include/experimental/__p2642_bits/layout_padded_fwd.hpp @@ -17,6 +17,7 @@ #include #include "../__p0009_bits/dynamic_extent.hpp" +#include "../__p0009_bits/utility.hpp" namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { @@ -82,36 +83,49 @@ struct is_layout_right_padded_mapping<_Mapping, std::enable_if_t::template mapping>::value>> : std::true_type {}; + +template +constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<0>) {} + template -constexpr void check_padded_layout_converting_constructor_mandates() +constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<1>) {} + +template +constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank) { - if constexpr (_LayoutExtentsType::rank() > 1) { - using extents_type = typename _PaddedLayoutMappingType::extents_type; - constexpr auto padding_value = _PaddedLayoutMappingType::padding_value; - constexpr auto idx = layout_padded_constants::extent_to_pad_idx; - if constexpr ((_LayoutExtentsType::static_extent(idx) != dynamic_extent) && - (extents_type::static_extent(idx) != dynamic_extent) && - (padding_value != dynamic_extent)) { - if constexpr (padding_value == 0) { - static_assert(_LayoutExtentsType::static_extent(idx) == 0); - } else { - static_assert( - _LayoutExtentsType::static_extent(idx) % padding_value == 0); - } - } - } + using extents_type = typename _PaddedLayoutMappingType::extents_type; + constexpr auto padding_value = _PaddedLayoutMappingType::padding_value; + constexpr auto idx = layout_padded_constants::extent_to_pad_idx; + + constexpr auto statically_determinable = + (_LayoutExtentsType::static_extent(idx) != dynamic_extent) && + (extents_type::static_extent(idx) != dynamic_extent) && + (padding_value != dynamic_extent); + + static_assert(not statically_determinable or + (padding_value == 0 + ? _LayoutExtentsType::static_extent(idx) == 0 + : _LayoutExtentsType::static_extent(idx) % padding_value == 0), + ""); } template -constexpr void check_padded_layout_converting_constructor_preconditions([[maybe_unused]] const _OtherMapping &other_mapping) { - if constexpr (_ExtentsType::rank() > 1) { - constexpr auto padded_stride_idx = - layout_padded_constants::padded_stride_idx; - constexpr auto extent_to_pad_idx = layout_padded_constants::extent_to_pad_idx; - assert(other_mapping.stride(padded_stride_idx) == other_mapping.extents().extent(extent_to_pad_idx)); - } +constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<0>, + const _OtherMapping&) {} +template +constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<1>, + const _OtherMapping&) {} +template +constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank, + const _OtherMapping &other_mapping) { + constexpr auto padded_stride_idx = + layout_padded_constants::padded_stride_idx; + constexpr auto extent_to_pad_idx = layout_padded_constants::extent_to_pad_idx; + MDSPAN_IMPL_PRECONDITION(other_mapping.stride(padded_stride_idx) == other_mapping.extents().extent(extent_to_pad_idx)); } + + } } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9f7a4810..f72dd761 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,14 +6,28 @@ if(MDSPAN_ENABLE_CUDA) endif() endif() -macro(mdspan_add_test name) +function(mdspan_add_test name) + set(options ENABLE_PRECONDITIONS) + set(one_value SOURCE) + set(multi_value) + cmake_parse_arguments(ARGUMENT "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if(NOT DEFINED ARGUMENT_SOURCE) + set(ARGUMENT_SOURCE ${name}.cpp) + endif() + if(MDSPAN_TEST_LANGUAGE) set_source_files_properties(${name} PROPERTIES LANGUAGE ${MDSPAN_TEST_LANGUAGE}) endif() - add_executable(${name} ${name}.cpp) + add_executable(${name} ${ARGUMENT_SOURCE}) target_link_libraries(${name} mdspan gtest_main) add_test(${name} ${name}) -endmacro() + set_property(TARGET ${name} PROPERTY COMPILE_WARNING_AS_ERROR ON) + target_compile_definitions(${name} + PUBLIC + MDSPAN_IMPL_CHECK_PRECONDITION=$ + ) +endfunction() if(MDSPAN_USE_SYSTEM_GTEST) find_package(GTest CONFIG REQUIRED) @@ -48,6 +62,27 @@ else() ) endif() +mdspan_add_test( + test_precondition_checks_can_be_disabled + SOURCE test_alternate_precondition_violation_handler.cpp +) +target_compile_definitions( + test_precondition_checks_can_be_disabled +PUBLIC + MDSPAN_IMPL_CHECK_PRECONDITION=0 +) +set_tests_properties( + test_precondition_checks_can_be_disabled +PROPERTIES + PASS_REGULAR_EXPRESSION "Failure.*Expected.*throws an exception of type std::logic_error.*Actual.*it throws nothing" +) + +if(NOT MDSPAN_ENABLE_CUDA AND NOT MDSPAN_ENABLE_HIP AND NOT MDSPAN_ENABLE_SYCL) +mdspan_add_test(test_alternate_precondition_violation_handler ENABLE_PRECONDITIONS) +endif() +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_mdspan_ctors) diff --git a/tests/test_alternate_precondition_violation_handler.cpp b/tests/test_alternate_precondition_violation_handler.cpp new file mode 100644 index 00000000..1cc3998b --- /dev/null +++ b/tests/test_alternate_precondition_violation_handler.cpp @@ -0,0 +1,29 @@ +#include + +#define MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER(cond, file, line) \ + do { \ + throw std::logic_error{"precondition failure"}; \ + } while (0); + +#include +#include + +TEST(mdspan_macros, alternate_precondition_violation_handler) +{ + ASSERT_THROW(MDSPAN_IMPL_PRECONDITION(false), std::logic_error); +} + +TEST(mdspan_macros, alternate_precondition_check_constexpr_invocable) +{ + struct fn + { + constexpr auto operator()() const + { + MDSPAN_IMPL_PRECONDITION(1 + 1 == 2); + return 42; + } + }; + + static constexpr auto x = fn{}(); + (void)x; +} diff --git a/tests/test_dims.cpp b/tests/test_dims.cpp index 4de05e7f..7ceb6f35 100644 --- a/tests/test_dims.cpp +++ b/tests/test_dims.cpp @@ -32,18 +32,18 @@ template void test_dims_with_one_template_argument() { using d = MDSPAN_IMPL_STANDARD_NAMESPACE :: MDSPAN_IMPL_PROPOSED_NAMESPACE :: dims; - static_assert(test::is_extents_v); - static_assert(std::is_same_v); - static_assert(d::rank() == Rank); + static_assert(test::is_extents_v, "dims is not an extents specialization"); + static_assert(std::is_same::value, "dims::index_type is wrong"); + static_assert(d::rank() == Rank, "dims::rank() is wrong"); } template void test_dims_with_two_template_arguments() { using d = MDSPAN_IMPL_STANDARD_NAMESPACE :: MDSPAN_IMPL_PROPOSED_NAMESPACE :: dims; - static_assert(test::is_extents_v); - static_assert(std::is_same_v); - static_assert(d::rank() == Rank); + static_assert(test::is_extents_v, "dims is not an extents specialization"); + static_assert(std::is_same::value, "dims::index_type is wrong"); + static_assert(d::rank() == Rank, "dims::rank() is wrong"); } } // namespace test diff --git a/tests/test_layout_preconditions.cpp b/tests/test_layout_preconditions.cpp new file mode 100644 index 00000000..c24f4a83 --- /dev/null +++ b/tests/test_layout_preconditions.cpp @@ -0,0 +1,32 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2024) 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 +#include + +#include + +TEST(TestConvertingConstructionFromLayoutStride, precondition_failure) { + using E = Kokkos::extents; + + const auto stride = Kokkos::layout_stride::mapping{E{}, std::array{2, 8}}; + +#if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_SYCL) + ASSERT_DEATH(Kokkos::layout_left::mapping{stride}, ""); + ASSERT_DEATH(Kokkos::layout_right::mapping{stride}, ""); +#else + ASSERT_DEATH(Kokkos::layout_left::mapping{stride}, "invalid strides"); + ASSERT_DEATH(Kokkos::layout_right::mapping{stride}, "invalid strides"); +#endif +} diff --git a/tests/test_macros.cpp b/tests/test_macros.cpp new file mode 100644 index 00000000..bea86d83 --- /dev/null +++ b/tests/test_macros.cpp @@ -0,0 +1,28 @@ +#include +#include + +TEST(mdspan_macros, precondition_violation) +{ +#if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_SYCL) + constexpr auto msg = ""; +#else + constexpr auto msg = "hello, world!"; +#endif + + ASSERT_DEATH(MDSPAN_IMPL_PRECONDITION(false and "hello, world!"), msg); +} + +TEST(mdspan_macros, precondition_check_constexpr_invocable) +{ + struct fn + { + constexpr auto operator()() const + { + MDSPAN_IMPL_PRECONDITION(1 + 1 == 2); + return 42; + } + }; + + static constexpr auto x = fn{}(); + (void)x; +} diff --git a/tests/test_mdspan_size.cpp b/tests/test_mdspan_size.cpp index 60286a12..714bdbac 100644 --- a/tests/test_mdspan_size.cpp +++ b/tests/test_mdspan_size.cpp @@ -55,6 +55,7 @@ void test_mdspan_size(std::vector& storage, Extents&& e) TEST(TestMdspan, MdspanSizeReturnTypeAndPrecondition) { + (void) test_info_; std::vector storage; static_assert(std::numeric_limits::max() == 127, "max int8_t != 127"); diff --git a/tests/test_submdspan_static_slice.cpp b/tests/test_submdspan_static_slice.cpp index 23f74db9..d0567a89 100644 --- a/tests/test_submdspan_static_slice.cpp +++ b/tests/test_submdspan_static_slice.cpp @@ -28,7 +28,7 @@ using IC = std::integral_constant; template void test_submdspan_static_slice( typename InputMdspan::mapping_type input_mapping, - Slices&&... slices) + [[maybe_unused]] Slices&&... slices) { using input_type = InputMdspan; std::vector storage(input_mapping.required_span_size()); @@ -44,7 +44,7 @@ void test_submdspan_static_slice( template void test_submdspan_static_slice( typename InputMdspan::extents_type input_extents, - Slices&&... slices) + [[maybe_unused]] Slices&&... slices) { using input_mapping_type = typename InputMdspan::mapping_type; input_mapping_type input_mapping(input_extents); @@ -52,12 +52,14 @@ void test_submdspan_static_slice( } TEST(TestMdspan, StaticAsserts) { + (void) test_info_; static_assert(std::is_convertible, std::size_t>::value, "Just a check."); static_assert(std::is_convertible, std::size_t>::value, "Just a check."); static_assert(std::is_convertible, std::size_t>::value, "Just a check."); } TEST(TestMdspan, SubmdspanStaticSlice_Left_iddd_FullFullIndex) { + (void) test_info_; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_left; using input_mdspan_type = Kokkos::mdspan; @@ -84,6 +86,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_iddd_FullFullIndex) { } TEST(TestMdspan, SubmdspanStaticSlice_Left_i345_FullFullIndex) { + (void) test_info_; using input_extents_type = Kokkos::extents; using input_layout_type = Kokkos::layout_left; using input_mdspan_type = Kokkos::mdspan; @@ -110,6 +113,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_i345_FullFullIndex) { } TEST(TestMdspan, SubmdspanStaticSlice_Right_iddd_FullFullIndex) { + (void) test_info_; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_right; using input_mdspan_type = Kokkos::mdspan; @@ -136,6 +140,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Right_iddd_FullFullIndex) { } TEST(TestMdspan, SubmdspanStaticSlice_Right_i345_FullFullIndex) { + (void) test_info_; using input_extents_type = Kokkos::extents; using input_layout_type = Kokkos::layout_right; using input_mdspan_type = Kokkos::mdspan; @@ -162,6 +167,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Right_i345_FullFullIndex) { } TEST(TestMdspan, SubmdspanStaticSlice_Left_iddd_FullIndexFull) { + (void) test_info_; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_left; using input_mdspan_type = Kokkos::mdspan; @@ -188,6 +194,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_iddd_FullIndexFull) { } TEST(TestMdspan, SubmdspanStaticSlice_Left_i345_FullIndexFull) { + (void) test_info_; using input_extents_type = Kokkos::extents; using input_layout_type = Kokkos::layout_left; using input_mdspan_type = Kokkos::mdspan; @@ -214,6 +221,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_i345_FullIndexFull) { } TEST(TestMdspan, SubmdspanStaticSlice_Left_iddd_FullTupleFull) { + (void) test_info_; using std::tuple; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_left; @@ -251,6 +259,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_iddd_FullTupleFull) { } TEST(TestMdspan, SubmdspanStaticSlice_Left_i345_FullTupleFull) { + (void) test_info_; using std::tuple; using input_extents_type = Kokkos::extents; using input_layout_type = Kokkos::layout_left; @@ -288,6 +297,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_i345_FullTupleFull) { } TEST(TestMdspan, SubmdspanStaticSlice_Right_iddd_FullTupleFull) { + (void) test_info_; using std::tuple; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_right; @@ -325,6 +335,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Right_iddd_FullTupleFull) { } TEST(TestMdspan, SubmdspanStaticSlice_Right_i345_FullTupleFull) { + (void) test_info_; using std::tuple; using input_extents_type = Kokkos::extents; using input_layout_type = Kokkos::layout_right; @@ -362,6 +373,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Right_i345_FullTupleFull) { } TEST(TestMdspan, SubmdspanStaticSlice_Left_iddd_TupleFullTuple) { + (void) test_info_; using std::tuple; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_left; @@ -403,6 +415,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_iddd_TupleFullTuple) { } TEST(TestMdspan, SubmdspanStaticSlice_Left_i345_TupleFullTuple) { + (void) test_info_; using std::tuple; using input_extents_type = Kokkos::extents; using input_layout_type = Kokkos::layout_left; @@ -444,6 +457,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_i345_TupleFullTuple) { } TEST(TestMdspan, SubmdspanStaticSlice_Right_iddd_TupleFullTuple) { + (void) test_info_; using std::tuple; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_right; @@ -485,6 +499,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Right_iddd_TupleFullTuple) { } TEST(TestMdspan, SubmdspanStaticSlice_Right_i345_TupleFullTuple) { + (void) test_info_; using std::tuple; using input_extents_type = Kokkos::extents; using input_layout_type = Kokkos::layout_right; @@ -529,6 +544,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Right_i345_TupleFullTuple) { // it may help to comment out all the tests but the ones below. TEST(TestMdspan, SubmdspanStaticSlice_Left_idd_FullTuple) { + (void) test_info_; // tuple of two integral_constant is convertible to tuple of two size_t. // Nevertheless, submdspan needs special handling to be able to compile // with a tuple of two integral_constant as a slice specifier. @@ -574,6 +590,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_idd_FullTuple) { } TEST(TestMdspan, SubmdspanStaticSlice_Right_idd_FullTuple) { + (void) test_info_; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_right; using input_mdspan_type = Kokkos::mdspan; @@ -608,6 +625,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Right_idd_FullTuple) { } TEST(TestMdspan, SubmdspanStaticSlice_Left_idd_TupleFull) { + (void) test_info_; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_left; using input_mdspan_type = Kokkos::mdspan; @@ -642,6 +660,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_idd_TupleFull) { } TEST(TestMdspan, SubmdspanStaticSlice_Right_idd_TupleFull) { + (void) test_info_; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_right; using input_mdspan_type = Kokkos::mdspan; @@ -676,6 +695,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Right_idd_TupleFull) { } TEST(TestMdspan, SubmdspanStaticSlice_Stride_idd_TupleFull) { + (void) test_info_; using input_extents_type = Kokkos::dextents; using input_layout_type = Kokkos::layout_stride; using input_mdspan_type = Kokkos::mdspan; @@ -715,6 +735,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Stride_idd_TupleFull) { // Rank 1 tests TEST(TestMdspan, SubmdspanStaticSlice_Left_id_Tuple) { + (void) test_info_; using input_extents_type = Kokkos::extents; using input_layout_type = Kokkos::layout_left; using input_mdspan_type = Kokkos::mdspan; @@ -749,6 +770,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_id_Tuple) { } TEST(TestMdspan, SubmdspanStaticSlice_Left_i5_Tuple) { + (void) test_info_; using input_extents_type = Kokkos::extents; using input_layout_type = Kokkos::layout_left; using input_mdspan_type = Kokkos::mdspan; @@ -783,6 +805,7 @@ TEST(TestMdspan, SubmdspanStaticSlice_Left_i5_Tuple) { } TEST(TestMdspan, SubmdspanStaticSlice_Right_id_Tuple) { + (void) test_info_; using input_extents_type = Kokkos::extents; using input_layout_type = Kokkos::layout_right; using input_mdspan_type = Kokkos::mdspan;