[libc++][mdspan] Implement default_accessor
authorChristian Trott <crtrott@sandia.gov>
Fri, 14 Jul 2023 17:47:59 +0000 (11:47 -0600)
committerChristian Trott <crtrott@sandia.gov>
Fri, 14 Jul 2023 17:52:40 +0000 (11:52 -0600)
This commit implements default_accessor in support of C++23 mdspan
(https://wg21.link/p0009). default_accessor is the trivial accessor
using plain pointers and reference to element types.

Co-authored-by: Damien L-G <dalg24@gmail.com>
Differential Revision: https://reviews.llvm.org/D153935

13 files changed:
libcxx/docs/Status/Cxx23.rst
libcxx/include/CMakeLists.txt
libcxx/include/__mdspan/default_accessor.h [new file with mode: 0644]
libcxx/include/mdspan
libcxx/include/module.modulemap.in
libcxx/modules/std/mdspan.cppm
libcxx/test/std/containers/views/mdspan/MinimalElementType.h [new file with mode: 0644]
libcxx/test/std/containers/views/mdspan/default_accessor/access.pass.cpp [new file with mode: 0644]
libcxx/test/std/containers/views/mdspan/default_accessor/ctor.conversion.pass.cpp [new file with mode: 0644]
libcxx/test/std/containers/views/mdspan/default_accessor/ctor.default.pass.cpp [new file with mode: 0644]
libcxx/test/std/containers/views/mdspan/default_accessor/element_type.verify.cpp [new file with mode: 0644]
libcxx/test/std/containers/views/mdspan/default_accessor/offset.pass.cpp [new file with mode: 0644]
libcxx/test/std/containers/views/mdspan/default_accessor/types.pass.cpp [new file with mode: 0644]

index c1c8170..6e60273 100644 (file)
@@ -40,7 +40,7 @@ Paper Status
 
 .. note::
 
-   .. [#note-P0009R18] P0009R18: ``extents``, ``dextents``, ``layout_left``, and ``layout_right`` are implemented.
+   .. [#note-P0009R18] P0009R18: ``extents``, ``dextents``, ``layout_left``, ``layout_right``, and ``default_accessor`` are implemented.
    .. [#note-P0533R9] P0533R9: ``isfinite``, ``isinf``, ``isnan`` and ``isnormal`` are implemented.
    .. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but
       clang doesn't issue a diagnostic for deprecated using template declarations.
index 12cb122..640de9a 100644 (file)
@@ -478,6 +478,7 @@ set(files
   __locale_dir/locale_base_api/bsd_locale_fallbacks.h
   __locale_dir/locale_base_api/locale_guard.h
   __mbstate_t.h
+  __mdspan/default_accessor.h
   __mdspan/extents.h
   __mdspan/layout_left.h
   __mdspan/layout_right.h
diff --git a/libcxx/include/__mdspan/default_accessor.h b/libcxx/include/__mdspan/default_accessor.h
new file mode 100644 (file)
index 0000000..1cc5f15
--- /dev/null
@@ -0,0 +1,66 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//                        Kokkos v. 4.0
+//       Copyright (2022) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___MDSPAN_DEFAULT_ACCESSOR_H
+#define _LIBCPP___MDSPAN_DEFAULT_ACCESSOR_H
+
+#include <__config>
+#include <__type_traits/is_abstract.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/remove_const.h>
+#include <cinttypes>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <class _ElementType>
+struct default_accessor {
+  static_assert(!is_array_v<_ElementType>, "default_accessor: template argument may not be an array type");
+  static_assert(!is_abstract_v<_ElementType>, "default_accessor: template argument may not be an abstract class");
+
+  using offset_policy    = default_accessor;
+  using element_type     = _ElementType;
+  using reference        = _ElementType&;
+  using data_handle_type = _ElementType*;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr default_accessor() noexcept = default;
+  template <class _OtherElementType>
+    requires(is_convertible_v<_OtherElementType (*)[], element_type (*)[]>)
+  _LIBCPP_HIDE_FROM_ABI constexpr default_accessor(default_accessor<_OtherElementType>) noexcept {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr reference access(data_handle_type __p, size_t __i) const noexcept { return __p[__i]; }
+  _LIBCPP_HIDE_FROM_ABI constexpr data_handle_type offset(data_handle_type __p, size_t __i) const noexcept {
+    return __p + __i;
+  }
+};
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MDSPAN_DEFAULT_ACCESSOR_H
index 9116c85..701def5 100644 (file)
@@ -27,7 +27,7 @@ namespace std {
 
   // [mdspan.accessor.default], class template default_accessor
   template<class ElementType>
-    class default_accessor; // not implemented yet
+    class default_accessor;
 
   // [mdspan.mdspan], class template mdspan
   template<class ElementType, class Extents, class LayoutPolicy = layout_right,
@@ -190,6 +190,24 @@ namespace std {
   };
 }
 
+// default_accessor synopsis
+
+namespace std {
+  template<class ElementType>
+  struct default_accessor {
+    using offset_policy = default_accessor;
+    using element_type = ElementType;
+    using reference = ElementType&;
+    using data_handle_type = ElementType*;
+
+    constexpr default_accessor() noexcept = default;
+    template<class OtherElementType>
+      constexpr default_accessor(default_accessor<OtherElementType>) noexcept;
+    constexpr reference access(data_handle_type p, size_t i) const noexcept;
+    constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept;
+  };
+}
+
 */
 
 #ifndef _LIBCPP_MDSPAN
@@ -197,6 +215,7 @@ namespace std {
 
 #include <__config>
 #include <__fwd/mdspan.h>
+#include <__mdspan/default_accessor.h>
 #include <__mdspan/extents.h>
 #include <__mdspan/layout_left.h>
 #include <__mdspan/layout_right.h>
index 395e6fa..19db0c8 100644 (file)
@@ -1239,6 +1239,7 @@ module std [system] {
     export *
 
     module __mdspan {
+      module default_accessor                { private header "__mdspan/default_accessor.h" }
       module extents                         {
         private header "__mdspan/extents.h"
         export array
index eef6097..5023dfb 100644 (file)
@@ -23,11 +23,9 @@ export namespace std {
   using std::layout_right;
   // using std::layout_stride;
 
-#if 0
-  // [mdspan.accessor.default], class template default_­accessor
+  // [mdspan.accessor.default], class template default_accessor
   using std::default_accessor;
 
   // [mdspan.mdspan], class template mdspan
-  using std::mdspan;
-#endif
+  // using std::mdspan;
 } // namespace std
diff --git a/libcxx/test/std/containers/views/mdspan/MinimalElementType.h b/libcxx/test/std/containers/views/mdspan/MinimalElementType.h
new file mode 100644 (file)
index 0000000..b1fbd6e
--- /dev/null
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_MINIMAL_ELEMENT_TYPE_H
+#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_MINIMAL_ELEMENT_TYPE_H
+
+#include<memory>
+
+// Idiosyncratic element type for mdspan
+// Make sure we don't assume copyable, default constructible, movable etc.
+struct MinimalElementType {
+  int val;
+  constexpr MinimalElementType() = delete;
+  constexpr MinimalElementType(const MinimalElementType&) = delete;
+  constexpr explicit MinimalElementType(int v) noexcept : val(v){}
+  constexpr MinimalElementType& operator=(const MinimalElementType&) = delete;
+};
+
+// Helper class to create pointer to MinimalElementType
+template<class T, size_t N>
+struct ElementPool {
+  constexpr ElementPool() {
+    ptr_ = std::allocator<T>().allocate(N);
+    for (int i = 0; i != N; ++i)
+      std::construct_at(ptr_ + i, 42);
+  }
+
+  constexpr T* get_ptr() { return ptr_; }
+
+  constexpr ~ElementPool() {
+    for (int i = 0; i != N; ++i)
+      std::destroy_at(ptr_ + i);
+    std::allocator<T>().deallocate(ptr_, N);
+  }
+
+private:
+  T* ptr_;
+};
+
+#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_MINIMAL_ELEMENT_TYPE_H
diff --git a/libcxx/test/std/containers/views/mdspan/default_accessor/access.pass.cpp b/libcxx/test/std/containers/views/mdspan/default_accessor/access.pass.cpp
new file mode 100644 (file)
index 0000000..a15ad68
--- /dev/null
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// constexpr reference access(data_handle_type p, size_t i) const noexcept;
+//
+// Effects: Equivalent to: return p[i];
+
+#include <mdspan>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+
+template <class T>
+constexpr void test_access() {
+  ElementPool<std::remove_const_t<T>, 10> data;
+  T* ptr = data.get_ptr();
+  std::default_accessor<T> acc;
+  for(int i = 0; i < 10; i++) {
+    static_assert(std::is_same_v<decltype(acc.access(ptr, i)), typename std::default_accessor<T>::reference>);
+    ASSERT_NOEXCEPT(acc.access(ptr, i));
+    assert(&acc.access(ptr, i) == ptr + i);
+  }
+}
+
+constexpr bool test() {
+  test_access<int>();
+  test_access<const int>();
+  test_access<MinimalElementType>();
+  test_access<const MinimalElementType>();
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/default_accessor/ctor.conversion.pass.cpp b/libcxx/test/std/containers/views/mdspan/default_accessor/ctor.conversion.pass.cpp
new file mode 100644 (file)
index 0000000..299d98f
--- /dev/null
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+//
+// Test converting constructor:
+//
+// template<class OtherElementType>
+//   constexpr default_accessor(default_accessor<OtherElementType>) noexcept {}
+//
+// Constraints: is_convertible_v<OtherElementType(*)[], element_type(*)[]> is true.
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+
+struct Base {};
+struct Derived: public Base {};
+
+template <class FromT, class ToT>
+constexpr void test_conversion() {
+  std::default_accessor<FromT> acc_from;
+  ASSERT_NOEXCEPT(std::default_accessor<ToT>(acc_from));
+  [[maybe_unused]] std::default_accessor<ToT> acc_to(acc_from);
+}
+
+constexpr bool test() {
+  // default accessor conversion largely behaves like pointer conversion
+  test_conversion<int, int>();
+  test_conversion<int, const int>();
+  test_conversion<const int, const int>();
+  test_conversion<MinimalElementType, MinimalElementType>();
+  test_conversion<MinimalElementType, const MinimalElementType>();
+  test_conversion<const MinimalElementType, const MinimalElementType>();
+
+  // char is convertible to int, but accessors are not
+  static_assert(!std::is_constructible_v<std::default_accessor<int>, std::default_accessor<char>>);
+  // don't allow conversion from const elements to non-const
+  static_assert(!std::is_constructible_v<std::default_accessor<int>, std::default_accessor<const int>>);
+  // MinimalElementType is constructible from int, but accessors should not be convertible
+  static_assert(!std::is_constructible_v<std::default_accessor<MinimalElementType>, std::default_accessor<int>>);
+  // don't allow conversion from const elements to non-const
+  static_assert(!std::is_constructible_v<std::default_accessor<MinimalElementType>, std::default_accessor<const MinimalElementType>>);
+  // don't allow conversion from Base to Derived
+  static_assert(!std::is_constructible_v<std::default_accessor<Derived>, std::default_accessor<Base>>);
+  // don't allow conversion from Derived to Base
+  static_assert(!std::is_constructible_v<std::default_accessor<Base>, std::default_accessor<Derived>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/default_accessor/ctor.default.pass.cpp b/libcxx/test/std/containers/views/mdspan/default_accessor/ctor.default.pass.cpp
new file mode 100644 (file)
index 0000000..28bd7c0
--- /dev/null
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// Test default construction:
+//
+// constexpr default_accessor() noexcept = default;
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+
+template <class T>
+constexpr void test_construction() {
+  ASSERT_NOEXCEPT(std::default_accessor<T>{});
+  [[maybe_unused]] std::default_accessor<T> acc;
+  static_assert(std::is_trivially_default_constructible_v<std::default_accessor<T>>);
+}
+
+constexpr bool test() {
+  test_construction<int>();
+  test_construction<const int>();
+  test_construction<MinimalElementType>();
+  test_construction<const MinimalElementType>();
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/default_accessor/element_type.verify.cpp b/libcxx/test/std/containers/views/mdspan/default_accessor/element_type.verify.cpp
new file mode 100644 (file)
index 0000000..d62171e
--- /dev/null
@@ -0,0 +1,33 @@
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class ElementType>
+// class default_accessor;
+
+// ElementType is required to be a complete object type that is neither an abstract class type nor an array type.
+
+#include <mdspan>
+
+class AbstractClass {
+public:
+  virtual void method() = 0;
+};
+
+void not_abstract_class() {
+  // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}default_accessor: template argument may not be an abstract class}}
+  [[maybe_unused]] std::default_accessor<AbstractClass> acc;
+}
+
+void not_array_type() {
+  // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}default_accessor: template argument may not be an array type}}
+  [[maybe_unused]] std::default_accessor<int[5]> acc;
+}
+
diff --git a/libcxx/test/std/containers/views/mdspan/default_accessor/offset.pass.cpp b/libcxx/test/std/containers/views/mdspan/default_accessor/offset.pass.cpp
new file mode 100644 (file)
index 0000000..5842ff9
--- /dev/null
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept;
+//
+// Effects: Equivalent to: return p+i;
+
+#include <mdspan>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+
+template <class T>
+constexpr void test_offset() {
+  ElementPool<std::remove_const_t<T>, 10> data;
+  T* ptr = data.get_ptr();
+  std::default_accessor<T> acc;
+  for(int i = 0; i < 10; i++) {
+    static_assert(std::is_same_v<decltype(acc.offset(ptr, i)), typename std::default_accessor<T>::data_handle_type>);
+    ASSERT_NOEXCEPT(acc.offset(ptr, i));
+    assert(acc.offset(ptr, i) == ptr + i);
+  }
+}
+
+constexpr bool test() {
+  test_offset<int>();
+  test_offset<const int>();
+  test_offset<MinimalElementType>();
+  test_offset<const MinimalElementType>();
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/default_accessor/types.pass.cpp b/libcxx/test/std/containers/views/mdspan/default_accessor/types.pass.cpp
new file mode 100644 (file)
index 0000000..025b992
--- /dev/null
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+//  template<class ElementType>
+//  struct default_accessor {
+//    using offset_policy = default_accessor;
+//    using element_type = ElementType;
+//    using reference = ElementType&;
+//    using data_handle_type = ElementType*;
+//    ...
+//  };
+//
+//  Each specialization of default_accessor is a trivially copyable type that models semiregular.
+
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+
+template <class T>
+void test() {
+  using A = std::default_accessor<T>;
+  ASSERT_SAME_TYPE(typename A::offset_policy, A);
+  ASSERT_SAME_TYPE(typename A::element_type, T);
+  ASSERT_SAME_TYPE(typename A::reference, T&);
+  ASSERT_SAME_TYPE(typename A::data_handle_type, T*);
+
+  static_assert(std::semiregular<A>);
+  static_assert(std::is_trivially_copyable_v<A>);
+
+  LIBCPP_STATIC_ASSERT(std::is_empty_v<A>);
+}
+
+int main(int, char**) {
+  test<int>();
+  test<const int>();
+  test<MinimalElementType>();
+  test<const MinimalElementType>();
+  return 0;
+}