[libc++][mdspan] Implement default_accessor

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
This commit is contained in:
Christian Trott 2023-07-14 11:47:59 -06:00
parent 4db6c717c4
commit 20c6b9d451
13 changed files with 434 additions and 6 deletions

View 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.

View 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

View File

@ -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

View 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>

View 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

View 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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}