From 69f59febb799630f05e4783d36b16b94d3d7669d Mon Sep 17 00:00:00 2001 From: AI agent Date: Mon, 6 Apr 2026 11:52:18 +0000 Subject: [PATCH 1/2] Add makeMdSpan(any) coverage --- test/unit/mem/constCorrectness.cpp | 79 ++++++++++++++++++++++++++++++ test/unit/mem/mdIterator.cpp | 48 ++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/test/unit/mem/constCorrectness.cpp b/test/unit/mem/constCorrectness.cpp index c5a02587b..0143d6a61 100644 --- a/test/unit/mem/constCorrectness.cpp +++ b/test/unit/mem/constCorrectness.cpp @@ -835,6 +835,85 @@ TEST_CASE( } } +TEST_CASE( + "alpaka::makeMdSpan(any) preserves host shape alignment and inner constness", + "[mem][mdspan][correctness][makeMdSpan]") +{ + auto mutableBuffer = alpaka::onHost::allocHost(alpaka::Vec{2u, 3u}); + auto mutableView = mutableBuffer.getView(); + auto const& outerConstBuffer = mutableBuffer; + auto const& outerConstView = mutableView; + + alignas(32) std::array innerConstStorage{0, 1, 2, 3, 4, 5}; + auto innerConstView + = alpaka::makeView(alpaka::api::host, innerConstStorage.data(), alpaka::Vec{2u, 3u}, alpaka::Alignment<32>{}); + + auto mutableBufferMdSpan = alpaka::makeMdSpan(mutableBuffer); + auto mutableViewMdSpan = alpaka::makeMdSpan(mutableView); + auto outerConstBufferMdSpan = alpaka::makeMdSpan(outerConstBuffer); + auto outerConstViewMdSpan = alpaka::makeMdSpan(outerConstView); + auto innerConstViewMdSpan = alpaka::makeMdSpan(innerConstView); + + SECTION("compile-time rebuilding keeps host layout metadata and inner constness") + { + // `makeMdSpan(any)` should rebuild host buffers and views without changing shape, pitches, alignment, or the + // pointed-to constness encoded by the source object. + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert( + std::same_as); + static_assert(!std::is_const_v>); + static_assert(!std::is_const_v>); + + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as); + static_assert(!std::is_const_v>); + static_assert(!std::is_const_v>); + + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert( + std::same_as); + static_assert(std::is_const_v>); + static_assert(std::is_const_v>); + + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert( + std::same_as); + static_assert(std::is_const_v>); + static_assert(std::is_const_v>); + + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert( + std::same_as); + static_assert(std::is_const_v>); + static_assert(std::is_const_v>); + } +} + TEST_CASE("View::getMdSpan keeps host mutability boundaries", "[mem][view][mdspan][correctness]") { alignas(32) std::array storage{}; diff --git a/test/unit/mem/mdIterator.cpp b/test/unit/mem/mdIterator.cpp index 1b7864b43..ad5febce6 100644 --- a/test/unit/mem/mdIterator.cpp +++ b/test/unit/mem/mdIterator.cpp @@ -175,6 +175,54 @@ TEST_CASE("mdIterator host coverage", "[mem][mdIterator][iterator]") REQUIRE(&mdSpan[sampleIdx] == &packedView[sampleIdx]); } + SECTION("makeMdSpan(any) rebuilds host buffers and views without breaking aliasing") + { + // Rebuilding an MdSpan from an existing host buffer or view should preserve the layout contract and keep + // direct access bound to the original storage. + auto const extents = alpaka::Vec{2u, 3u, 4u}; + auto buffer = onHost::allocHost(extents); + auto bufferMdSpan = alpaka::makeMdSpan(buffer); + + meta::ndLoopIncIdx( + extents, + [&](alpaka::concepts::Vector auto idx) + { buffer[idx] = static_cast(100u + linearize(extents, idx)); }); + + alignas(64) std::array storage{}; + for(std::size_t i = 0; i < storage.size(); ++i) + storage[i] = static_cast(i); + + auto view = alpaka::makeView(api::host, storage.data(), extents, alpaka::Alignment<64>{}); + auto mdSpan = alpaka::makeMdSpan(view); + auto const constMdSpan = alpaka::makeMdSpan(std::as_const(view)); + + REQUIRE(bufferMdSpan.getExtents() == buffer.getExtents()); + REQUIRE(bufferMdSpan.getPitches() == buffer.getPitches()); + REQUIRE(bufferMdSpan.data() == buffer.data()); + STATIC_REQUIRE(std::is_same_v); + + REQUIRE(mdSpan.getExtents() == view.getExtents()); + REQUIRE(mdSpan.getPitches() == view.getPitches()); + REQUIRE(mdSpan.data() == view.data()); + STATIC_REQUIRE(std::is_same_v>); + STATIC_REQUIRE(std::is_same_v>); + static_assert(!std::is_const_v>); + static_assert(std::is_const_v>); + + auto const sampleIdx = alpaka::Vec{1u, 2u, 3u}; + mdSpan[sampleIdx] = 777; + REQUIRE(view[sampleIdx] == 777); + REQUIRE(storage[storage.size() - 1u] == 777); + REQUIRE(&mdSpan[sampleIdx] == &view[sampleIdx]); + REQUIRE(&constMdSpan[sampleIdx] == &view[sampleIdx]); + + auto const visited = collectValues(mdSpan); + REQUIRE(visited.size() == storage.size()); + REQUIRE(visited.front() == 0); + REQUIRE(visited.back() == 777); + REQUIRE(collectValues(constMdSpan) == visited); + } + SECTION("pre-increment and post-increment advance one element at a time") { // Forward-iterator increments need to preserve the old value for post-increment and return self for From c82fc13ab43d05817a252ea184e1c5cb68f4c111 Mon Sep 17 00:00:00 2001 From: AI agent Date: Wed, 8 Apr 2026 18:18:22 +0000 Subject: [PATCH 2/2] Add explicit-pitch makeMdSpan coverage --- test/unit/mem/constCorrectness.cpp | 55 ++++++++++++++++++++++++++++++ test/unit/mem/mdIterator.cpp | 48 ++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/test/unit/mem/constCorrectness.cpp b/test/unit/mem/constCorrectness.cpp index 0143d6a61..27b6ce544 100644 --- a/test/unit/mem/constCorrectness.cpp +++ b/test/unit/mem/constCorrectness.cpp @@ -914,6 +914,61 @@ TEST_CASE( } } +TEST_CASE( + "alpaka::makeMdSpan(pointer, extents, pitches, alignment) preserves explicit host metadata and inner constness", + "[mem][mdspan][correctness][makeMdSpan]") +{ + auto const extents = alpaka::Vec{2u, 3u, 4u}; + auto const explicitPitches = alpaka::Vec{80u, 20u, 4u}; + alignas(64) std::array mutableStorage{}; + alignas(64) std::array innerConstStorage{}; + + auto mutableExplicitPitchMdSpan + = alpaka::makeMdSpan(mutableStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{}); + auto const outerConstExplicitPitchMdSpan + = alpaka::makeMdSpan(mutableStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{}); + auto innerConstExplicitPitchMdSpan + = alpaka::makeMdSpan(innerConstStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{}); + + SECTION("compile-time explicit pitches stay part of the rebuilt mdspan contract") + { + // The raw explicit-pitch overload should keep the caller-supplied host layout, alignment, and pointed-to + // constness instead of normalizing the span back to a packed representation. + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as>); + static_assert(!std::is_const_v>); + static_assert( + !std::is_const_v>); + + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as>); + static_assert(std::is_const_v>); + static_assert(std::is_const_v< + std::remove_reference_t>); + + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as< + std::remove_cvref_t, + std::remove_cvref_t>); + static_assert(std::same_as>); + static_assert(std::is_const_v>); + static_assert(std::is_const_v< + std::remove_reference_t>); + } +} + TEST_CASE("View::getMdSpan keeps host mutability boundaries", "[mem][view][mdspan][correctness]") { alignas(32) std::array storage{}; diff --git a/test/unit/mem/mdIterator.cpp b/test/unit/mem/mdIterator.cpp index ad5febce6..02cd33836 100644 --- a/test/unit/mem/mdIterator.cpp +++ b/test/unit/mem/mdIterator.cpp @@ -223,6 +223,54 @@ TEST_CASE("mdIterator host coverage", "[mem][mdIterator][iterator]") REQUIRE(collectValues(constMdSpan) == visited); } + SECTION("explicit-pitch makeMdSpan keeps padded host layout and aliases the same storage") + { + // The raw explicit-pitch overload must keep caller-provided padded pitches for indexing instead of deriving a + // packed layout from the extents. + auto const extents = alpaka::Vec{2u, 3u, 4u}; + auto const explicitPitches = alpaka::Vec{80u, 20u, 4u}; + auto const packedPitches = alpaka::calculatePitchesFromExtents(extents); + alignas(64) std::array mutableStorage{}; + mutableStorage.fill(-1); + alignas(64) std::array innerConstStorage{}; + + auto mdSpan = alpaka::makeMdSpan(mutableStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{}); + auto const outerConstMdSpan + = alpaka::makeMdSpan(mutableStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{}); + auto innerConstMdSpan + = alpaka::makeMdSpan(innerConstStorage.data(), extents, explicitPitches, alpaka::Alignment<64>{}); + + REQUIRE(mdSpan.getExtents() == extents); + REQUIRE(mdSpan.getPitches() == explicitPitches); + REQUIRE(mdSpan.getPitches() != packedPitches); + REQUIRE(mdSpan.data() == mutableStorage.data()); + STATIC_REQUIRE(std::is_same_v>); + STATIC_REQUIRE(std::is_same_v>); + STATIC_REQUIRE(std::is_same_v>); + static_assert(!std::is_const_v>); + static_assert(std::is_const_v>); + static_assert(std::is_const_v>); + + mutableStorage[5] = -50; + mutableStorage[4] = -40; + REQUIRE(mdSpan[alpaka::Vec{0u, 1u, 0u}] == -50); + + mutableStorage[20] = 220; + mutableStorage[12] = 120; + REQUIRE(mdSpan[alpaka::Vec{1u, 0u, 0u}] == 220); + + auto const sampleIdx = alpaka::Vec{1u, 2u, 3u}; + mdSpan[sampleIdx] = 1323; + REQUIRE(mutableStorage[33] == 1323); + REQUIRE(mutableStorage[23] == -1); + REQUIRE(&mdSpan[sampleIdx] == &mutableStorage[33]); + REQUIRE(&outerConstMdSpan[sampleIdx] == &mutableStorage[33]); + + auto iter = mdSpan.begin(); + ++iter; + REQUIRE(&*iter == &mutableStorage[1]); + } + SECTION("pre-increment and post-increment advance one element at a time") { // Forward-iterator increments need to preserve the old value for post-increment and return self for