diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 8b8dce5083149..92dbc0cd8c727 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -58,5 +58,13 @@ Announcements About Future Releases ABI Affecting Changes --------------------- +- The ``const_iterator`` member type of ``std::deque`` is now corrected to hold a (possibly fancy) pointer to the + (possibly fancy) pointer allocated in the internal map. E.g. when the allocators use fancy pointers, the internal map + stores ``fancy_ptr`` objects, and the previous strategy accessed these objects via ``const fancy_ptr`` + lvalues, caused undefined behavior. Now ``const_iterator`` stores + ``fancy_ptr>`` instead of ``fancy_ptr>``, and ABI break can happen when + such two types have incompatible layouts. This is necessary for reducing undefined behavior and ``constexpr`` support + for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior. + Build System Changes -------------------- diff --git a/libcxx/include/deque b/libcxx/include/deque index 395a1076fd3c4..fb6c81a81633a 100644 --- a/libcxx/include/deque +++ b/libcxx/include/deque @@ -227,6 +227,7 @@ template # include <__type_traits/disjunction.h> # include <__type_traits/enable_if.h> # include <__type_traits/is_allocator.h> +# include <__type_traits/is_const.h> # include <__type_traits/is_convertible.h> # include <__type_traits/is_nothrow_assignable.h> # include <__type_traits/is_nothrow_constructible.h> @@ -234,6 +235,7 @@ template # include <__type_traits/is_same.h> # include <__type_traits/is_swappable.h> # include <__type_traits/is_trivially_relocatable.h> +# include <__type_traits/remove_reference.h> # include <__type_traits/type_identity.h> # include <__utility/forward.h> # include <__utility/move.h> @@ -270,6 +272,15 @@ struct __deque_block_size { static const _DiffType value = sizeof(_ValueType) < 256 ? 4096 / sizeof(_ValueType) : 16; }; +// When using fancy pointers, _MapPointer can be FancyPtr>, which causes strict +// aliasing violation in direct dereferencing because the internal map stores FancyPtr<_ValueType> objects. +// We need to transform the type to something like FancyPtr>. +template +using __get_deque_map_iterator _LIBCPP_NODEBUG = + __conditional_t >::value, + __rebind_pointer_t<_MapPointer, const __rebind_pointer_t<_MapPointer, _ValueType> >, + _MapPointer>; + template class __deque_iterator { - typedef _MapPointer __map_iterator; + using __map_iterator _LIBCPP_NODEBUG = __get_deque_map_iterator<_ValueType, _MapPointer>; public: typedef _Pointer pointer; @@ -461,7 +472,7 @@ private: __deque_iterator<_ValueType, _Pointer, _Reference, _MapPointer, _DiffType, _BlockSize>; public: - using __segment_iterator _LIBCPP_NODEBUG = _MapPointer; + using __segment_iterator _LIBCPP_NODEBUG = __get_deque_map_iterator<_ValueType, _MapPointer>; using __local_iterator _LIBCPP_NODEBUG = _Pointer; static _LIBCPP_HIDE_FROM_ABI __segment_iterator __segment(_Iterator __iter) { return __iter.__m_iter_; } @@ -509,14 +520,22 @@ public: using __map _LIBCPP_NODEBUG = __split_buffer; using __map_alloc_traits _LIBCPP_NODEBUG = allocator_traits<__pointer_allocator>; using __map_pointer _LIBCPP_NODEBUG = typename __map_alloc_traits::pointer; - using __map_const_pointer _LIBCPP_NODEBUG = typename allocator_traits<__const_pointer_allocator>::const_pointer; using __map_const_iterator _LIBCPP_NODEBUG = typename __map::const_iterator; + // Direct dereferencing __map_const_pointer possibly causes strict aliasing violation. + // We need to transform it to something like __map_alloc_traits::const_pointer for the const_iterator. + using __map_const_pointer _LIBCPP_NODEBUG = typename allocator_traits<__const_pointer_allocator>::const_pointer; + using __map_proper_const_pointer _LIBCPP_NODEBUG = typename __map_alloc_traits::const_pointer; + + using __iter_proper_const_pointer _LIBCPP_NODEBUG = __get_deque_map_iterator<_Tp, __map_const_pointer>; + static_assert(is_same::value, + "The fancy pointer types provided by the allocators cannot be restored by pointer_traits::rebind."); + using reference = value_type&; using const_reference = const value_type&; - using iterator = __deque_iterator; - using const_iterator = + using iterator = __deque_iterator; + using const_iterator = // Use of __map_const_pointer is merely kept for stability of const_iterator's true name. __deque_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; @@ -601,6 +620,11 @@ private: deque* const __base_; }; + static _LIBCPP_HIDE_FROM_ABI __iter_proper_const_pointer + __map_const_ptr_to_iter_const_ptr(__map_proper_const_pointer __mp) _NOEXCEPT { + return __mp ? pointer_traits<__iter_proper_const_pointer>::pointer_to(*__mp) : nullptr; + } + static const difference_type __block_size; __map __map_; @@ -725,8 +749,9 @@ public: } _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { - __map_const_pointer __mp = static_cast<__map_const_pointer>(__map_.begin() + __start_ / __block_size); - return const_iterator(__mp, __map_.empty() ? 0 : *__mp + __start_ % __block_size); + __map_proper_const_pointer __mp = __map_.begin() + __start_ / __block_size; + return const_iterator( + __map_const_ptr_to_iter_const_ptr(__mp), __map_.empty() ? 0 : *__mp + __start_ % __block_size); } _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { @@ -736,9 +761,9 @@ public: } _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { - size_type __p = size() + __start_; - __map_const_pointer __mp = static_cast<__map_const_pointer>(__map_.begin() + __p / __block_size); - return const_iterator(__mp, __map_.empty() ? 0 : *__mp + __p % __block_size); + size_type __p = size() + __start_; + __map_proper_const_pointer __mp = __map_.begin() + __p / __block_size; + return const_iterator(__map_const_ptr_to_iter_const_ptr(__mp), __map_.empty() ? 0 : *__mp + __p % __block_size); } _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); } @@ -2335,7 +2360,7 @@ deque<_Tp, _Allocator>::__move_and_check(iterator __f, iterator __l, iterator __ __fe = __fb + __bs; } if (__fb <= __vt && __vt < __fe) - __vt = (const_iterator(static_cast<__map_const_pointer>(__f.__m_iter_), __vt) -= __f - __r).__ptr_; + __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__f.__m_iter_), __vt) -= __f - __r).__ptr_; __r = std::move(__fb, __fe, __r); __n -= __bs; __f += __bs; @@ -2362,7 +2387,7 @@ deque<_Tp, _Allocator>::__move_backward_and_check(iterator __f, iterator __l, it __lb = __le - __bs; } if (__lb <= __vt && __vt < __le) - __vt = (const_iterator(static_cast<__map_const_pointer>(__l.__m_iter_), __vt) += __r - __l - 1).__ptr_; + __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__l.__m_iter_), __vt) += __r - __l - 1).__ptr_; __r = std::move_backward(__lb, __le, __r); __n -= __bs; __l -= __bs - 1; @@ -2388,7 +2413,7 @@ void deque<_Tp, _Allocator>::__move_construct_and_check(iterator __f, iterator _ __fe = __fb + __bs; } if (__fb <= __vt && __vt < __fe) - __vt = (const_iterator(static_cast<__map_const_pointer>(__f.__m_iter_), __vt) += __r - __f).__ptr_; + __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__f.__m_iter_), __vt) += __r - __f).__ptr_; for (; __fb != __fe; ++__fb, ++__r, ++__size()) __alloc_traits::construct(__a, std::addressof(*__r), std::move(*__fb)); __n -= __bs; @@ -2420,7 +2445,7 @@ void deque<_Tp, _Allocator>::__move_construct_backward_and_check( __lb = __le - __bs; } if (__lb <= __vt && __vt < __le) - __vt = (const_iterator(static_cast<__map_const_pointer>(__l.__m_iter_), __vt) -= __l - __r + 1).__ptr_; + __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__l.__m_iter_), __vt) -= __l - __r + 1).__ptr_; while (__le != __lb) { __alloc_traits::construct(__a, std::addressof(*--__r), std::move(*--__le)); --__start_; diff --git a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp index 30586d8b2422c..d65163cc09df9 100644 --- a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp +++ b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp @@ -18,6 +18,9 @@ template class small_pointer { std::uint16_t offset; + +public: + T& operator*() const; }; template