diff --git a/.gitignore b/.gitignore index 4e3e747f..6c297409 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ CMakeUserPresets.json out build* Makefile +_codeql_build_dir/ +_codeql_detected_source_root diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d9431a6..75eaff3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,10 +28,12 @@ option(MDSPAN_GENERATE_STD_NAMESPACE_TARGETS "Whether to generate and install ta # Option to override which C++ standard to use set(MDSPAN_CXX_STANDARD DETECT CACHE STRING "Override the default CXX_STANDARD to compile with.") -set_property(CACHE MDSPAN_CXX_STANDARD PROPERTY STRINGS DETECT 14 17 20 23) +set_property(CACHE MDSPAN_CXX_STANDARD PROPERTY STRINGS DETECT 14 17 20 23 26) option(MDSPAN_ENABLE_CONCEPTS "Try to enable concepts support by giving extra flags." On) +option(MDSPAN_IMPL_ENABLE_P3663 "Enable implementation of P3663 (Future-proof submdspan_mapping)." On) + ################################################################################ # Decide on the standard to use @@ -63,8 +65,18 @@ elseif(MDSPAN_CXX_STANDARD STREQUAL "23") else() message(FATAL_ERROR "Requested MDSPAN_CXX_STANDARD \"23\" not supported by provided C++ compiler") endif() +elseif(MDSPAN_CXX_STANDARD STREQUAL "26") + if("cxx_std_26" IN_LIST CMAKE_CXX_COMPILE_FEATURES) + message(STATUS "Using C++26 standard") + set(CMAKE_CXX_STANDARD 26) + else() + message(WARNING "Requested MDSPAN_CXX_STANDARD \"26\" not supported by provided C++ compiler") + endif() else() - if("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES) + if("cxx_std_26" IN_LIST CMAKE_CXX_COMPILE_FEATURES) + set(CMAKE_CXX_STANDARD 26) + message(STATUS "Detected support for C++26 standard") + elseif("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES) set(CMAKE_CXX_STANDARD 23) message(STATUS "Detected support for C++23 standard") elseif("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES) diff --git a/include/experimental/__p0009_bits/config.hpp b/include/experimental/__p0009_bits/config.hpp index 9a00e425..3102aa54 100644 --- a/include/experimental/__p0009_bits/config.hpp +++ b/include/experimental/__p0009_bits/config.hpp @@ -303,3 +303,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/include/experimental/__p2630_bits/constant_wrapper.hpp b/include/experimental/__p2630_bits/constant_wrapper.hpp new file mode 100644 index 00000000..e2ff6bfd --- /dev/null +++ b/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/include/experimental/__p2630_bits/submdspan_extents.hpp b/include/experimental/__p2630_bits/submdspan_extents.hpp index 4fe5dc6e..789307a8 100644 --- a/include/experimental/__p2630_bits/submdspan_extents.hpp +++ b/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" @@ -261,36 +262,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/tests/CMakeLists.txt b/tests/CMakeLists.txt index 97c7fdb9..c3abd883 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,7 +26,13 @@ function(mdspan_add_test name) target_compile_definitions(${name} PUBLIC MDSPAN_IMPL_CHECK_PRECONDITION=$ - ) + ) + if(MDSPAN_IMPL_ENABLE_P3663) + target_compile_definitions(${name} + PUBLIC + MDSPAN_IMPL_ENABLE_P3663=1 + ) + endif() endfunction() if(MDSPAN_USE_SYSTEM_GTEST) @@ -105,3 +111,5 @@ if((CMAKE_CXX_COMPILER_ID STREQUAL Clang) OR ((CMAKE_CXX_COMPILER_ID STREQUAL GN add_subdirectory(libcxx-backports) endif() endif() + +mdspan_add_test(test_constant_wrapper) diff --git a/tests/test_constant_wrapper.cpp b/tests/test_constant_wrapper.cpp new file mode 100644 index 00000000..6b2ffdfc --- /dev/null +++ b/tests/test_constant_wrapper.cpp @@ -0,0 +1,143 @@ +//@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 +#include +#include + +#include "../include/experimental/__p2630_bits/constant_wrapper.hpp" + +namespace { // (anonymous) + +#if defined(__cpp_lib_constant_wrapper) + +template +using IC = std::integral_constant; + +template +constexpr void test_integral_constant_wrapper(IC ic) { + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::constant_wrapper; + + constexpr auto c = cw; + + static_assert(std::is_same_v< + std::remove_const_t)>, + constant_wrapper>); + static_assert(decltype(c)::value == Value); + static_assert(std::is_same_v< + typename decltype(c)::type, + constant_wrapper>); + static_assert(std::is_same_v< + typename decltype(c)::value_type, + Integral>); + + constexpr auto c2 = cw; + // Casting the arithmetic result back to Integral undoes + // any integer promotions (e.g., short + short -> int). + constexpr auto val_plus_1 = Integral(Value + Integral(1)); + constexpr auto c_assigned = (c2 = IC{}); + static_assert(c_assigned == val_plus_1); +} + +TEST(TestConstantWrapper, Construction) { + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); + test_integral_constant_wrapper(IC{}); +} +#endif + +TEST(TestConstantWrapper, IntegerPlus) { + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::constant_wrapper; + + constant_wrapper cw_11; + constexpr size_t value = cw_11; + constexpr size_t value2 = constant_wrapper::value; + static_assert(value == value2); + constexpr size_t value3 = decltype(cw_11)(); + static_assert(value == value3); + +#if defined(__cpp_lib_constant_wrapper) + static_assert(std::is_same_v< + decltype(cw_11), + std::remove_const_t)>>); +#endif + + [[maybe_unused]] auto expected_result = cw; + using expected_type = constant_wrapper; + static_assert(std::is_same_v); + +#if defined(__cpp_lib_constant_wrapper) + [[maybe_unused]] auto cw_11_plus_one = cw_11 + cw; + [[maybe_unused]] auto one_plus_cw_11 = cw + cw_11; + + static_assert(! std::is_same_v< + decltype(cw_11 + cw), + size_t>); + static_assert(std::is_same_v< + decltype(cw_11 + cw), + constant_wrapper>); + static_assert(std::is_same_v< + decltype(cw + cw_11), + constant_wrapper>); +#endif +} + +TEST(TestConstantWrapper, Arithmetic) { + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::constant_wrapper; + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::increment; + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::divide; + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::multiply; + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::is_constant_wrapper; + + constexpr auto c6 = cw; + constexpr auto c3 = cw; + constexpr auto c0 = cw; + + // increment preserves compile-time constant + constexpr auto c7 = increment(c6); + static_assert(size_t(c7) == 7u); + static_assert(is_constant_wrapper); + + // divide preserves compile-time constant + constexpr auto div_result = divide(c6, c3); + static_assert(size_t(div_result) == 2u); + static_assert(is_constant_wrapper); + + // divide by zero returns 0 (special case for strided_slice) + constexpr auto div_zero = divide(c0, c3); + static_assert(size_t(div_zero) == 0u); + static_assert(is_constant_wrapper); + + // multiply preserves compile-time constant + constexpr auto mul_result = multiply(c6, c3); + static_assert(size_t(mul_result) == 18u); + static_assert(is_constant_wrapper); +} + +} // namespace (anonymous)