[libc++] Fix PR35491 - std::array of zero-size doesn't work with non-default construc...
authorEric Fiselier <eric@efcs.ca>
Wed, 7 Feb 2018 21:06:13 +0000 (21:06 +0000)
committerEric Fiselier <eric@efcs.ca>
Wed, 7 Feb 2018 21:06:13 +0000 (21:06 +0000)
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

19 files changed:
libcxx/include/__config
libcxx/include/array
libcxx/test/libcxx/containers/sequences/array/array.zero/db_back.pass.cpp [new file with mode: 0644]
libcxx/test/libcxx/containers/sequences/array/array.zero/db_front.pass.cpp [new file with mode: 0644]
libcxx/test/libcxx/containers/sequences/array/array.zero/db_indexing.pass.cpp [new file with mode: 0644]
libcxx/test/std/containers/sequences/array/array.cons/default.pass.cpp
libcxx/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp [new file with mode: 0644]
libcxx/test/std/containers/sequences/array/array.data/data.pass.cpp
libcxx/test/std/containers/sequences/array/array.data/data_const.pass.cpp
libcxx/test/std/containers/sequences/array/array.fill/fill.fail.cpp [new file with mode: 0644]
libcxx/test/std/containers/sequences/array/array.swap/swap.fail.cpp [new file with mode: 0644]
libcxx/test/std/containers/sequences/array/at.pass.cpp
libcxx/test/std/containers/sequences/array/begin.pass.cpp
libcxx/test/std/containers/sequences/array/compare.fail.cpp [new file with mode: 0644]
libcxx/test/std/containers/sequences/array/compare.pass.cpp [new file with mode: 0644]
libcxx/test/std/containers/sequences/array/empty.fail.cpp
libcxx/test/std/containers/sequences/array/front_back.pass.cpp
libcxx/test/std/containers/sequences/array/indexing.pass.cpp
libcxx/test/std/containers/sequences/array/size_and_alignment.pass.cpp [new file with mode: 0644]

index 470b22d..a4acbca 100644 (file)
@@ -793,8 +793,13 @@ namespace std {
 # if !defined(_LIBCPP_DEBUG)
 #   error cannot use _LIBCPP_DEBUG_USE_EXCEPTIONS unless _LIBCPP_DEBUG is defined
 # endif
-# define _NOEXCEPT_DEBUG noexcept(false)
-# define _NOEXCEPT_DEBUG_(x) noexcept(false)
+# ifdef _LIBCPP_HAS_NO_NOEXCEPT
+#   define _NOEXCEPT_DEBUG
+#   define _NOEXCEPT_DEBUG_(x)
+# else
+#   define _NOEXCEPT_DEBUG noexcept(false)
+#   define _NOEXCEPT_DEBUG_(x) noexcept(false)
+#endif
 #else
 # define _NOEXCEPT_DEBUG _NOEXCEPT
 # define _NOEXCEPT_DEBUG_(x) _NOEXCEPT_(x)
index 4eb2fe6..706e573 100644 (file)
@@ -108,6 +108,8 @@ template <size_t I, class T, size_t N> const T&& get(const array<T, N>&&) noexce
 #include <iterator>
 #include <algorithm>
 #include <stdexcept>
+#include <cstdlib> // for _LIBCPP_UNREACHABLE
+#include <__debug>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #pragma GCC system_header
@@ -117,6 +119,7 @@ template <size_t I, class T, size_t N> const T&& get(const array<T, N>&&) noexce
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+
 template <class _Tp, size_t _Size>
 struct _LIBCPP_TEMPLATE_VIS array
 {
@@ -134,31 +137,27 @@ struct _LIBCPP_TEMPLATE_VIS array
     typedef std::reverse_iterator<iterator>       reverse_iterator;
     typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
 
-    value_type __elems_[_Size > 0 ? _Size : 1];
+    _Tp __elems_[_Size];
 
     // No explicit construct/copy/destroy for aggregate type
-    _LIBCPP_INLINE_VISIBILITY void fill(const value_type& __u)
-        {_VSTD::fill_n(__elems_, _Size, __u);}
-    _LIBCPP_INLINE_VISIBILITY
-    void swap(array& __a) _NOEXCEPT_(_Size == 0 || __is_nothrow_swappable<_Tp>::value)
-        { __swap_dispatch((std::integral_constant<bool, _Size == 0>()), __a); }
+    _LIBCPP_INLINE_VISIBILITY void fill(const value_type& __u) {
+      _VSTD::fill_n(__elems_, _Size, __u);
+    }
 
     _LIBCPP_INLINE_VISIBILITY
-    void __swap_dispatch(std::true_type, array&) {}
-
-    _LIBCPP_INLINE_VISIBILITY
-    void __swap_dispatch(std::false_type, array& __a)
-        { _VSTD::swap_ranges(__elems_, __elems_ + _Size, __a.__elems_);}
+    void swap(array& __a) _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value) {
+      std::swap_ranges(__elems_, __elems_ + _Size, __a.__elems_);
+    }
 
     // iterators:
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-    iterator begin() _NOEXCEPT {return iterator(__elems_);}
+    iterator begin() _NOEXCEPT {return iterator(data());}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-    const_iterator begin() const _NOEXCEPT {return const_iterator(__elems_);}
+    const_iterator begin() const _NOEXCEPT {return const_iterator(data());}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-    iterator end() _NOEXCEPT {return iterator(__elems_ + _Size);}
+    iterator end() _NOEXCEPT {return iterator(data() + _Size);}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-    const_iterator end() const _NOEXCEPT {return const_iterator(__elems_ + _Size);}
+    const_iterator end() const _NOEXCEPT {return const_iterator(data() + _Size);}
 
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
     reverse_iterator rbegin() _NOEXCEPT {return reverse_iterator(end());}
@@ -184,7 +183,7 @@ struct _LIBCPP_TEMPLATE_VIS array
     _LIBCPP_INLINE_VISIBILITY
     _LIBCPP_CONSTEXPR size_type max_size() const _NOEXCEPT {return _Size;}
     _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
-    _LIBCPP_CONSTEXPR bool empty() const _NOEXCEPT {return _Size == 0;}
+    _LIBCPP_CONSTEXPR bool empty() const _NOEXCEPT {return false; }
 
     // element access:
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
@@ -197,8 +196,8 @@ struct _LIBCPP_TEMPLATE_VIS array
 
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 reference front()             {return __elems_[0];}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 const_reference front() const {return __elems_[0];}
-    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 reference back()              {return __elems_[_Size > 0 ? _Size-1 : 0];}
-    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 const_reference back() const  {return __elems_[_Size > 0 ? _Size-1 : 0];}
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 reference back()              {return __elems_[_Size - 1];}
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 const_reference back() const  {return __elems_[_Size - 1];}
 
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
     value_type* data() _NOEXCEPT {return __elems_;}
@@ -206,6 +205,7 @@ struct _LIBCPP_TEMPLATE_VIS array
     const value_type* data() const _NOEXCEPT {return __elems_;}
 };
 
+
 template <class _Tp, size_t _Size>
 _LIBCPP_CONSTEXPR_AFTER_CXX14
 typename array<_Tp, _Size>::reference
@@ -227,12 +227,138 @@ array<_Tp, _Size>::at(size_type __n) const
     return __elems_[__n];
 }
 
+template <class _Tp>
+struct _LIBCPP_TEMPLATE_VIS array<_Tp, 0>
+{
+    // types:
+    typedef array __self;
+    typedef _Tp                                   value_type;
+    typedef value_type&                           reference;
+    typedef const value_type&                     const_reference;
+    typedef value_type*                           iterator;
+    typedef const value_type*                     const_iterator;
+    typedef value_type*                           pointer;
+    typedef const value_type*                     const_pointer;
+    typedef size_t                                size_type;
+    typedef ptrdiff_t                             difference_type;
+    typedef std::reverse_iterator<iterator>       reverse_iterator;
+    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+
+    typedef typename conditional<is_const<_Tp>::value, const char,
+                                char>::type _CharType;
+    _ALIGNAS(alignment_of<_Tp[1]>::value) _CharType __elems_[sizeof(_Tp[1])];
+
+    // No explicit construct/copy/destroy for aggregate type
+    _LIBCPP_INLINE_VISIBILITY void fill(const value_type&) {
+      static_assert(!is_const<_Tp>::value,
+                    "cannot fill zero-sized array of type 'const T'");
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    void swap(array&) _NOEXCEPT {
+      static_assert(!is_const<_Tp>::value,
+                    "cannot swap zero-sized array of type 'const T'");
+    }
+
+    // iterators:
+    _LIBCPP_INLINE_VISIBILITY
+    iterator begin() _NOEXCEPT {return iterator(data());}
+    _LIBCPP_INLINE_VISIBILITY
+    const_iterator begin() const _NOEXCEPT {return const_iterator(data());}
+    _LIBCPP_INLINE_VISIBILITY
+    iterator end() _NOEXCEPT {return iterator(data());}
+    _LIBCPP_INLINE_VISIBILITY
+    const_iterator end() const _NOEXCEPT {return const_iterator(data());}
+
+    _LIBCPP_INLINE_VISIBILITY
+    reverse_iterator rbegin() _NOEXCEPT {return reverse_iterator(end());}
+    _LIBCPP_INLINE_VISIBILITY
+    const_reverse_iterator rbegin() const _NOEXCEPT {return const_reverse_iterator(end());}
+    _LIBCPP_INLINE_VISIBILITY
+    reverse_iterator rend() _NOEXCEPT {return reverse_iterator(begin());}
+    _LIBCPP_INLINE_VISIBILITY
+    const_reverse_iterator rend() const _NOEXCEPT {return const_reverse_iterator(begin());}
+
+    _LIBCPP_INLINE_VISIBILITY
+    const_iterator cbegin() const _NOEXCEPT {return begin();}
+    _LIBCPP_INLINE_VISIBILITY
+    const_iterator cend() const _NOEXCEPT {return end();}
+    _LIBCPP_INLINE_VISIBILITY
+    const_reverse_iterator crbegin() const _NOEXCEPT {return rbegin();}
+    _LIBCPP_INLINE_VISIBILITY
+    const_reverse_iterator crend() const _NOEXCEPT {return rend();}
+
+    // capacity:
+    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR size_type size() const _NOEXCEPT {return 0; }
+    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR size_type max_size() const _NOEXCEPT {return 0;}
+    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR bool empty() const _NOEXCEPT {return true;}
+
+    // element access:
+    _LIBCPP_INLINE_VISIBILITY
+    reference operator[](size_type) {
+      _LIBCPP_ASSERT(false, "cannot call array<T, 0>::operator[] on a zero-sized array");
+      _LIBCPP_UNREACHABLE();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+    const_reference operator[](size_type) const {
+      _LIBCPP_ASSERT(false, "cannot call array<T, 0>::operator[] on a zero-sized array");
+      _LIBCPP_UNREACHABLE();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    reference at(size_type) {
+      __throw_out_of_range("array<T, 0>::at");
+      _LIBCPP_UNREACHABLE();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    const_reference at(size_type) const {
+      __throw_out_of_range("array<T, 0>::at");
+      _LIBCPP_UNREACHABLE();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    reference front() {
+      _LIBCPP_ASSERT(false, "cannot call array<T, 0>::front() on a zero-sized array");
+      _LIBCPP_UNREACHABLE();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    const_reference front() const {
+      _LIBCPP_ASSERT(false, "cannot call array<T, 0>::front() on a zero-sized array");
+      _LIBCPP_UNREACHABLE();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    reference back() {
+      _LIBCPP_ASSERT(false, "cannot call array<T, 0>::back() on a zero-sized array");
+      _LIBCPP_UNREACHABLE();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    const_reference back() const {
+      _LIBCPP_ASSERT(false, "cannot call array<T, 0>::back() on a zero-sized array");
+      _LIBCPP_UNREACHABLE();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    value_type* data() _NOEXCEPT {return reinterpret_cast<value_type*>(__elems_);}
+    _LIBCPP_INLINE_VISIBILITY
+    const value_type* data() const _NOEXCEPT {return reinterpret_cast<const value_type*>(__elems_);}
+};
+
+
 template <class _Tp, size_t _Size>
 inline _LIBCPP_INLINE_VISIBILITY
 bool
 operator==(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y)
 {
-    return _VSTD::equal(__x.__elems_, __x.__elems_ + _Size, __y.__elems_);
+    return _VSTD::equal(__x.begin(), __x.end(), __y.begin());
 }
 
 template <class _Tp, size_t _Size>
@@ -248,7 +374,8 @@ inline _LIBCPP_INLINE_VISIBILITY
 bool
 operator<(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y)
 {
-    return _VSTD::lexicographical_compare(__x.__elems_, __x.__elems_ + _Size, __y.__elems_, __y.__elems_ + _Size);
+    return _VSTD::lexicographical_compare(__x.begin(), __x.end(),
+                                          __y.begin(), __y.end());
 }
 
 template <class _Tp, size_t _Size>
diff --git a/libcxx/test/libcxx/containers/sequences/array/array.zero/db_back.pass.cpp b/libcxx/test/libcxx/containers/sequences/array/array.zero/db_back.pass.cpp
new file mode 100644 (file)
index 0000000..465523f
--- /dev/null
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: libcpp-no-exceptions
+// MODULES_DEFINES: _LIBCPP_DEBUG=1
+// MODULES_DEFINES: _LIBCPP_DEBUG_USE_EXCEPTIONS
+
+// Can't test the system lib because this test enables debug mode
+// UNSUPPORTED: with_system_cxx_lib
+
+// test array<T, 0>::front() throws a debug exception.
+
+#define _LIBCPP_DEBUG 1
+#define _LIBCPP_DEBUG_USE_EXCEPTIONS
+#include <array>
+
+template <class Array>
+inline bool CheckDebugThrows(Array& Arr) {
+  try {
+    Arr.back();
+  } catch (std::__libcpp_debug_exception const&) {
+    return true;
+  }
+  return false;
+}
+
+int main()
+{
+  {
+    typedef std::array<int, 0> C;
+    C c = {};
+    C const& cc = c;
+    assert(CheckDebugThrows(c));
+    assert(CheckDebugThrows(cc));
+  }
+  {
+    typedef std::array<const int, 0> C;
+    C c = {{}};
+    C const& cc = c;
+    assert(CheckDebugThrows(c));
+    assert(CheckDebugThrows(cc));
+  }
+}
diff --git a/libcxx/test/libcxx/containers/sequences/array/array.zero/db_front.pass.cpp b/libcxx/test/libcxx/containers/sequences/array/array.zero/db_front.pass.cpp
new file mode 100644 (file)
index 0000000..d49b3a7
--- /dev/null
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: libcpp-no-exceptions
+// MODULES_DEFINES: _LIBCPP_DEBUG=1
+// MODULES_DEFINES: _LIBCPP_DEBUG_USE_EXCEPTIONS
+
+// Can't test the system lib because this test enables debug mode
+// UNSUPPORTED: with_system_cxx_lib
+
+// test array<T, 0>::front() throws a debug exception.
+
+#define _LIBCPP_DEBUG 1
+#define _LIBCPP_DEBUG_USE_EXCEPTIONS
+#include <array>
+
+template <class Array>
+inline bool CheckDebugThrows(Array& Arr) {
+  try {
+    Arr.front();
+  } catch (std::__libcpp_debug_exception const&) {
+    return true;
+  }
+  return false;
+}
+
+int main()
+{
+  {
+    typedef std::array<int, 0> C;
+    C c = {};
+    C const& cc = c;
+    assert(CheckDebugThrows(c));
+    assert(CheckDebugThrows(cc));
+  }
+  {
+    typedef std::array<const int, 0> C;
+    C c = {{}};
+    C const& cc = c;
+    assert(CheckDebugThrows(c));
+    assert(CheckDebugThrows(cc));
+  }
+}
diff --git a/libcxx/test/libcxx/containers/sequences/array/array.zero/db_indexing.pass.cpp b/libcxx/test/libcxx/containers/sequences/array/array.zero/db_indexing.pass.cpp
new file mode 100644 (file)
index 0000000..4d6ad69
--- /dev/null
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: libcpp-no-exceptions
+// MODULES_DEFINES: _LIBCPP_DEBUG=1
+// MODULES_DEFINES: _LIBCPP_DEBUG_USE_EXCEPTIONS
+
+// Can't test the system lib because this test enables debug mode
+// UNSUPPORTED: with_system_cxx_lib
+
+// test array<T, 0>::operator[] throws a debug exception.
+
+#define _LIBCPP_DEBUG 1
+#define _LIBCPP_DEBUG_USE_EXCEPTIONS
+#include <array>
+
+template <class Array>
+inline bool CheckDebugThrows(Array& Arr, size_t Index) {
+  try {
+    Arr[Index];
+  } catch (std::__libcpp_debug_exception const&) {
+    return true;
+  }
+  return false;
+}
+
+int main()
+{
+  {
+    typedef std::array<int, 0> C;
+    C c = {};
+    C const& cc = c;
+    assert(CheckDebugThrows(c, 0));
+    assert(CheckDebugThrows(c, 1));
+    assert(CheckDebugThrows(cc, 0));
+    assert(CheckDebugThrows(cc, 1));
+  }
+  {
+    typedef std::array<const int, 0> C;
+    C c = {{}};
+    C const& cc = c;
+    assert(CheckDebugThrows(c, 0));
+    assert(CheckDebugThrows(c, 1));
+    assert(CheckDebugThrows(cc, 0));
+    assert(CheckDebugThrows(cc, 1));
+  }
+}
index 7bc62b7..9a2a6ea 100644 (file)
 #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 @@ int main()
         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 (file)
index 0000000..7814085
--- /dev/null
@@ -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);
+  }
+
+}
index d7aed70..7148943 100644 (file)
@@ -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 @@ int main()
         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);
     }
 }
index 5be082e..b99bf6a 100644 (file)
@@ -38,6 +38,25 @@ int main()
         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 (file)
index 0000000..039992f
--- /dev/null
@@ -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 (file)
index 0000000..c54905b
--- /dev/null
@@ -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}}
+  }
+}
index 27b326f..84a8d6f 100644 (file)
@@ -56,6 +56,26 @@ int main()
         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;
index b12ffc8..282a947 100644 (file)
@@ -18,6 +18,7 @@
 // Disable the missing braces warning for this reason.
 #include "disable_missing_braces_warning.h"
 
+
 int main()
 {
     {
@@ -31,4 +32,13 @@ int main()
         *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 (file)
index 0000000..2aa7cd8
--- /dev/null
@@ -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 (file)
index 0000000..c8bcf75
--- /dev/null
@@ -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);
+  }
+}
index 85bf5a7..fe118c5 100644 (file)
@@ -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}}
 }
index 0591ca7..443f28d 100644 (file)
@@ -64,7 +64,38 @@ int main()
         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;
index 43c4947..7718b92 100644 (file)
@@ -56,7 +56,34 @@ int main()
         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 (file)
index 0000000..d01e1ce
--- /dev/null
@@ -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>();
+}