[libc++] Fix PR35491 - std::array of zero-size doesn't work with non-default constructible types.

Summary:
This patch fixes llvm.org/PR35491 and LWG2157  (https://cplusplus.github.io/LWG/issue2157)

The fix attempts to maintain ABI compatibility by replacing the array with a instance of `aligned_storage`.

Reviewers: mclow.lists, EricWF

Reviewed By: EricWF

Subscribers: lichray, cfe-commits

Differential Revision: https://reviews.llvm.org/D41223

llvm-svn: 324526
diff --git a/libcxx/test/std/containers/sequences/array/array.cons/default.pass.cpp b/libcxx/test/std/containers/sequences/array/array.cons/default.pass.cpp
index 7bc62b7..9a2a6ea 100644
--- a/libcxx/test/std/containers/sequences/array/array.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/array.cons/default.pass.cpp
@@ -14,6 +14,14 @@
 #include <array>
 #include <cassert>
 
+// std::array is explicitly allowed to be initialized with A a = { init-list };.
+// Disable the missing braces warning for this reason.
+#include "disable_missing_braces_warning.h"
+
+struct NoDefault {
+  NoDefault(int) {}
+};
+
 int main()
 {
     {
@@ -28,4 +36,13 @@
         C c;
         assert(c.size() == 0);
     }
+    {
+      typedef std::array<NoDefault, 0> C;
+      C c;
+      assert(c.size() == 0);
+      C c1 = {};
+      assert(c1.size() == 0);
+      C c2 = {{}};
+      assert(c2.size() == 0);
+    }
 }
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
new file mode 100644
index 0000000..7814085
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// <array>
+
+// implicitly generated array constructors / assignment operators
+
+#include <array>
+#include <type_traits>
+#include <cassert>
+#include "test_macros.h"
+
+// std::array is explicitly allowed to be initialized with A a = { init-list };.
+// Disable the missing braces warning for this reason.
+#include "disable_missing_braces_warning.h"
+
+// In C++03 the copy assignment operator is not deleted when the implicitly
+// generated operator would be ill-formed; like in the case of a struct with a
+// const member.
+#if TEST_STD_VER < 11
+#define TEST_NOT_COPY_ASSIGNABLE(T) ((void)0)
+#else
+#define TEST_NOT_COPY_ASSIGNABLE(T) static_assert(!std::is_copy_assignable<T>::value, "")
+#endif
+
+struct NoDefault {
+  NoDefault(int) {}
+};
+
+int main() {
+  {
+    typedef double T;
+    typedef std::array<T, 3> C;
+    C c = {1.1, 2.2, 3.3};
+    C c2 = c;
+    c2 = c;
+    static_assert(std::is_copy_constructible<C>::value, "");
+    static_assert(std::is_copy_assignable<C>::value, "");
+  }
+  {
+    typedef double T;
+    typedef std::array<const T, 3> C;
+    C c = {1.1, 2.2, 3.3};
+    C c2 = c;
+    ((void)c2);
+    static_assert(std::is_copy_constructible<C>::value, "");
+    TEST_NOT_COPY_ASSIGNABLE(C);
+  }
+  {
+    typedef double T;
+    typedef std::array<T, 0> C;
+    C c = {};
+    C c2 = c;
+    c2 = c;
+    static_assert(std::is_copy_constructible<C>::value, "");
+    static_assert(std::is_copy_assignable<C>::value, "");
+  }
+  {
+    // const arrays of size 0 should disable the implicit copy assignment operator.
+    typedef double T;
+    typedef std::array<const T, 0> C;
+    C c = {{}};
+    C c2 = c;
+    ((void)c2);
+    static_assert(std::is_copy_constructible<C>::value, "");
+    TEST_NOT_COPY_ASSIGNABLE(C);
+  }
+  {
+    typedef NoDefault T;
+    typedef std::array<T, 0> C;
+    C c = {};
+    C c2 = c;
+    c2 = c;
+    static_assert(std::is_copy_constructible<C>::value, "");
+    static_assert(std::is_copy_assignable<C>::value, "");
+  }
+  {
+    typedef NoDefault T;
+    typedef std::array<const T, 0> C;
+    C c = {{}};
+    C c2 = c;
+    ((void)c2);
+    static_assert(std::is_copy_constructible<C>::value, "");
+    TEST_NOT_COPY_ASSIGNABLE(C);
+  }
+
+}
diff --git a/libcxx/test/std/containers/sequences/array/array.data/data.pass.cpp b/libcxx/test/std/containers/sequences/array/array.data/data.pass.cpp
index d7aed70..7148943 100644
--- a/libcxx/test/std/containers/sequences/array/array.data/data.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/array.data/data.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <array>
 #include <cassert>
+#include "test_macros.h"
 
 // std::array is explicitly allowed to be initialized with A a = { init-list };.
 // Disable the missing braces warning for this reason.
@@ -34,6 +35,33 @@
         typedef std::array<T, 0> C;
         C c = {};
         T* p = c.data();
-        (void)p; // to placate scan-build
+        assert(p != nullptr);
+    }
+    {
+      typedef double T;
+      typedef std::array<const T, 0> C;
+      C c = {{}};
+      const T* p = c.data();
+      static_assert((std::is_same<decltype(c.data()), const T*>::value), "");
+      assert(p != nullptr);
+    }
+  {
+      typedef std::max_align_t T;
+      typedef std::array<T, 0> C;
+      const C c = {};
+      const T* p = c.data();
+      assert(p != nullptr);
+      std::uintptr_t pint = reinterpret_cast<std::uintptr_t>(p);
+      assert(pint % TEST_ALIGNOF(std::max_align_t) == 0);
+    }
+    {
+      struct NoDefault {
+        NoDefault(int) {}
+      };
+      typedef NoDefault T;
+      typedef std::array<T, 0> C;
+      C c = {};
+      T* p = c.data();
+      assert(p != nullptr);
     }
 }
diff --git a/libcxx/test/std/containers/sequences/array/array.data/data_const.pass.cpp b/libcxx/test/std/containers/sequences/array/array.data/data_const.pass.cpp
index 5be082e..b99bf6a 100644
--- a/libcxx/test/std/containers/sequences/array/array.data/data_const.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/array.data/data_const.pass.cpp
@@ -38,6 +38,25 @@
         const T* p = c.data();
         (void)p; // to placate scan-build
     }
+    {
+      struct NoDefault {
+        NoDefault(int) {}
+      };
+      typedef NoDefault T;
+      typedef std::array<T, 0> C;
+      const C c = {};
+      const T* p = c.data();
+      assert(p != nullptr);
+    }
+    {
+      typedef std::max_align_t T;
+      typedef std::array<T, 0> C;
+      const C c = {};
+      const T* p = c.data();
+      assert(p != nullptr);
+      std::uintptr_t pint = reinterpret_cast<std::uintptr_t>(p);
+      assert(pint % TEST_ALIGNOF(std::max_align_t) == 0);
+    }
 #if TEST_STD_VER > 14
     {
         typedef std::array<int, 5> C;
diff --git a/libcxx/test/std/containers/sequences/array/array.fill/fill.fail.cpp b/libcxx/test/std/containers/sequences/array/array.fill/fill.fail.cpp
new file mode 100644
index 0000000..039992f
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/array.fill/fill.fail.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// <array>
+
+// void fill(const T& u);
+
+#include <array>
+#include <cassert>
+
+// std::array is explicitly allowed to be initialized with A a = { init-list };.
+// Disable the missing braces warning for this reason.
+#include "disable_missing_braces_warning.h"
+
+int main() {
+  {
+    typedef double T;
+    typedef std::array<const T, 0> C;
+    C c = {};
+    // expected-error-re@array:* {{static_assert failed {{.*}} "cannot fill zero-sized array of type 'const T'"}}
+    c.fill(5.5); // expected-note {{requested here}}
+  }
+}
diff --git a/libcxx/test/std/containers/sequences/array/array.swap/swap.fail.cpp b/libcxx/test/std/containers/sequences/array/array.swap/swap.fail.cpp
new file mode 100644
index 0000000..c54905b
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/array.swap/swap.fail.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// <array>
+
+// void swap(array& a);
+
+#include <array>
+#include <cassert>
+
+// std::array is explicitly allowed to be initialized with A a = { init-list };.
+// Disable the missing braces warning for this reason.
+#include "disable_missing_braces_warning.h"
+
+int main() {
+  {
+    typedef double T;
+    typedef std::array<const T, 0> C;
+    C c = {};
+    C c2 = {};
+    // expected-error-re@array:* {{static_assert failed {{.*}} "cannot swap zero-sized array of type 'const T'"}}
+    c.swap(c2); // expected-note {{requested here}}
+  }
+}
diff --git a/libcxx/test/std/containers/sequences/array/at.pass.cpp b/libcxx/test/std/containers/sequences/array/at.pass.cpp
index 27b326f..84a8d6f 100644
--- a/libcxx/test/std/containers/sequences/array/at.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/at.pass.cpp
@@ -56,6 +56,26 @@
         catch (const std::out_of_range &) {}
 #endif
     }
+#ifndef TEST_HAS_NO_EXCEPTIONS
+    {
+        typedef double T;
+        typedef std::array<T, 0> C;
+        C c = {};
+        C const& cc = c;
+        try
+        {
+            TEST_IGNORE_NODISCARD  c.at(0);
+            assert(false);
+        }
+        catch (const std::out_of_range &) {}
+        try
+        {
+            TEST_IGNORE_NODISCARD  cc.at(0);
+            assert(false);
+        }
+        catch (const std::out_of_range &) {}
+    }
+#endif
     {
         typedef double T;
         typedef std::array<T, 3> C;
diff --git a/libcxx/test/std/containers/sequences/array/begin.pass.cpp b/libcxx/test/std/containers/sequences/array/begin.pass.cpp
index b12ffc8..282a947 100644
--- a/libcxx/test/std/containers/sequences/array/begin.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/begin.pass.cpp
@@ -18,6 +18,7 @@
 // Disable the missing braces warning for this reason.
 #include "disable_missing_braces_warning.h"
 
+
 int main()
 {
     {
@@ -31,4 +32,13 @@
         *i = 5.5;
         assert(c[0] == 5.5);
     }
+    {
+      struct NoDefault {
+        NoDefault(int) {}
+      };
+      typedef NoDefault T;
+      typedef std::array<T, 0> C;
+      C c = {};
+      assert(c.begin() == c.end());
+    }
 }
diff --git a/libcxx/test/std/containers/sequences/array/compare.fail.cpp b/libcxx/test/std/containers/sequences/array/compare.fail.cpp
new file mode 100644
index 0000000..2aa7cd8
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/compare.fail.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// <array>
+
+// bool operator==(array<T, N> const&, array<T, N> const&);
+// bool operator!=(array<T, N> const&, array<T, N> const&);
+// bool operator<(array<T, N> const&, array<T, N> const&);
+// bool operator<=(array<T, N> const&, array<T, N> const&);
+// bool operator>(array<T, N> const&, array<T, N> const&);
+// bool operator>=(array<T, N> const&, array<T, N> const&);
+
+
+#include <array>
+#include <vector>
+#include <cassert>
+
+#include "test_macros.h"
+
+// std::array is explicitly allowed to be initialized with A a = { init-list };.
+// Disable the missing braces warning for this reason.
+#include "disable_missing_braces_warning.h"
+
+template <class Array>
+void test_compare(const Array& LHS, const Array& RHS) {
+  typedef std::vector<typename Array::value_type> Vector;
+  const Vector LHSV(LHS.begin(), LHS.end());
+  const Vector RHSV(RHS.begin(), RHS.end());
+  assert((LHS == RHS) == (LHSV == RHSV));
+  assert((LHS != RHS) == (LHSV != RHSV));
+  assert((LHS < RHS) == (LHSV < RHSV));
+  assert((LHS <= RHS) == (LHSV <= RHSV));
+  assert((LHS > RHS) == (LHSV > RHSV));
+  assert((LHS >= RHS) == (LHSV >= RHSV));
+}
+
+template <int Dummy> struct NoCompare {};
+
+int main()
+{
+  {
+    typedef NoCompare<0> T;
+    typedef std::array<T, 3> C;
+    C c1 = {{}};
+    // expected-error@algorithm:* 2 {{invalid operands to binary expression}}
+    TEST_IGNORE_NODISCARD (c1 == c1);
+    TEST_IGNORE_NODISCARD (c1 < c1);
+  }
+  {
+    typedef NoCompare<1> T;
+    typedef std::array<T, 3> C;
+    C c1 = {{}};
+    // expected-error@algorithm:* 2 {{invalid operands to binary expression}}
+    TEST_IGNORE_NODISCARD (c1 != c1);
+    TEST_IGNORE_NODISCARD (c1 > c1);
+  }
+  {
+    typedef NoCompare<2> T;
+    typedef std::array<T, 0> C;
+    C c1 = {{}};
+    // expected-error@algorithm:* 2 {{invalid operands to binary expression}}
+    TEST_IGNORE_NODISCARD (c1 == c1);
+    TEST_IGNORE_NODISCARD (c1 < c1);
+  }
+}
diff --git a/libcxx/test/std/containers/sequences/array/compare.pass.cpp b/libcxx/test/std/containers/sequences/array/compare.pass.cpp
new file mode 100644
index 0000000..c8bcf75
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/compare.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// <array>
+
+// bool operator==(array<T, N> const&, array<T, N> const&);
+// bool operator!=(array<T, N> const&, array<T, N> const&);
+// bool operator<(array<T, N> const&, array<T, N> const&);
+// bool operator<=(array<T, N> const&, array<T, N> const&);
+// bool operator>(array<T, N> const&, array<T, N> const&);
+// bool operator>=(array<T, N> const&, array<T, N> const&);
+
+
+#include <array>
+#include <vector>
+#include <cassert>
+
+#include "test_macros.h"
+
+// std::array is explicitly allowed to be initialized with A a = { init-list };.
+// Disable the missing braces warning for this reason.
+#include "disable_missing_braces_warning.h"
+
+template <class Array>
+void test_compare(const Array& LHS, const Array& RHS) {
+  typedef std::vector<typename Array::value_type> Vector;
+  const Vector LHSV(LHS.begin(), LHS.end());
+  const Vector RHSV(RHS.begin(), RHS.end());
+  assert((LHS == RHS) == (LHSV == RHSV));
+  assert((LHS != RHS) == (LHSV != RHSV));
+  assert((LHS < RHS) == (LHSV < RHSV));
+  assert((LHS <= RHS) == (LHSV <= RHSV));
+  assert((LHS > RHS) == (LHSV > RHSV));
+  assert((LHS >= RHS) == (LHSV >= RHSV));
+}
+
+int main()
+{
+  {
+    typedef int T;
+    typedef std::array<T, 3> C;
+    C c1 = {1, 2, 3};
+    C c2 = {1, 2, 3};
+    C c3 = {3, 2, 1};
+    C c4 = {1, 2, 1};
+    test_compare(c1, c2);
+    test_compare(c1, c3);
+    test_compare(c1, c4);
+  }
+  {
+    typedef int T;
+    typedef std::array<T, 0> C;
+    C c1 = {};
+    C c2 = {};
+    test_compare(c1, c2);
+  }
+}
diff --git a/libcxx/test/std/containers/sequences/array/empty.fail.cpp b/libcxx/test/std/containers/sequences/array/empty.fail.cpp
index 85bf5a7..fe118c5 100644
--- a/libcxx/test/std/containers/sequences/array/empty.fail.cpp
+++ b/libcxx/test/std/containers/sequences/array/empty.fail.cpp
@@ -23,6 +23,9 @@
 
 int main ()
 {
+
     std::array<int, 1> c;
-    c.empty();  // expected-error {{ignoring return value of function declared with 'nodiscard' attribute}}
+    c.empty(); // expected-error {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::array<int, 0> c0;
+    c0.empty(); // expected-error {{ignoring return value of function declared with 'nodiscard' attribute}}
 }
diff --git a/libcxx/test/std/containers/sequences/array/front_back.pass.cpp b/libcxx/test/std/containers/sequences/array/front_back.pass.cpp
index 0591ca7..443f28d 100644
--- a/libcxx/test/std/containers/sequences/array/front_back.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/front_back.pass.cpp
@@ -64,7 +64,38 @@
         C::const_reference r2 = c.back();
         assert(r2 == 3.5);
     }
-
+    {
+      typedef double T;
+      typedef std::array<T, 0> C;
+      C c = {};
+      C const& cc = c;
+      static_assert((std::is_same<decltype(c.front()), T &>::value), "");
+      static_assert((std::is_same<decltype(cc.front()), const T &>::value), "");
+      static_assert((std::is_same<decltype(c.back()), T &>::value), "");
+      static_assert((std::is_same<decltype(cc.back()), const T &>::value), "");
+      if (c.size() > (0)) { // always false
+        TEST_IGNORE_NODISCARD c.front();
+        TEST_IGNORE_NODISCARD c.back();
+        TEST_IGNORE_NODISCARD cc.front();
+        TEST_IGNORE_NODISCARD cc.back();
+      }
+    }
+    {
+      typedef double T;
+      typedef std::array<const T, 0> C;
+      C c = {{}};
+      C const& cc = c;
+      static_assert((std::is_same<decltype(c.front()),  const T &>::value), "");
+      static_assert((std::is_same<decltype(cc.front()), const T &>::value), "");
+      static_assert((std::is_same<decltype(c.back()),   const T &>::value), "");
+      static_assert((std::is_same<decltype(cc.back()),  const T &>::value), "");
+      if (c.size() > (0)) {
+        TEST_IGNORE_NODISCARD c.front();
+        TEST_IGNORE_NODISCARD c.back();
+        TEST_IGNORE_NODISCARD cc.front();
+        TEST_IGNORE_NODISCARD cc.back();
+      }
+    }
 #if TEST_STD_VER > 11
     {
         typedef double T;
diff --git a/libcxx/test/std/containers/sequences/array/indexing.pass.cpp b/libcxx/test/std/containers/sequences/array/indexing.pass.cpp
index 43c4947..7718b92 100644
--- a/libcxx/test/std/containers/sequences/array/indexing.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/indexing.pass.cpp
@@ -56,7 +56,34 @@
         C::const_reference r2 = c[2];
         assert(r2 == 3.5);
     }
-
+    { // Test operator[] "works" on zero sized arrays
+        typedef double T;
+        typedef std::array<T, 0> C;
+        C c = {};
+        C const& cc = c;
+        static_assert((std::is_same<decltype(c[0]), T &>::value), "");
+        static_assert((std::is_same<decltype(cc[0]), const T &>::value), "");
+        if (c.size() > (0)) { // always false
+          C::reference r1 = c[0];
+          C::const_reference r2 = cc[0];
+          ((void)r1);
+          ((void)r2);
+        }
+    }
+    { // Test operator[] "works" on zero sized arrays
+        typedef double T;
+        typedef std::array<const T, 0> C;
+        C c = {{}};
+        C const& cc = c;
+        static_assert((std::is_same<decltype(c[0]), const T &>::value), "");
+        static_assert((std::is_same<decltype(cc[0]), const T &>::value), "");
+        if (c.size() > (0)) { // always false
+          C::reference r1 = c[0];
+          C::const_reference r2 = cc[0];
+          ((void)r1);
+          ((void)r2);
+        }
+    }
 #if TEST_STD_VER > 11
     {
         typedef double T;
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
new file mode 100644
index 0000000..d01e1ce
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/size_and_alignment.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// <array>
+
+// template <class T, size_t N >
+// struct array
+
+// Test the size and alignment matches that of an array of a given type.
+
+#include <array>
+#include <iterator>
+#include <type_traits>
+#include <cstddef>
+
+#include "test_macros.h"
+
+template <class T, size_t Size>
+void test() {
+  typedef T CArrayT[Size == 0 ? 1 : Size];
+  typedef std::array<T, Size> ArrayT;
+  static_assert(sizeof(CArrayT) == sizeof(ArrayT), "");
+  static_assert(TEST_ALIGNOF(CArrayT) == TEST_ALIGNOF(ArrayT), "");
+}
+
+template <class T>
+void test_type() {
+  test<T, 1>();
+  test<T, 42>();
+  test<T, 0>();
+}
+
+struct TEST_ALIGNAS(TEST_ALIGNOF(std::max_align_t) * 2) TestType1 {
+
+};
+
+struct TEST_ALIGNAS(TEST_ALIGNOF(std::max_align_t) * 2) TestType2 {
+  char data[1000];
+};
+
+int main() {
+  test_type<char>();
+  test_type<int>();
+  test_type<double>();
+  test_type<long double>();
+  test_type<std::max_align_t>();
+  test_type<TestType1>();
+  test_type<TestType2>();
+}