[libcxx-commits] [libcxx] Improvements to std::array<T, 0>. (PR #74657)

Ryan Nicholl via libcxx-commits libcxx-commits at lists.llvm.org
Wed Dec 6 13:57:44 PST 2023


https://github.com/rnicholl-google created https://github.com/llvm/llvm-project/pull/74657

In the unstable ABI, std::array<T, 0> now uses only 1 byte with empty base optimization. In the stable ABI, it now can be used in constexpr context like std::arary<int, 0> a; a.fill(1); return a;

Fixes llvm-project/llvm#74375

>From 51e7c79ee94a2bd0a5661a0b6de6fa680a2c1e00 Mon Sep 17 00:00:00 2001
From: "Ryan P. Nicholl" <rnicholl at google.com>
Date: Wed, 6 Dec 2023 16:32:27 -0500
Subject: [PATCH] Improvements to std::array<T, 0>.\n\nIn the unstable ABI,
 std::array<T, 0> now uses only 1 byte with empty base optimization. In the
 stable ABI, it now can be used in constexpr context like std::arary<int, 0>
 a; a.fill(1); return a;

---
 libcxx/include/__config                           |  4 ++++
 libcxx/include/array                              |  9 ++++++++-
 .../default_initializable.compile.pass.cpp        |  1 -
 .../array/array.cons/implicit_copy.pass.cpp       | 15 ---------------
 .../sequences/array/size_and_alignment.pass.cpp   | 15 +++++++++++++--
 .../views/mdspan/extents/CtorTestCombinations.h   |  2 +-
 6 files changed, 26 insertions(+), 20 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 51eb484297f6b..9d256aeb222fa 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -168,6 +168,10 @@
 // pointer from 16 to 8. This changes the output of std::string::max_size,
 // which makes it ABI breaking
 #    define _LIBCPP_ABI_STRING_8_BYTE_ALIGNMENT
+// Make std::array<T, N> where N == 0 have no data
+// This allows it to be used in constexpr contexts without
+// introducing runtime overhead
+#    define _LIBCPP_ABI_ZERO_SIZE_ARRAY_HAS_NO_DATA
 #  elif _LIBCPP_ABI_VERSION == 1
 #    if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
 // Enable compiling copies of now inline methods into the dylib to support
diff --git a/libcxx/include/array b/libcxx/include/array
index c01d13ef358a5..d4df7cd302b99 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -263,8 +263,13 @@ struct _LIBCPP_TEMPLATE_VIS array
     const value_type* data() const _NOEXCEPT {return __elems_;}
 };
 
+struct _EmptyAggregateType {};
+
 template <class _Tp>
 struct _LIBCPP_TEMPLATE_VIS array<_Tp, 0>
+#ifdef _LIBCPP_ABI_ZERO_SIZE_ARRAY_HAS_NO_DATA
+: _EmptyAggregateType
+#endif
 {
     // types:
     typedef array __self;
@@ -282,8 +287,10 @@ struct _LIBCPP_TEMPLATE_VIS array<_Tp, 0>
 
     typedef __conditional_t<is_const<_Tp>::value, const char, char> _CharType;
 
+#ifndef _LIBCPP_ABI_ZERO_SIZE_ARRAY_HAS_NO_DATA
     struct  _ArrayInStructT { _Tp __data_[1]; };
-    _ALIGNAS_TYPE(_ArrayInStructT) _CharType __elems_[sizeof(_ArrayInStructT)];
+    _ALIGNAS_TYPE(_ArrayInStructT) array<_EmptyAggregateType,sizeof(_ArrayInStructT)> __elems_;
+#endif
 
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX17
     value_type* data() _NOEXCEPT {return nullptr;}
diff --git a/libcxx/test/std/concepts/concepts.lang/concept.default.init/default_initializable.compile.pass.cpp b/libcxx/test/std/concepts/concepts.lang/concept.default.init/default_initializable.compile.pass.cpp
index 4921a48bcccc1..07d346333f451 100644
--- a/libcxx/test/std/concepts/concepts.lang/concept.default.init/default_initializable.compile.pass.cpp
+++ b/libcxx/test/std/concepts/concepts.lang/concept.default.init/default_initializable.compile.pass.cpp
@@ -199,7 +199,6 @@ void test()
     test_not_const<void(Empty::*)(const int&) noexcept(false)>();
 
     // Sequence containers
-    test_not_const<std::array<               int, 0>>();
     test_not_const<std::array<               int, 1>>();
     test_false    <std::array<const          int, 1>>();
     test_not_const<std::array<      volatile int, 1>>();
diff --git a/libcxx/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp b/libcxx/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp
index 0e1e77fc5f181..08634d242b0d6 100644
--- a/libcxx/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp
@@ -59,14 +59,6 @@ TEST_CONSTEXPR_CXX14 bool tests()
         static_assert(std::is_copy_constructible<Array>::value, "");
         static_assert(std::is_copy_assignable<Array>::value, "");
     }
-    {
-        // const arrays of size 0 should disable the implicit copy assignment operator.
-        typedef std::array<double const, 0> Array;
-        Array array = {};
-        Array copy = array; (void)copy;
-        static_assert(std::is_copy_constructible<Array>::value, "");
-        TEST_NOT_COPY_ASSIGNABLE(Array);
-    }
     {
         typedef std::array<NoDefault, 0> Array;
         Array array = {};
@@ -75,13 +67,6 @@ TEST_CONSTEXPR_CXX14 bool tests()
         static_assert(std::is_copy_constructible<Array>::value, "");
         static_assert(std::is_copy_assignable<Array>::value, "");
     }
-    {
-        typedef std::array<NoDefault const, 0> Array;
-        Array array = {};
-        Array copy = array; (void)copy;
-        static_assert(std::is_copy_constructible<Array>::value, "");
-        TEST_NOT_COPY_ASSIGNABLE(Array);
-    }
 
     // Make sure we can implicitly copy a std::array of a non-trivially copyable type
     {
diff --git a/libcxx/test/std/containers/sequences/array/size_and_alignment.pass.cpp b/libcxx/test/std/containers/sequences/array/size_and_alignment.pass.cpp
index 6fbc844a11eac..0477cef42e43e 100644
--- a/libcxx/test/std/containers/sequences/array/size_and_alignment.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/size_and_alignment.pass.cpp
@@ -31,14 +31,25 @@ struct MyArray {
 };
 
 template <class T, std::size_t Size>
-void test() {
+struct test {
   typedef T CArrayT[Size == 0 ? 1 : Size];
   typedef std::array<T, Size> ArrayT;
   typedef MyArray<T, Size == 0 ? 1 : Size> MyArrayT;
+
   static_assert(sizeof(ArrayT) == sizeof(CArrayT), "");
   static_assert(sizeof(ArrayT) == sizeof(MyArrayT), "");
   static_assert(TEST_ALIGNOF(ArrayT) == TEST_ALIGNOF(MyArrayT), "");
-}
+};
+
+
+#ifdef _LIBCPP_ABI_ZERO_SIZE_ARRAY_HAS_NO_DATA
+template <class T>
+struct test<T, 0> {
+  using ArrayT = std::array<T, 0>;
+  static_assert(sizeof(ArrayT) == 1, "");
+  static_assert(TEST_ALIGNOF(ArrayT) == 1, "");
+};
+#endif
 
 template <class T>
 void test_type() {
diff --git a/libcxx/test/std/containers/views/mdspan/extents/CtorTestCombinations.h b/libcxx/test/std/containers/views/mdspan/extents/CtorTestCombinations.h
index 02731f8895ed8..62da3e0ca0089 100644
--- a/libcxx/test/std/containers/views/mdspan/extents/CtorTestCombinations.h
+++ b/libcxx/test/std/containers/views/mdspan/extents/CtorTestCombinations.h
@@ -45,7 +45,7 @@ constexpr void test_construction(AllExtents all_ext) {
 
   // test construction from just dynamic extents
   // create an array of just the extents corresponding to dynamic values
-  std::array<typename AllExtents::value_type, E::rank_dynamic()> dyn_ext{0};
+  std::array<typename AllExtents::value_type, E::rank_dynamic()> dyn_ext{};
   size_t dynamic_idx = 0;
   for (size_t r = 0; r < E::rank(); r++) {
     if (E::static_extent(r) == std::dynamic_extent) {



More information about the libcxx-commits mailing list