On Thu, Jul 17, 2025 at 11:57 AM Luc Grosheintz <luc.groshei...@gmail.com> wrote:
> > > On 7/8/25 17:37, Tomasz Kaminski wrote: > > On Thu, Jul 3, 2025 at 12:38 PM Luc Grosheintz <luc.groshei...@gmail.com > > > > wrote: > > > >> This commit completes the implementation of P2897R7 by implementing and > >> testing the template class aligned_accessor. > >> > >> libstdc++-v3/ChangeLog: > >> > >> * include/bits/version.def (aligned_accessor): Add. > >> * include/bits/version.h: Regenerate. > >> * include/std/mdspan (aligned_accessor): New class. > >> * src/c++23/std.cc.in (aligned_accessor): Add. > >> * testsuite/23_containers/mdspan/accessors/generic.cc: Add > tests > >> for aligned_accessor. > >> * testsuite/23_containers/mdspan/accessors/aligned.cc: New > test. > >> * testsuite/23_containers/mdspan/accessors/aligned_ftm.cc: New > >> test. > >> * testsuite/23_containers/mdspan/accessors/aligned_neg.cc: New > >> test. > >> --- > >> libstdc++-v3/include/bits/version.def | 10 +++ > >> libstdc++-v3/include/bits/version.h | 10 +++ > >> libstdc++-v3/include/std/mdspan | 72 +++++++++++++++++++ > >> libstdc++-v3/src/c++23/std.cc.in | 3 +- > >> .../23_containers/mdspan/accessors/aligned.cc | 43 +++++++++++ > >> .../mdspan/accessors/aligned_ftm.cc | 6 ++ > >> .../mdspan/accessors/aligned_neg.cc | 33 +++++++++ > >> .../accessors/debug/aligned_access_neg.cc | 23 ++++++ > >> .../accessors/debug/aligned_offset_neg.cc | 23 ++++++ > >> .../23_containers/mdspan/accessors/generic.cc | 27 +++++++ > >> 10 files changed, 249 insertions(+), 1 deletion(-) > >> create mode 100644 > >> libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned.cc > >> create mode 100644 > >> libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_ftm.cc > >> create mode 100644 > >> libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc > >> create mode 100644 > >> > libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc > >> create mode 100644 > >> > libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc > >> > >> diff --git a/libstdc++-v3/include/bits/version.def > >> b/libstdc++-v3/include/bits/version.def > >> index a2695e67716..42445165b91 100644 > >> --- a/libstdc++-v3/include/bits/version.def > >> +++ b/libstdc++-v3/include/bits/version.def > >> @@ -1022,6 +1022,16 @@ ftms = { > >> }; > >> }; > >> > >> +ftms = { > >> + name = aligned_accessor; > >> + values = { > >> + v = 202411; > >> + cxxmin = 26; > >> + extra_cond = "__glibcxx_assume_aligned " > >> + "&& __glibcxx_is_sufficiently_aligned"; > >> + }; > >> +}; > >> + > >> ftms = { > >> name = ssize; > >> values = { > >> diff --git a/libstdc++-v3/include/bits/version.h > >> b/libstdc++-v3/include/bits/version.h > >> index 1b17a965239..3efa7b1baae 100644 > >> --- a/libstdc++-v3/include/bits/version.h > >> +++ b/libstdc++-v3/include/bits/version.h > >> @@ -1143,6 +1143,16 @@ > >> #endif /* !defined(__cpp_lib_mdspan) && > defined(__glibcxx_want_mdspan) */ > >> #undef __glibcxx_want_mdspan > >> > >> +#if !defined(__cpp_lib_aligned_accessor) > >> +# if (__cplusplus > 202302L) && (__glibcxx_assume_aligned && > >> __glibcxx_is_sufficiently_aligned) > >> +# define __glibcxx_aligned_accessor 202411L > >> +# if defined(__glibcxx_want_all) || > >> defined(__glibcxx_want_aligned_accessor) > >> +# define __cpp_lib_aligned_accessor 202411L > >> +# endif > >> +# endif > >> +#endif /* !defined(__cpp_lib_aligned_accessor) && > >> defined(__glibcxx_want_aligned_accessor) */ > >> +#undef __glibcxx_want_aligned_accessor > >> + > >> #if !defined(__cpp_lib_ssize) > >> # if (__cplusplus >= 202002L) > >> # define __glibcxx_ssize 201902L > >> diff --git a/libstdc++-v3/include/std/mdspan > >> b/libstdc++-v3/include/std/mdspan > >> index c72a64094b7..6eb804bf9a0 100644 > >> --- a/libstdc++-v3/include/std/mdspan > >> +++ b/libstdc++-v3/include/std/mdspan > >> @@ -39,7 +39,12 @@ > >> #include <limits> > >> #include <utility> > >> > >> +#if __cplusplus > 202302L > >> +#include <bits/align.h> > >> +#endif > >> + > >> #define __glibcxx_want_mdspan > >> +#define __glibcxx_want_aligned_accessor > >> #include <bits/version.h> > >> > >> #ifdef __glibcxx_mdspan > >> @@ -1035,6 +1040,73 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> { return __p + __i; } > >> }; > >> > >> +#ifdef __glibcxx_aligned_accessor > >> + template<typename _ElementType, size_t _ByteAlignment> > >> + struct aligned_accessor > >> + { > >> + static_assert(has_single_bit(_ByteAlignment), > >> + "ByteAlignment must be a power of two"); > >> + static_assert(_ByteAlignment >= alignof(_ElementType), > >> + "ByteAlignment is too small for ElementType"); > >> + static_assert(!is_array_v<_ElementType>, > >> + "ElementType must not be an array type"); > >> + static_assert(!is_abstract_v<_ElementType>, > >> + "ElementType must not be an abstract class type"); > >> + > >> + using offset_policy = default_accessor<_ElementType>; > >> + using element_type = _ElementType; > >> + using reference = element_type&; > >> + using data_handle_type = element_type*; > >> + > >> + static constexpr size_t byte_alignment = _ByteAlignment; > >> + > >> + constexpr > >> + aligned_accessor() noexcept = default; > >> + > >> + template<typename _OElementType, size_t _OByteAlignment> > >> + requires (is_convertible_v<_OElementType(*)[], > element_type(*)[]> > >> + && _OByteAlignment >= byte_alignment) > >> + constexpr > >> + aligned_accessor(aligned_accessor<_OElementType, > _OByteAlignment>) > >> + noexcept > >> + { } > >> + > >> + template<typename _OElementType> > >> + requires is_convertible_v<_OElementType(*)[], element_type(*)[]> > >> + constexpr explicit > >> + aligned_accessor(default_accessor<_OElementType>) noexcept > >> + { } > >> + > >> + template<typename _OElementType> > >> + requires is_convertible_v<_OElementType(*)[], element_type(*)[]> > >> + constexpr > >> + operator default_accessor<_OElementType>() const noexcept > >> + { return {}; } > >> + > >> + constexpr reference > >> + access(data_handle_type __p, size_t __i) const noexcept > >> + { > >> + if !consteval > >> + { > >> + _GLIBCXX_DEBUG_ASSERT( > >> + std::is_sufficiently_aligned<_ByteAlignment>(__p)); > >> + } > >> > > I would not add this check, as the purpose of this accessor is to assume > > aligned, > > and that should be checked during mdspan construction, however we do not > > currently have a > > way to do so. > > Such a check is currently performed anyway by assume_aligned, and in my > > opinion should stay there. > > Correct, I'll update the patch accordingly. > > > > > > >> + return std::assume_aligned<byte_alignment>(__p)[__i]; > >> + } > >> + > >> + constexpr typename offset_policy::data_handle_type > >> + offset(data_handle_type __p, size_t __i) const noexcept > >> + { > >> + if !consteval > >> + { > >> + _GLIBCXX_DEBUG_ASSERT( > >> + std::is_sufficiently_aligned<_ByteAlignment>(__p)); > >> + } > >> + return std::assume_aligned<byte_alignment>(__p) + __i; > >> + } > >> + }; > >> +#endif > >> + > >> _GLIBCXX_END_NAMESPACE_VERSION > >> } > >> #endif > >> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/ > >> std.cc.in > >> index 6f4214ed3a7..02e1713bdc3 100644 > >> --- a/libstdc++-v3/src/c++23/std.cc.in > >> +++ b/libstdc++-v3/src/c++23/std.cc.in > >> @@ -1851,7 +1851,8 @@ export namespace std > >> using std::layout_right; > >> using std::layout_stride; > >> using std::default_accessor; > >> - // FIXME layout_left_padded, layout_right_padded, aligned_accessor > and > >> mdspan > >> + using std::aligned_accessor; > >> + // FIXME layout_left_padded, layout_right_padded and mdspan > >> } > >> #endif > >> > >> diff --git > >> a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned.cc > >> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned.cc > >> new file mode 100644 > >> index 00000000000..75581d3eb70 > >> --- /dev/null > >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned.cc > >> @@ -0,0 +1,43 @@ > >> +// { dg-do compile { target c++26 } } > >> +#include <mdspan> > >> + > >> +#include <testsuite_hooks.h> > >> + > >> +constexpr bool > >> +test_from_other() > >> +{ > >> + std::aligned_accessor<double, 4*sizeof(double)> a4; > >> + [[maybe_unused]] std::aligned_accessor<double, 2*sizeof(double)> > a2(a4); > >> + > static_assert(std::is_nothrow_convertible_v<std::aligned_accessor<char, > >> 4>, > >> + > std::aligned_accessor<char, > >> 2>>); > >> + static_assert(!std::is_convertible_v<std::aligned_accessor<char, 2>, > >> + std::aligned_accessor<char, 4>>); > >> > > I would check !is_cosntructible, to indicate that there is no explicit > > constructor. > > > >> + return true; > >> +} > >> +static_assert(test_from_other()); > >> + > >> +constexpr bool > >> +test_from_default() > >> +{ > >> + std::default_accessor<double> ad; > >> + [[maybe_unused]] std::aligned_accessor<double, 4*sizeof(double)> > a4(ad); > >> + static_assert(!std::is_convertible_v<std::default_accessor<char>, > >> + std::aligned_accessor<char, 1>>); > >> + static_assert(!std::is_convertible_v<std::default_accessor<char>, > >> + std::aligned_accessor<char, 2>>); > >> + static_assert(std::is_nothrow_constructible_v< > >> + std::aligned_accessor<char, 4>, std::default_accessor<char>>); > >> + return true; > >> +} > >> +static_assert(test_from_default()); > >> + > >> +constexpr bool > >> +test_to_default() > >> +{ > >> + std::aligned_accessor<double, 4*sizeof(double)> a4; > >> + [[maybe_unused]] std::default_accessor<double> ad = a4; > >> + > static_assert(std::is_nothrow_convertible_v<std::aligned_accessor<char, > >> 2>, > >> + > >> std::default_accessor<char>>); > >> + return true; > >> +} > >> +static_assert(test_to_default()); > >> diff --git > >> a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_ftm.cc > >> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_ftm.cc > >> new file mode 100644 > >> index 00000000000..361bf750c33 > >> --- /dev/null > >> +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_ftm.cc > >> @@ -0,0 +1,6 @@ > >> +// { dg-do compile { target c++26 } } > >> +#include <mdspan> > >> + > >> +#ifndef __cpp_lib_aligned_accessor > >> +#error "Missing FTM" > >> > > Updated how this test is performed. And I would rename this file to > version. > > > >> +#endif > >> diff --git > >> a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc > >> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc > >> new file mode 100644 > >> index 00000000000..46d80bf4083 > >> --- /dev/null > >> +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc > >> @@ -0,0 +1,33 @@ > >> +// { dg-do compile { target c++26 } } > >> +#include<mdspan> > >> + > >> +#include <cstdint> > >> + > >> +std::aligned_accessor<uint32_t, 0> a; // { dg-error "required > >> from here" } > >> +std::aligned_accessor<uint32_t, 7> b; // { dg-error "required > >> from here" } > >> +std::aligned_accessor<uint32_t, size_t(-1)> c; // { dg-error "required > >> from here" } > >> + > >> +std::aligned_accessor<uint32_t, 2> d; // { dg-error "required > >> from here" } > >> + > >> +std::aligned_accessor<int[2], 32> e; // { dg-error "required > >> from here" } > >> + > >> +class Abstract > >> +{ > >> + virtual void > >> + foo() const = 0; > >> +}; > >> + > >> +class Derived : public Abstract > >> +{ > >> + void > >> + foo() const override > >> + { } > >> +}; > >> + > >> +std::aligned_accessor<Derived, alignof(int)> f_ok; > >> +std::aligned_accessor<Abstract, alignof(int)> f_err; // { dg-error > >> "required from here" } > >> + > >> +// { dg-prune-output "ByteAlignment must be a power of two" } > >> +// { dg-prune-output "ByteAlignment is too small for ElementType" } > >> +// { dg-prune-output "ElementType must not be an array type" } > >> +// { dg-prune-output "ElementType must not be an abstract" } > >> diff --git > >> > a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc > >> > b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc > >> new file mode 100644 > >> index 00000000000..3511cef1c3a > >> --- /dev/null > >> +++ > >> > b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc > >> > > Again, I feel like these negative checks belong to assume_alinged. > > I fully agree that offset and access can rely on assume_aligned > to do the check. > > However, testing that the two methods fail for non-aligned input seems > prudent. We're testing a property of offset and access. They happen to > rely on assume_aligned to perform the check, but that's a detail. We're > not testing how it does the check, only that the check happens. > > Since it might make the tests too brittle, feel free to insist. > The standard just says that this is UB, so we are testing something that is not required to pass by the standard. This is why I am a bit hesitant on death tests like that. Jonathan, do you have an opinion on this? > > > > >> @@ -0,0 +1,23 @@ > >> +// { dg-do run { target c++26 xfail *-*-* } } > >> +// { dg-require-debug-mode "" } > >> + > >> +#include <mdspan> > >> +#include <array> > >> + > >> +void > >> +test_unaligned_access() > >> +{ > >> + constexpr size_t N = 4; > >> + alignas(N) std::array<char, 128> buffer{}; > >> + auto* unaligned = buffer.data() + 1; > >> + auto a = std::aligned_accessor<char, N>{}; > >> + > >> + [[maybe_unused]] char x = a.access(unaligned, 0); > >> +} > >> + > >> +int > >> +main() > >> +{ > >> + test_unaligned_access(); > >> + return 0; > >> +}; > >> diff --git > >> > a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc > >> > b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc > >> new file mode 100644 > >> index 00000000000..319da5ffef3 > >> --- /dev/null > >> +++ > >> > b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc > >> @@ -0,0 +1,23 @@ > >> +// { dg-do run { target c++26 xfail *-*-* } } > >> +// { dg-require-debug-mode "" } > >> + > >> +#include <mdspan> > >> +#include <array> > >> + > >> +void > >> +test_unaligned_offset() > >> +{ > >> + constexpr size_t N = 4; > >> + alignas(N) std::array<char, 128> buffer{}; > >> + auto* unaligned = buffer.data() + 1; > >> + auto a = std::aligned_accessor<char, N>{}; > >> + > >> + [[maybe_unused]] char* x = a.offset(unaligned, 0); > >> +} > >> + > >> +int > >> +main() > >> +{ > >> + test_unaligned_offset(); > >> + return 0; > >> +}; > >> diff --git > >> a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc > >> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc > >> index 600d152b690..f97946bd276 100644 > >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc > >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc > >> @@ -98,10 +98,32 @@ > >> static_assert(test_class_properties<std::default_accessor<double>>()); > >> static_assert(test_accessor_policy<std::default_accessor<double>>()); > >> static_assert(test_ctor<DefaultAccessorTrait>()); > >> > >> +#ifdef __glibcxx_aligned_accessor > >> +struct AlignedAccessorTrait > >> +{ > >> + template<typename T> > >> + using type = std::aligned_accessor<T, alignof(T)>; > >> +}; > >> + > >> +static_assert(test_class_properties<std::aligned_accessor<double, > >> + > >> sizeof(double)>>()); > >> +static_assert(test_accessor_policy<std::aligned_accessor<double, > >> + > >> sizeof(double)>>()); > >> +static_assert(test_accessor_policy<std::aligned_accessor<double, > >> + > >> 2*sizeof(double)>>()); > >> +static_assert(test_ctor<AlignedAccessorTrait>()); > >> +#endif > >> + > >> template<typename A> > >> constexpr size_t > >> accessor_alignment = sizeof(typename A::element_type); > >> > >> +#ifdef __glibcxx_aligned_accessor > >> +template<typename T, size_t N> > >> + constexpr size_t > >> + accessor_alignment<std::aligned_accessor<T, N>> = N; > >> +#endif > >> + > >> template<typename Accessor> > >> constexpr void > >> test_access(Accessor accessor) > >> @@ -137,5 +159,10 @@ main() > >> { > >> test_all<std::default_accessor<double>>(); > >> static_assert(test_all<std::default_accessor<double>>()); > >> + > >> +#ifdef __glibcxx_aligned_accessor > >> + test_all<std::aligned_accessor<double, 4*sizeof(double)>>(); > >> + static_assert(test_all<std::aligned_accessor<double, > >> 4*sizeof(double)>>()); > >> +#endif > >> return 0; > >> } > >> -- > >> 2.49.0 > >> > >> > > > >