diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -688,6 +688,8 @@ "traits_type::char_type must be the same type as CharT"); static_assert(( is_same::value), "Allocator::value_type must be same type as value_type"); + static_assert((is_trivially_destructible::value), + "pointer type must have a trivial destructor."); typedef __wrap_iter iterator; typedef __wrap_iter const_iterator; @@ -776,7 +778,21 @@ static_assert(sizeof(__short) == (sizeof(value_type) * (__min_cap + 1)), "__short has an unexpected size."); +#ifdef _LIBCPP_CXX03_LANG union __ulx{__long __lx; __short __lxx;}; +#else + // Since C++03, unions can contain members with non-trivial special member functions. The corresponding special + // member functions of the union will be deleted and therefore must be explicitly defined. To support fancy pointers + // with non-trivial constructors in __long, we must explicitly define a constructor in __ulx, as the default + // constructor will be deleted. + union __ulx + { + _LIBCPP_CONSTEXPR_AFTER_CXX17 __ulx() _NOEXCEPT : __lx() {} + + __long __lx; + __short __lxx; + }; +#endif // _LIBCPP_CXX03_LANG enum {__n_words = sizeof(__ulx) / sizeof(size_type)}; @@ -785,8 +801,45 @@ size_type __words[__n_words]; }; +#ifdef _LIBCPP_CXX03_LANG + struct __rep + { + union + { + __long __l; + __short __s; + __raw __r; + }; + }; +#else + // Since C++03, unions can contain members with non-trivial special member functions. The corresponding special + // member functions of the union will be deleted and therefore must be explicitly defined. To support fancy pointers + // with non-trivial special member functions in __long, we must explicitly define a constructor, copy constructor + // and copy assignment operator in __rep. struct __rep { + _LIBCPP_CONSTEXPR_AFTER_CXX17 __rep() _NOEXCEPT : __l() {} + + _LIBCPP_CONSTEXPR_AFTER_CXX17 __rep(const __rep& __other) _NOEXCEPT + { + __l.__is_long_ = __other.__l.__is_long_; + __l.__cap_ = __other.__l.__cap_; + __l.__size_ = __other.__l.__size_; + __l.__data_ = __other.__l.__data_; + } + + _LIBCPP_CONSTEXPR_AFTER_CXX17 __rep& operator=(__rep __other) _NOEXCEPT + { + if (this != &__other) + { + __l.__is_long_ = __other.__l.__is_long_; + __l.__cap_ = __other.__l.__cap_; + __l.__size_ = __other.__l.__size_; + __l.__data_ = __other.__l.__data_; + } + return *this; + } + union { __long __l; @@ -794,6 +847,7 @@ __raw __r; }; }; +#endif // _LIBCPP_CXX03_LANG __compressed_pair<__rep, allocator_type> __r_; diff --git a/libcxx/test/libcxx/strings/basic.string/allocator_with_fancy_pointer.pass.cpp b/libcxx/test/libcxx/strings/basic.string/allocator_with_fancy_pointer.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/strings/basic.string/allocator_with_fancy_pointer.pass.cpp @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// Create string with custom allocator using fancy pointer and ensure compilation is successful + +#ifndef _LIBCPP_CXX03_LANG + +# include + +template +class NonTriviallyConstructiblePointer { + T* ptr; + +public: + NonTriviallyConstructiblePointer() {} + NonTriviallyConstructiblePointer(T* p) : ptr(p) {} + operator T*() const { return ptr; } + NonTriviallyConstructiblePointer(NonTriviallyConstructiblePointer&&) = default; + NonTriviallyConstructiblePointer(const NonTriviallyConstructiblePointer&) = default; + NonTriviallyConstructiblePointer& operator=(NonTriviallyConstructiblePointer&&) = default; + NonTriviallyConstructiblePointer& operator=(const NonTriviallyConstructiblePointer&) = default; + ~NonTriviallyConstructiblePointer() = default; + T& operator*() const { return *ptr; } + T* operator->() const { return ptr; } + operator bool() const { return ptr; } +}; + +template +class allocator : public std::allocator { + using base = std::allocator; + +public: + using pointer = NonTriviallyConstructiblePointer; + pointer allocate(size_t n) { return base::allocate(n); } + void deallocate(pointer p, size_t n) { return base::deallocate(p, n); } +}; + +int main() { std::basic_string, allocator > s; } + +#endif // _LIBCPP_CXX03_LANG diff --git a/libcxx/test/libcxx/strings/basic.string/allocator_with_fancy_pointer_non_trivial_destructor.fail.cpp b/libcxx/test/libcxx/strings/basic.string/allocator_with_fancy_pointer_non_trivial_destructor.fail.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/strings/basic.string/allocator_with_fancy_pointer_non_trivial_destructor.fail.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// Test that basic_string requires its allocator to use a pointer with a trivial destructor. + +#ifndef _LIBCPP_CXX03_LANG + +# include + +template +class NonTriviallyDestructiblePointer { + T* ptr; + +public: + NonTriviallyDestructiblePointer() {} // non-trivial constructor + NonTriviallyDestructiblePointer(T* p) : ptr(p) {} + operator T*() const { return ptr; } + NonTriviallyDestructiblePointer(NonTriviallyDestructiblePointer&&) = default; + NonTriviallyDestructiblePointer(const NonTriviallyDestructiblePointer&) = default; + NonTriviallyDestructiblePointer& operator=(NonTriviallyDestructiblePointer&&) = default; + NonTriviallyDestructiblePointer& operator=(const NonTriviallyDestructiblePointer&) = default; + ~NonTriviallyDestructiblePointer() {} // non-trivial destructor + T& operator*() const { return *ptr; } + T* operator->() const { return ptr; } + operator bool() const { return ptr; } +}; + +template +class allocator : public std::allocator { + using base = std::allocator; + +public: + using pointer = NonTriviallyDestructiblePointer; + pointer allocate(size_t n) { return base::allocate(n); } + void deallocate(pointer p, size_t n) { return base::deallocate(p, n); } +}; + +int main() { + std::basic_string, allocator > + s; // expected-error-re@* {{{{.*}} failed due to requirement 'is_trivially_destructible{{.*}} pointer type must have a trivial destructor.}} +} + +#endif // _LIBCPP_CXX03_LANG \ No newline at end of file