On 7/23/25 13:20, Tomasz Kaminski wrote:
On Wed, Jul 23, 2025 at 11:34 AM Luc Grosheintz <luc.groshei...@gmail.com>
wrote:

This commit completes the implementation of P2897R7 by implementing and
testing the template class aligned_accessor.

         PR libstdc++/120994

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_neg.cc: New
test.
         * testsuite/23_containers/mdspan/version.cc: Add test for
         __cpp_lib_aligned_accessor.

Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
---
  libstdc++-v3/include/bits/version.def         | 10 +++
  libstdc++-v3/include/bits/version.h           | 10 +++
  libstdc++-v3/include/std/mdspan               | 68 +++++++++++++++++++
  libstdc++-v3/src/c++23/std.cc.in              |  9 ++-
  .../23_containers/mdspan/accessors/aligned.cc | 43 ++++++++++++
  .../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 | 23 +++++++
  .../testsuite/23_containers/mdspan/version.cc |  8 +++
  10 files changed, 247 insertions(+), 3 deletions(-)
  create mode 100644
libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned.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 56ad9ee9d4b..9d5122fed17 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1025,6 +1025,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 51805d292f0..6feeaa4d15a 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1149,6 +1149,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 7d17f32a836..4b2221bd070 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
@@ -1062,6 +1067,69 @@ _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");
+
+      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 think we discussed this before, and agreed on removing this check
here and in the next function, let me know if I missed some part of the
conversation.
I have checked that the test passes with them removed.
Sorry for missing that on v2.

Sorry, that's strange. This was one of the important parts of the
v1 review. I'll create v4 with only two patches 1/3 and 3/3.


+       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
+
    namespace __mdspan
    {
      template<typename _Extents, typename _IndexType, size_t _Nm>
diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
std.cc.in
index 24411476ba0..40d2fa03c5a 100644
--- a/libstdc++-v3/src/c++23/std.cc.in
+++ b/libstdc++-v3/src/c++23/std.cc.in
@@ -1858,10 +1858,13 @@ export namespace std
    using std::layout_right;
    using std::layout_stride;
    using std::default_accessor;
+#if __glibcxx_aligned_accessor
+  using std::aligned_accessor;
+#endif
    using std::mdspan;
-  // FIXME layout_left_padded, layout_right_padded, aligned_accessor,
-  // strided_slice, submdspan_mapping_result, full_extent_t, full_extent,
-  // submdspan_extents, mdsubspan
+  // FIXME layout_left_padded, layout_right_padded, strided_slice,
+  // submdspan_mapping_result, full_extent_t, full_extent,
submdspan_extents,
+  // mdsubspan
  }
  #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..f84092f6d89
--- /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_constructible_v<std::aligned_accessor<char, 4>,
+                                        std::aligned_accessor<char, 2>>);
+  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_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
@@ -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 c3350353aae..d420fe885aa 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc
@@ -82,10 +82,28 @@ template<template<typename T> typename Accessor>

  static_assert(test_properties<std::default_accessor>());

+#ifdef __glibcxx_aligned_accessor
+template<size_t Mult>
+struct OverAlignedAccessorTrait
+{
+  template<typename T>
+    using type = std::aligned_accessor<T, Mult*alignof(T)>;
+};
+
+static_assert(test_properties<OverAlignedAccessorTrait<1>::type>());
+static_assert(test_properties<OverAlignedAccessorTrait<2>::type>());
+#endif
+
  template<typename A>
    constexpr size_t
    accessor_alignment = alignof(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)
@@ -121,5 +139,10 @@ main()
  {
    test_all<std::default_accessor<double>>();
    static_assert(test_all<std::default_accessor<double>>());
+
+#ifdef __glibcxx_aligned_accessor
+  test_all<typename OverAlignedAccessorTrait<4>::type<double>>();
+  static_assert(test_all<typename
OverAlignedAccessorTrait<4>::type<double>>());
+#endif
    return 0;
  }
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/version.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/version.cc
index 752060262a0..18826005971 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/version.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/version.cc
@@ -10,3 +10,11 @@
  #elif __cplusplus > 202302L && __cpp_lib_mdspan != 202406L
  #error "Feature test macro __cpp_lib_mdspan has the wrong value for C++26"
  #endif
+
+#if __cplusplus > 202302L
+#ifndef __cpp_lib_aligned_accessor
+#error "Feature test macro __cpp_lib_aligned_accessor is missing for
<mdspan>"
+#elif __cpp_lib_aligned_accessor != 202411L
+#error "Feature test macro __cpp_lib_aligned_accessor has the wrong value"
+#endif
+#endif
--
2.50.0




Reply via email to