libstdc++: Check prerequisites of layout_*::operator().

Previously, the prerequisite that the arguments passed to operator() are
a multi-dimensional index (of extents()) was not checked.

Both mapping::operator() and mdspan::operator[] have the same
prerequisite. Since, mdspan must check the prerequisite for user-defined
layout mappings, the preference is to check in mdspan.

Because out-of-bounds accesses are very common it's nevertheless useful
to check the prerequisite in mapping::operator(). This is relevant for
cases where the layout mappings are used without mdspan. This commit
checks the prerequisites via _GLIBCXX_DEBUG_ASSERTs and adds the required
tests.

More discussion in the email chain starting at:

  https://gcc.gnu.org/pipermail/libstdc++/2025-July/062265.html

libstdc++-v3/ChangeLog:

	* include/std/mdspan: Check prerequisites of
	layout_*::operator() with _GLIBCXX_DEBUG_ASSERTs.
	* testsuite/23_containers/mdspan/layouts/debug/out_of_bounds_neg.cc:
	Add tests for prerequisites.

Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com>
Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
This commit is contained in:
Luc Grosheintz
2025-07-08 13:43:47 +02:00
committed by Tomasz Kamiński
parent 90f7df6194
commit 4d86e4cda0
2 changed files with 35 additions and 0 deletions
+5
View File
@@ -441,6 +441,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_IndexType __mult = 1;
auto __update = [&, __pos = 0u](_IndexType __idx) mutable
{
_GLIBCXX_DEBUG_ASSERT(cmp_less(__idx, __exts.extent(__pos)));
__res += __idx * __mult;
__mult *= __exts.extent(__pos);
++__pos;
@@ -651,6 +652,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
auto __update = [&, __pos = __exts.rank()](_IndexType) mutable
{
--__pos;
_GLIBCXX_DEBUG_ASSERT(cmp_less(__ind_arr[__pos],
__exts.extent(__pos)));
__res += __ind_arr[__pos] * __mult;
__mult *= __exts.extent(__pos);
};
@@ -822,6 +825,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
auto __update = [&, __pos = 0u](_IndexType __idx) mutable
{
_GLIBCXX_DEBUG_ASSERT(cmp_less(__idx,
__m.extents().extent(__pos)));
__res += __idx * __m.stride(__pos++);
};
(__update(__indices), ...);
@@ -0,0 +1,30 @@
// { dg-do compile { target c++23 } }
// { dg-require-debug-mode "" }
#include<mdspan>
template<typename Layout>
constexpr bool
test_out_of_bounds_1d()
{
auto m = typename Layout::mapping<std::extents<int, 0>>{};
(void) m(0); // { dg-error "expansion of" }
return true;
}
static_assert(test_out_of_bounds_1d<std::layout_left>()); // { dg-error "expansion of" }
static_assert(test_out_of_bounds_1d<std::layout_right>()); // { dg-error "expansion of" }
static_assert(test_out_of_bounds_1d<std::layout_stride>()); // { dg-error "expansion of" }
template<typename Layout>
constexpr bool
test_out_of_bounds_3d()
{
auto m = typename Layout::mapping<std::extents<int, 3, 5, 7>>{};
(void) m(2, 5, 5); // { dg-error "expansion of" }
return true;
}
static_assert(test_out_of_bounds_3d<std::layout_left>()); // { dg-error "expansion of" }
static_assert(test_out_of_bounds_3d<std::layout_right>()); // { dg-error "expansion of" }
static_assert(test_out_of_bounds_3d<std::layout_stride>()); // { dg-error "expansion of" }
// { dg-prune-output "non-constant condition for static assertion" }
// { dg-prune-output "__glibcxx_assert" }