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));
+         }
+       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>>);
+  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"
+#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
@@ -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

Reply via email to