Skip to content

Commit

Permalink
[libc++] Take the ABI break for std::list's pointer UB unconditiona…
Browse files Browse the repository at this point in the history
…lly (#100585)

This ABI break only affects fancy pointer which have a different value
representation when pointing to a base of T instead of T itself. This
seems like a rather small set of fancy pointers, which themselves
already represent a very small niche. This patch swaps a pointer to T
with a pointer to base of T in a few library-internal types.
  • Loading branch information
philnik777 authored Sep 16, 2024
1 parent 50985d2 commit 01df775
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 98 deletions.
6 changes: 6 additions & 0 deletions libcxx/docs/ReleaseNotes/20.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ LLVM 21
ABI Affecting Changes
---------------------

- The ABI breaks for removing undefined behaviour in ``std::forward_list``, ``std::list``, ``std::map``, ``std::set``,
``std::multimap``, ``std::multiset``, ``std::unordered_map``, ``std::unordered_set``, ``std::unordered_multimap`` and
``std::unordered_multiset`` are now applied unconditionally. This only affects fancy pointers which have a different
value representation when pointing at the base of an internal node type instead of the type itself. A size or
alignment difference is diagnosed, but more subtle ABI breaks may result in unexpected behaviour.

- The internal structure ``__compressed_pair`` has been replaced with ``[[no_unique_address]]``. The ABI impact is:

- When using the Itanium ABI (most non-MSVC platforms), empty types are now placed at the beginning of the enclosing
Expand Down
15 changes: 11 additions & 4 deletions libcxx/include/__hash_table
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,18 @@ struct __hash_node_base {
typedef __hash_node_base __first_node;
typedef __rebind_pointer_t<_NodePtr, __first_node> __node_base_pointer;
typedef _NodePtr __node_pointer;

#if defined(_LIBCPP_ABI_FIX_UNORDERED_NODE_POINTER_UB)
typedef __node_base_pointer __next_pointer;
#else
typedef __conditional_t<is_pointer<__node_pointer>::value, __node_base_pointer, __node_pointer> __next_pointer;

// TODO(LLVM 22): Remove this check
#ifndef _LIBCPP_ABI_FIX_UNORDERED_NODE_POINTER_UB
static_assert(sizeof(__node_base_pointer) == sizeof(__node_pointer) && _LIBCPP_ALIGNOF(__node_base_pointer) ==
_LIBCPP_ALIGNOF(__node_pointer),
"It looks like you are using std::__hash_table (an implementation detail for the unordered containers) "
"with a fancy pointer type that thas a different representation depending on whether it points to a "
"__hash_table base pointer or a __hash_table node pointer (both of which are implementation details of "
"the standard library). This means that your ABI is being broken between LLVM 19 and LLVM 20. If you "
"don't care about your ABI being broken, define the _LIBCPP_ABI_TREE_REMOVE_NODE_POINTER_UB macro to "
"silence this diagnostic.");
#endif

__next_pointer __next_;
Expand Down
20 changes: 11 additions & 9 deletions libcxx/include/__tree
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,18 @@ struct __tree_node_base_types {

typedef __tree_end_node<__node_base_pointer> __end_node_type;
typedef __rebind_pointer_t<_VoidPtr, __end_node_type> __end_node_pointer;
#if defined(_LIBCPP_ABI_TREE_REMOVE_NODE_POINTER_UB)
typedef __end_node_pointer __parent_pointer;
#else
typedef __conditional_t< is_pointer<__end_node_pointer>::value, __end_node_pointer, __node_base_pointer>
__parent_pointer;

// TODO(LLVM 22): Remove this check
#ifndef _LIBCPP_ABI_TREE_REMOVE_NODE_POINTER_UB
static_assert(sizeof(__node_base_pointer) == sizeof(__end_node_pointer) && _LIBCPP_ALIGNOF(__node_base_pointer) ==
_LIBCPP_ALIGNOF(__end_node_pointer),
"It looks like you are using std::__tree (an implementation detail for (multi)map/set) with a fancy "
"pointer type that thas a different representation depending on whether it points to a __tree base "
"pointer or a __tree node pointer (both of which are implementation details of the standard library). "
"This means that your ABI is being broken between LLVM 19 and LLVM 20. If you don't care about your "
"ABI being broken, define the _LIBCPP_ABI_TREE_REMOVE_NODE_POINTER_UB macro to silence this "
"diagnostic.");
#endif

private:
Expand Down Expand Up @@ -605,12 +612,7 @@ public:
typedef _Tp __node_value_type;
typedef __rebind_pointer_t<_VoidPtr, __node_value_type> __node_value_type_pointer;
typedef __rebind_pointer_t<_VoidPtr, const __node_value_type> __const_node_value_type_pointer;
#if defined(_LIBCPP_ABI_TREE_REMOVE_NODE_POINTER_UB)
typedef typename __base::__end_node_pointer __iter_pointer;
#else
typedef __conditional_t< is_pointer<__node_pointer>::value, typename __base::__end_node_pointer, __node_pointer>
__iter_pointer;
#endif

private:
static_assert(!is_const<__node_type>::value, "_NodePtr should never be a pointer to const");
Expand Down
28 changes: 14 additions & 14 deletions libcxx/include/forward_list
Original file line number Diff line number Diff line change
Expand Up @@ -278,18 +278,20 @@ struct __forward_node_traits {
typedef __rebind_pointer_t<_NodePtr, __begin_node> __begin_node_pointer;
typedef __rebind_pointer_t<_NodePtr, void> __void_pointer;

#if defined(_LIBCPP_ABI_FORWARD_LIST_REMOVE_NODE_POINTER_UB)
typedef __begin_node_pointer __iter_node_pointer;
#else
typedef __conditional_t<is_pointer<__void_pointer>::value, __begin_node_pointer, __node_pointer> __iter_node_pointer;
// TODO(LLVM 22): Remove this check
#ifndef _LIBCPP_ABI_FORWARD_LIST_REMOVE_NODE_POINTER_UB
static_assert(sizeof(__begin_node_pointer) == sizeof(__node_pointer) && _LIBCPP_ALIGNOF(__begin_node_pointer) ==
_LIBCPP_ALIGNOF(__node_pointer),
"It looks like you are using std::forward_list with a fancy pointer type that thas a different "
"representation depending on whether it points to a forward_list base pointer or a forward_list node "
"pointer (both of which are implementation details of the standard library). This means that your ABI "
"is being broken between LLVM 19 and LLVM 20. If you don't care about your ABI being broken, define "
"the _LIBCPP_ABI_FORWARD_LIST_REMOVE_NODE_POINTER_UB macro to silence this diagnostic.");
#endif

typedef __conditional_t<is_same<__iter_node_pointer, __node_pointer>::value, __begin_node_pointer, __node_pointer>
__non_iter_node_pointer;

_LIBCPP_HIDE_FROM_ABI static __iter_node_pointer __as_iter_node(__iter_node_pointer __p) { return __p; }
_LIBCPP_HIDE_FROM_ABI static __iter_node_pointer __as_iter_node(__non_iter_node_pointer __p) {
return static_cast<__iter_node_pointer>(static_cast<__void_pointer>(__p));
_LIBCPP_HIDE_FROM_ABI static __begin_node_pointer __as_iter_node(__begin_node_pointer __p) { return __p; }
_LIBCPP_HIDE_FROM_ABI static __begin_node_pointer __as_iter_node(__node_pointer __p) {
return static_cast<__begin_node_pointer>(static_cast<__void_pointer>(__p));
}
};

Expand Down Expand Up @@ -351,10 +353,9 @@ class _LIBCPP_TEMPLATE_VIS __forward_list_iterator {
typedef __forward_node_traits<_NodePtr> __traits;
typedef typename __traits::__node_pointer __node_pointer;
typedef typename __traits::__begin_node_pointer __begin_node_pointer;
typedef typename __traits::__iter_node_pointer __iter_node_pointer;
typedef typename __traits::__void_pointer __void_pointer;

__iter_node_pointer __ptr_;
__begin_node_pointer __ptr_;

_LIBCPP_HIDE_FROM_ABI __begin_node_pointer __get_begin() const {
return static_cast<__begin_node_pointer>(static_cast<__void_pointer>(__ptr_));
Expand Down Expand Up @@ -417,10 +418,9 @@ class _LIBCPP_TEMPLATE_VIS __forward_list_const_iterator {
typedef typename __traits::__node_type __node_type;
typedef typename __traits::__node_pointer __node_pointer;
typedef typename __traits::__begin_node_pointer __begin_node_pointer;
typedef typename __traits::__iter_node_pointer __iter_node_pointer;
typedef typename __traits::__void_pointer __void_pointer;

__iter_node_pointer __ptr_;
__begin_node_pointer __ptr_;

_LIBCPP_HIDE_FROM_ABI __begin_node_pointer __get_begin() const {
return static_cast<__begin_node_pointer>(static_cast<__void_pointer>(__ptr_));
Expand Down
Loading

0 comments on commit 01df775

Please sign in to comment.