[libcxx-commits] [libcxx] [libc++] Fix constexpr initialization of std::array<T, 0> (PR #74667)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Dec 6 14:37:18 PST 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Louis Dionne (ldionne)
<details>
<summary>Changes</summary>
This patch fixes constexpr default initialization of empty arrays and improves the tests accordingly.
Fixes #<!-- -->74375
---
Full diff: https://github.com/llvm/llvm-project/pull/74667.diff
4 Files Affected:
- (modified) libcxx/include/array (+2-1)
- (modified) libcxx/test/std/containers/sequences/array/array.cons/initialization.pass.cpp (+21-4)
- (added) libcxx/test/std/containers/sequences/array/size_and_alignment.compile.pass.cpp (+130)
- (removed) libcxx/test/std/containers/sequences/array/size_and_alignment.pass.cpp (-78)
``````````diff
diff --git a/libcxx/include/array b/libcxx/include/array
index 127092f6bca9b..334fa747339ba 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -280,7 +280,8 @@ struct _LIBCPP_TEMPLATE_VIS array<_Tp, 0>
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
- typedef __conditional_t<is_const<_Tp>::value, const char, char> _CharType;
+ struct _EmptyAggregate { };
+ typedef __conditional_t<is_const<_Tp>::value, const _EmptyAggregate, _EmptyAggregate> _CharType;
struct _ArrayInStructT { _Tp __data_[1]; };
_ALIGNAS_TYPE(_ArrayInStructT) _CharType __elems_[sizeof(_ArrayInStructT)];
diff --git a/libcxx/test/std/containers/sequences/array/array.cons/initialization.pass.cpp b/libcxx/test/std/containers/sequences/array/array.cons/initialization.pass.cpp
index 9153106b384fc..bdc79e22013f8 100644
--- a/libcxx/test/std/containers/sequences/array/array.cons/initialization.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/array.cons/initialization.pass.cpp
@@ -19,11 +19,9 @@ struct NoDefault {
};
// Test default initialization
-// This one isn't constexpr because omitting to initialize fundamental types
-// isn't valid in a constexpr context.
struct test_default_initialization {
template <typename T>
- void operator()() const
+ TEST_CONSTEXPR_CXX14 void operator()() const
{
std::array<T, 0> a0; (void)a0;
std::array<T, 1> a1; (void)a1;
@@ -34,6 +32,20 @@ struct test_default_initialization {
}
};
+// Additional tests for constexpr default initialization of empty arrays since
+// it seems that making a variable constexpr and merely having a non-constexpr
+// variable in a constexpr function behave differently.
+//
+// Reproducer for https://github.com/llvm/llvm-project/issues/74375
+struct test_default_initialization_74375 {
+ template <typename T>
+ TEST_CONSTEXPR_CXX14 void operator()() const
+ {
+ constexpr std::array<T, 0> a0; (void)a0;
+ constexpr std::array<NoDefault, 0> nodefault; (void)nodefault;
+ }
+};
+
struct test_nondefault_initialization {
template <typename T>
TEST_CONSTEXPR_CXX14 void operator()() const
@@ -177,10 +189,15 @@ TEST_CONSTEXPR_CXX14 bool with_all_types()
int main(int, char**)
{
with_all_types<test_nondefault_initialization>();
- with_all_types<test_default_initialization>(); // not constexpr
+ with_all_types<test_default_initialization>();
+ with_all_types<test_default_initialization_74375>();
test_initializer_list();
#if TEST_STD_VER >= 14
static_assert(with_all_types<test_nondefault_initialization>(), "");
+#if TEST_STD_VER >= 20
+ static_assert(with_all_types<test_default_initialization>(), "");
+#endif
+ static_assert(with_all_types<test_default_initialization_74375>(), "");
static_assert(test_initializer_list(), "");
#endif
diff --git a/libcxx/test/std/containers/sequences/array/size_and_alignment.compile.pass.cpp b/libcxx/test/std/containers/sequences/array/size_and_alignment.compile.pass.cpp
new file mode 100644
index 0000000000000..66069715103fd
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/size_and_alignment.compile.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <array>
+
+// template <class T, size_t N>
+// struct array
+
+// Make sure std::array<T, N> has the correct object size and alignment.
+// This test is mostly meant to catch subtle ABI-breaking regressions.
+
+// Ignore error about requesting a large alignment not being ABI compatible with older AIX systems.
+#if defined(_AIX)
+# pragma clang diagnostic ignored "-Waix-compat"
+#endif
+
+#include <array>
+#include <cstddef>
+
+#include "test_macros.h"
+
+template <class T, std::size_t Size>
+struct MyArray {
+ T elems[Size];
+};
+
+template <class T>
+void test_type() {
+ {
+ static_assert(sizeof(std::array<T, 0>) == sizeof(T), "");
+ static_assert(alignof(std::array<T, 0>) == alignof(T), "");
+ static_assert(sizeof(std::array<T, 0>) == sizeof(T[1]), "");
+ static_assert(sizeof(std::array<T, 0>) == sizeof(MyArray<T, 1>), "");
+ static_assert(alignof(std::array<T, 0>) == alignof(MyArray<T, 1>), "");
+ }
+
+ {
+ static_assert(sizeof(std::array<T, 1>) == sizeof(T), "");
+ static_assert(alignof(std::array<T, 1>) == alignof(T), "");
+ static_assert(sizeof(std::array<T, 1>) == sizeof(T[1]), "");
+ static_assert(sizeof(std::array<T, 1>) == sizeof(MyArray<T, 1>), "");
+ static_assert(alignof(std::array<T, 1>) == alignof(MyArray<T, 1>), "");
+ }
+
+ {
+ static_assert(sizeof(std::array<T, 2>) == sizeof(T) * 2, "");
+ static_assert(alignof(std::array<T, 2>) == alignof(T), "");
+ static_assert(sizeof(std::array<T, 2>) == sizeof(T[2]), "");
+ static_assert(sizeof(std::array<T, 2>) == sizeof(MyArray<T, 2>), "");
+ static_assert(alignof(std::array<T, 2>) == alignof(MyArray<T, 2>), "");
+ }
+
+ {
+ static_assert(sizeof(std::array<T, 3>) == sizeof(T) * 3, "");
+ static_assert(alignof(std::array<T, 3>) == alignof(T), "");
+ static_assert(sizeof(std::array<T, 3>) == sizeof(T[3]), "");
+ static_assert(sizeof(std::array<T, 3>) == sizeof(MyArray<T, 3>), "");
+ static_assert(alignof(std::array<T, 3>) == alignof(MyArray<T, 3>), "");
+ }
+
+ {
+ static_assert(sizeof(std::array<T, 444>) == sizeof(T) * 444, "");
+ static_assert(alignof(std::array<T, 444>) == alignof(T), "");
+ static_assert(sizeof(std::array<T, 444>) == sizeof(T[444]), "");
+ static_assert(sizeof(std::array<T, 444>) == sizeof(MyArray<T, 444>), "");
+ static_assert(alignof(std::array<T, 444>) == alignof(MyArray<T, 444>), "");
+ }
+}
+
+struct Empty {};
+
+struct Aggregate {
+ int i;
+};
+
+struct WithPadding {
+ long double ld;
+ char c;
+};
+
+#if TEST_STD_VER >= 11
+struct alignas(alignof(std::max_align_t) * 2) Overaligned1 {};
+
+struct alignas(alignof(std::max_align_t) * 2) Overaligned2 {
+ char data[1000];
+};
+
+struct alignas(alignof(std::max_align_t)) Overaligned3 {
+ char data[1000];
+};
+
+struct alignas(8) Overaligned4 {
+ char c;
+};
+
+struct alignas(8) Overaligned5 {};
+#endif
+
+int main(int, char**) {
+ test_type<char>();
+ test_type<short>();
+ test_type<int>();
+ test_type<long>();
+ test_type<long long>();
+ test_type<float>();
+ test_type<double>();
+ test_type<long double>();
+ test_type<char[1]>();
+ test_type<char[2]>();
+ test_type<char[3]>();
+ test_type<Empty>();
+ test_type<Aggregate>();
+ test_type<WithPadding>();
+
+#if TEST_STD_VER >= 11
+ test_type<std::max_align_t>();
+ test_type<Overaligned1>();
+ test_type<Overaligned2>();
+ test_type<Overaligned3>();
+ test_type<Overaligned4>();
+ test_type<Overaligned5>();
+#endif
+
+ return 0;
+}
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
deleted file mode 100644
index 6fbc844a11eac..0000000000000
--- a/libcxx/test/std/containers/sequences/array/size_and_alignment.pass.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <array>
-
-// template <class T, size_t N>
-// struct array
-
-// Test the size and alignment matches that of an array of a given type.
-
-// Ignore error about requesting a large alignment not being ABI compatible with older AIX systems.
-#if defined(_AIX)
-# pragma clang diagnostic ignored "-Waix-compat"
-#endif
-
-#include <array>
-#include <iterator>
-#include <type_traits>
-#include <cstddef>
-
-#include "test_macros.h"
-
-template <class T, std::size_t Size>
-struct MyArray {
- T elems[Size];
-};
-
-template <class T, std::size_t Size>
-void 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), "");
-}
-
-template <class T>
-void test_type() {
- test<T, 1>();
- test<T, 42>();
- test<T, 0>();
-}
-
-#if TEST_STD_VER >= 11
-struct alignas(alignof(std::max_align_t) * 2) TestType1 {
-
-};
-
-struct alignas(alignof(std::max_align_t) * 2) TestType2 {
- char data[1000];
-};
-
-struct alignas(alignof(std::max_align_t)) TestType3 {
- char data[1000];
-};
-#endif
-
-int main(int, char**) {
- test_type<char>();
- test_type<int>();
- test_type<double>();
- test_type<long double>();
-
-#if TEST_STD_VER >= 11
- test_type<std::max_align_t>();
- test_type<TestType1>();
- test_type<TestType2>();
- test_type<TestType3>();
-#endif
-
- return 0;
-}
``````````
</details>
https://github.com/llvm/llvm-project/pull/74667
More information about the libcxx-commits
mailing list