Author: ericwf Date: Sat Feb 3 18:17:02 2018 New Revision: 324185 URL: http://llvm.org/viewvc/llvm-project?rev=324185&view=rev Log: 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. Added: libcxx/trunk/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp libcxx/trunk/test/std/containers/sequences/array/array.fill/fill.fail.cpp libcxx/trunk/test/std/containers/sequences/array/array.swap/swap.fail.cpp Modified: libcxx/trunk/include/array libcxx/trunk/test/std/containers/sequences/array/array.data/data.pass.cpp Modified: libcxx/trunk/include/array URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/array?rev=324185&r1=324184&r2=324185&view=diff ============================================================================== --- libcxx/trunk/include/array (original) +++ libcxx/trunk/include/array Sat Feb 3 18:17:02 2018 @@ -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 Added: libcxx/trunk/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp?rev=324185&view=auto ============================================================================== --- libcxx/trunk/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp (added) +++ libcxx/trunk/test/std/containers/sequences/array/array.cons/implicit_copy.pass.cpp Sat Feb 3 18:17:02 2018 @@ -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); + } + +} Modified: libcxx/trunk/test/std/containers/sequences/array/array.data/data.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/containers/sequences/array/array.data/data.pass.cpp?rev=324185&r1=324184&r2=324185&view=diff ============================================================================== --- libcxx/trunk/test/std/containers/sequences/array/array.data/data.pass.cpp (original) +++ libcxx/trunk/test/std/containers/sequences/array/array.data/data.pass.cpp Sat Feb 3 18:17:02 2018 @@ -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) {} }; Added: libcxx/trunk/test/std/containers/sequences/array/array.fill/fill.fail.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/containers/sequences/array/array.fill/fill.fail.cpp?rev=324185&view=auto ============================================================================== --- libcxx/trunk/test/std/containers/sequences/array/array.fill/fill.fail.cpp (added) +++ libcxx/trunk/test/std/containers/sequences/array/array.fill/fill.fail.cpp Sat Feb 3 18:17:02 2018 @@ -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}} + } +} Added: libcxx/trunk/test/std/containers/sequences/array/array.swap/swap.fail.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/containers/sequences/array/array.swap/swap.fail.cpp?rev=324185&view=auto ============================================================================== --- libcxx/trunk/test/std/containers/sequences/array/array.swap/swap.fail.cpp (added) +++ libcxx/trunk/test/std/containers/sequences/array/array.swap/swap.fail.cpp Sat Feb 3 18:17:02 2018 @@ -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}} + } +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits