diff options
author | Eric Fiselier <eric@efcs.ca> | 2018-02-04 02:17:02 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2018-02-04 02:17:02 +0000 |
commit | 122c064a764a01f302540bf1952ec782c753fcb7 (patch) | |
tree | caf7c9f92d826dd28125bf06432c5591114a4538 | |
parent | f3224ac0076b2294d52c6f79305ac2d9e4e9f947 (diff) | |
download | libcxx-122c064a764a01f302540bf1952ec782c753fcb7.tar.gz |
Make array<const T, 0> non-CopyAssignable and make swap and fill ill-formed.
The standard isn't exactly clear how std::array should handle zero-sized arrays
with const element types. In particular W.R.T. copy assignment, swap, and fill.
This patch takes the position that those operations should be ill-formed,
and makes changes to libc++ to make it so.
This follows up on commit r324182.
git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@324185 91177308-0d34-0410-b5e6-96231b3b80d8
5 files changed, 181 insertions, 10 deletions
diff --git a/include/array b/include/array index 8cd3ecdf9..4a89ea90d 100644 --- a/include/array +++ b/include/array @@ -122,7 +122,8 @@ struct __array_traits { typedef _Tp _StorageT[_Size]; _LIBCPP_INLINE_VISIBILITY - static _LIBCPP_CONSTEXPR_AFTER_CXX14 _Tp* __data(_StorageT& __store) { + static _LIBCPP_CONSTEXPR_AFTER_CXX14 typename remove_const<_Tp>::type* + __data(typename remove_const<_StorageT>::type& __store) { return __store; } @@ -144,12 +145,16 @@ struct __array_traits { template <class _Tp> struct __array_traits<_Tp, 0> { - typedef typename aligned_storage<sizeof(_Tp), alignment_of<_Tp>::value>::type _StorageT; + typedef typename aligned_storage<sizeof(_Tp), alignment_of<_Tp>::value>::type + _NonConstStorageT; + typedef typename conditional<is_const<_Tp>::value, const _NonConstStorageT, + _NonConstStorageT>::type _StorageT; + typedef typename remove_const<_Tp>::type _NonConstTp; _LIBCPP_INLINE_VISIBILITY - static _Tp* __data(_StorageT& __store) { + static _NonConstTp* __data(_NonConstStorageT& __store) { _StorageT *__ptr = std::addressof(__store); - return reinterpret_cast<_Tp*>(__ptr); + return reinterpret_cast<_NonConstTp*>(__ptr); } _LIBCPP_INLINE_VISIBILITY @@ -162,8 +167,7 @@ struct __array_traits<_Tp, 0> { static void __swap(_StorageT&, _StorageT&) {} _LIBCPP_INLINE_VISIBILITY - static void __fill(_StorageT&, _Tp const&) { - } + static void __fill(_StorageT&, _Tp const&) {} }; template <class _Tp, size_t _Size> @@ -187,12 +191,19 @@ struct _LIBCPP_TEMPLATE_VIS array typename _Traits::_StorageT __elems_; // No explicit construct/copy/destroy for aggregate type - _LIBCPP_INLINE_VISIBILITY void fill(const value_type& __u) - {_Traits::__fill(__elems_, __u);} + _LIBCPP_INLINE_VISIBILITY void fill(const value_type& __u) { + static_assert(_Size != 0 || !is_const<_Tp>::value, + "cannot fill zero-sized array of type 'const T'"); + _Traits::__fill(__elems_, __u); + } _LIBCPP_INLINE_VISIBILITY - void swap(array& __a) _NOEXCEPT_(_Size == 0 || __is_nothrow_swappable<_Tp>::value) - { _Traits::__swap(__elems_, __a.__elems_); } + void swap(array& __a) + _NOEXCEPT_(_Size == 0 || __is_nothrow_swappable<_Tp>::value) { + static_assert(_Size != 0 || !is_const<_Tp>::value, + "cannot swap zero-sized array of type 'const T'"); + _Traits::__swap(__elems_, __a.__elems_); + } // iterators: _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 diff --git a/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp b/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp new file mode 100644 index 000000000..864318116 --- /dev/null +++ b/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" + +// FIXME: Clang generates copy assignment operators for types with const members +// in C++03. The generated operator is ill-formed but still present. +// I'm not sure if this is a Clang bug, but we need to work around it for now. +#if TEST_STD_VER < 11 && defined(__clang__) +#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/test/std/containers/sequences/array/array.data/data.pass.cpp b/test/std/containers/sequences/array/array.data/data.pass.cpp index 7b510c203..452d6b7f3 100644 --- a/test/std/containers/sequences/array/array.data/data.pass.cpp +++ b/test/std/containers/sequences/array/array.data/data.pass.cpp @@ -37,6 +37,14 @@ int main() (void)p; // to placate scan-build } { + 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), ""); + (void)p; // to placate scan-build + } + { struct NoDefault { NoDefault(int) {} }; diff --git a/test/std/containers/sequences/array/array.fill/fill.fail.cpp b/test/std/containers/sequences/array/array.fill/fill.fail.cpp new file mode 100644 index 000000000..07816c7c7 --- /dev/null +++ b/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@array:* {{static_assert failed "cannot fill zero-sized array of type 'const T'"}} + c.fill(5.5); // expected-note {{requested here}} + } +} diff --git a/test/std/containers/sequences/array/array.swap/swap.fail.cpp b/test/std/containers/sequences/array/array.swap/swap.fail.cpp new file mode 100644 index 000000000..26febadb7 --- /dev/null +++ b/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@array:* {{static_assert failed "cannot swap zero-sized array of type 'const T'"}} + c.swap(c2); // expected-note {{requested here}} + } +} |