Skip to content

[libc++] Make forward_list constexpr as part of P3372R3 #129435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

winner245
Copy link
Contributor

@winner245 winner245 commented Mar 2, 2025

Fixes #128658.

@winner245 winner245 force-pushed the constexpr-forward_list branch from 7e3dd4c to e8ec4ce Compare March 2, 2025 13:36
Copy link

github-actions bot commented Mar 2, 2025

✅ With the latest revision this PR passed the Python code formatter.

@winner245 winner245 force-pushed the constexpr-forward_list branch 2 times, most recently from dde423e to 57338e2 Compare March 2, 2025 15:50
@winner245 winner245 marked this pull request as ready for review March 2, 2025 16:34
@winner245 winner245 requested a review from a team as a code owner March 2, 2025 16:34
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Mar 2, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 2, 2025

@llvm/pr-subscribers-libcxx

Author: Peng Liu (winner245)

Changes

Fixes #128658.


Patch is 165.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/129435.diff

76 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+2)
  • (modified) libcxx/include/__memory/allocation_guard.h (+11-9)
  • (modified) libcxx/include/forward_list (+278-203)
  • (modified) libcxx/include/version (+2)
  • (modified) libcxx/test/std/containers/Emplaceable.h (+12-10)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/compare.three_way.pass.cpp (+5-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/empty.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.access/front.pass.cpp (+13-3)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/alloc.compile.fail.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/alloc.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_copy.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_init.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_move.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_op_init.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_range.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_size_value.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/copy.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/copy_alloc.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/default.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/from_range.pass.cpp (+15-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/init.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/init_alloc.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/move.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/move_alloc.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/range.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/range_alloc.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp (+2-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size_value.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size_value_alloc.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.erasure/erase.pass.cpp (+13-5)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.erasure/erase_if.pass.cpp (+13-5)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.iter/before_begin.pass.cpp (+13-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.iter/iterators.pass.cpp (+20-7)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp (+15-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/clear.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/emplace_after.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/emplace_front.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/erase_after_many.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/erase_after_one.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_const.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_init.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_range.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_rv.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_size_value.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_range_after.pass.cpp (+18-5)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/pop_front.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp (+15-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/push_front_const.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/push_front_exception_safety.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/push_front_rv.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/resize_size.pass.cpp (+13-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/resize_size_value.pass.cpp (+12-3)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue.pass.cpp (+13-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_lvalue_pred.pass.cpp (+13-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue.pass.cpp (+13-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/merge_rvalue_pred.pass.cpp (+13-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/remove.pass.cpp (+18-9)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/remove_if.pass.cpp (+17-8)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/reverse.pass.cpp (+14-5)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/splice_after_flist.pass.cpp (+16-7)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/splice_after_one.pass.cpp (+17-8)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/splice_after_range.pass.cpp (+19-8)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/unique.pass.cpp (+12-3)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.ops/unique_pred.pass.cpp (+17-8)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/equal.pass.cpp (+13-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/member_swap.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/non_member_swap.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/relational.pass.cpp (+15-6)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/swap_noexcept.pass.cpp (+2-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/get_allocator.pass.cpp (+11-2)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/incomplete.pass.cpp (+13-4)
  • (modified) libcxx/test/std/containers/sequences/forwardlist/max_size.pass.cpp (+11-2)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/forward_list.version.compile.pass.cpp (+28)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+28)
  • (modified) libcxx/test/support/counting_predicates.h (+30-25)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+5)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index dcf9838edd74b..5ac25a69ac52e 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -416,6 +416,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_bitset``                                       ``202306L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_constexpr_forward_list``                       ``202502L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_constexpr_new``                                ``202406L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_constrained_equality``                         *unimplemented*
diff --git a/libcxx/include/__memory/allocation_guard.h b/libcxx/include/__memory/allocation_guard.h
index 66edcd92ed618..2fc485f4ed0ed 100644
--- a/libcxx/include/__memory/allocation_guard.h
+++ b/libcxx/include/__memory/allocation_guard.h
@@ -49,24 +49,26 @@ struct __allocation_guard {
   using _Size _LIBCPP_NODEBUG    = typename allocator_traits<_Alloc>::size_type;
 
   template <class _AllocT> // we perform the allocator conversion inside the constructor
-  _LIBCPP_HIDE_FROM_ABI explicit __allocation_guard(_AllocT __alloc, _Size __n)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit __allocation_guard(_AllocT __alloc, _Size __n)
       : __alloc_(std::move(__alloc)),
         __n_(__n),
         __ptr_(allocator_traits<_Alloc>::allocate(__alloc_, __n_)) // initialization order is important
   {}
 
-  _LIBCPP_HIDE_FROM_ABI ~__allocation_guard() _NOEXCEPT { __destroy(); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI ~__allocation_guard() _NOEXCEPT { __destroy(); }
 
-  _LIBCPP_HIDE_FROM_ABI __allocation_guard(const __allocation_guard&) = delete;
-  _LIBCPP_HIDE_FROM_ABI __allocation_guard(__allocation_guard&& __other) _NOEXCEPT
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __allocation_guard(const __allocation_guard&) = delete;
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __allocation_guard(__allocation_guard&& __other) _NOEXCEPT
       : __alloc_(std::move(__other.__alloc_)),
         __n_(__other.__n_),
         __ptr_(__other.__ptr_) {
     __other.__ptr_ = nullptr;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __allocation_guard& operator=(const __allocation_guard& __other) = delete;
-  _LIBCPP_HIDE_FROM_ABI __allocation_guard& operator=(__allocation_guard&& __other) _NOEXCEPT {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __allocation_guard&
+  operator=(const __allocation_guard& __other) = delete;
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __allocation_guard&
+  operator=(__allocation_guard&& __other) _NOEXCEPT {
     if (std::addressof(__other) != this) {
       __destroy();
 
@@ -79,17 +81,17 @@ struct __allocation_guard {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI _Pointer
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI _Pointer
   __release_ptr() _NOEXCEPT { // not called __release() because it's a keyword in objective-c++
     _Pointer __tmp = __ptr_;
     __ptr_         = nullptr;
     return __tmp;
   }
 
-  _LIBCPP_HIDE_FROM_ABI _Pointer __get() const _NOEXCEPT { return __ptr_; }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI _Pointer __get() const _NOEXCEPT { return __ptr_; }
 
 private:
-  _LIBCPP_HIDE_FROM_ABI void __destroy() _NOEXCEPT {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __destroy() _NOEXCEPT {
     if (__ptr_ != nullptr) {
       allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __n_);
     }
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 8c688611d5ee2..aca31bc37dc1d 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -295,9 +295,14 @@ struct __forward_node_traits {
                 "the _LIBCPP_ABI_FORWARD_LIST_REMOVE_NODE_POINTER_UB macro to silence this diagnostic.");
 #  endif
 
-  _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));
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI static __begin_node_pointer
+  __as_iter_node(__begin_node_pointer __p) {
+    return __p;
+  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _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));
+    return __p ? pointer_traits<__begin_node_pointer>::pointer_to(*__p) // fancy pointer
+               : static_cast<__begin_node_pointer>(nullptr);
   }
 };
 
@@ -308,11 +313,13 @@ struct __forward_begin_node {
 
   pointer __next_;
 
-  _LIBCPP_HIDE_FROM_ABI __forward_begin_node() : __next_(nullptr) {}
-  _LIBCPP_HIDE_FROM_ABI explicit __forward_begin_node(pointer __n) : __next_(__n) {}
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __forward_begin_node() : __next_(nullptr) {}
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit __forward_begin_node(pointer __n) : __next_(__n) {}
 
-  _LIBCPP_HIDE_FROM_ABI __begin_node_pointer __next_as_begin() const {
-    return static_cast<__begin_node_pointer>(__next_);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __begin_node_pointer __next_as_begin() const {
+    // return static_cast<__begin_node_pointer>(__next_);
+    return __next_ ? pointer_traits<__begin_node_pointer>::pointer_to(*__next_) // fancy pointer
+                   : static_cast<__begin_node_pointer>(nullptr);
   }
 };
 
@@ -336,18 +343,20 @@ private:
   };
 
 public:
-  _LIBCPP_HIDE_FROM_ABI _Tp& __get_value() { return __value_; }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI _Tp& __get_value() { return __value_; }
 #  else
 
 private:
   _ALIGNAS_TYPE(_Tp) char __buffer_[sizeof(_Tp)];
 
 public:
-  _LIBCPP_HIDE_FROM_ABI _Tp& __get_value() { return *std::__launder(reinterpret_cast<_Tp*>(&__buffer_)); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI _Tp& __get_value() {
+    return *std::__launder(reinterpret_cast<_Tp*>(&__buffer_));
+  }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI explicit __forward_list_node(_NodePtr __next) : _Base(__next) {}
-  _LIBCPP_HIDE_FROM_ABI ~__forward_list_node() {}
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit __forward_list_node(_NodePtr __next) : _Base(__next) {}
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI ~__forward_list_node() {}
 };
 
 template <class _Tp, class _Alloc = allocator<_Tp> >
@@ -358,25 +367,29 @@ class _LIBCPP_TEMPLATE_VIS __forward_list_const_iterator;
 template <class _NodePtr>
 class _LIBCPP_TEMPLATE_VIS __forward_list_iterator {
   typedef __forward_node_traits<_NodePtr> __traits;
+  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::__void_pointer __void_pointer;
 
   __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_));
-  }
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __get_unsafe_node_pointer() const {
-    return static_cast<__node_pointer>(static_cast<__void_pointer>(__ptr_));
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __begin_node_pointer __get_begin() const { return __ptr_; }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __node_pointer __get_unsafe_node_pointer() const {
+    // return static_cast<__node_pointer>(static_cast<__void_pointer>(__ptr_));
+    return __ptr_ ? pointer_traits<__node_pointer>::pointer_to(
+                        const_cast<__node_type&>(static_cast<const __node_type&>(*__ptr_))) // fancy pointer
+                  : static_cast<__node_pointer>(nullptr);
   }
 
-  _LIBCPP_HIDE_FROM_ABI explicit __forward_list_iterator(nullptr_t) _NOEXCEPT : __ptr_(nullptr) {}
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit __forward_list_iterator(nullptr_t) _NOEXCEPT
+      : __ptr_(nullptr) {}
 
+  _LIBCPP_CONSTEXPR_SINCE_CXX26
   _LIBCPP_HIDE_FROM_ABI explicit __forward_list_iterator(__begin_node_pointer __p) _NOEXCEPT
       : __ptr_(__traits::__as_iter_node(__p)) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit __forward_list_iterator(__node_pointer __p) _NOEXCEPT
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit __forward_list_iterator(__node_pointer __p) _NOEXCEPT
       : __ptr_(__traits::__as_iter_node(__p)) {}
 
   template <class, class>
@@ -391,27 +404,31 @@ public:
   typedef typename pointer_traits<__node_pointer>::difference_type difference_type;
   typedef __rebind_pointer_t<__node_pointer, value_type> pointer;
 
-  _LIBCPP_HIDE_FROM_ABI __forward_list_iterator() _NOEXCEPT : __ptr_(nullptr) {}
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __forward_list_iterator() _NOEXCEPT : __ptr_(nullptr) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __get_unsafe_node_pointer()->__get_value(); }
-  _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI reference operator*() const {
+    return __get_unsafe_node_pointer()->__get_value();
+  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
     return pointer_traits<pointer>::pointer_to(__get_unsafe_node_pointer()->__get_value());
   }
 
-  _LIBCPP_HIDE_FROM_ABI __forward_list_iterator& operator++() {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __forward_list_iterator& operator++() {
     __ptr_ = __traits::__as_iter_node(__ptr_->__next_);
     return *this;
   }
-  _LIBCPP_HIDE_FROM_ABI __forward_list_iterator operator++(int) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __forward_list_iterator operator++(int) {
     __forward_list_iterator __t(*this);
     ++(*this);
     return __t;
   }
 
-  friend _LIBCPP_HIDE_FROM_ABI bool operator==(const __forward_list_iterator& __x, const __forward_list_iterator& __y) {
+  friend _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI bool
+  operator==(const __forward_list_iterator& __x, const __forward_list_iterator& __y) {
     return __x.__ptr_ == __y.__ptr_;
   }
-  friend _LIBCPP_HIDE_FROM_ABI bool operator!=(const __forward_list_iterator& __x, const __forward_list_iterator& __y) {
+  friend _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI bool
+  operator!=(const __forward_list_iterator& __x, const __forward_list_iterator& __y) {
     return !(__x == __y);
   }
 };
@@ -429,18 +446,22 @@ class _LIBCPP_TEMPLATE_VIS __forward_list_const_iterator {
 
   __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_));
-  }
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __get_unsafe_node_pointer() const {
-    return static_cast<__node_pointer>(static_cast<__void_pointer>(__ptr_));
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __begin_node_pointer __get_begin() const { return __ptr_; }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __node_pointer __get_unsafe_node_pointer() const {
+    // return static_cast<__node_pointer>(static_cast<__void_pointer>(__ptr_));
+    return __ptr_ ? pointer_traits<__node_pointer>::pointer_to(
+                        const_cast<__node_type&>(static_cast<const __node_type&>(*__ptr_))) // fancy pointer
+                  : static_cast<__node_pointer>(nullptr);
   }
 
-  _LIBCPP_HIDE_FROM_ABI explicit __forward_list_const_iterator(nullptr_t) _NOEXCEPT : __ptr_(nullptr) {}
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit __forward_list_const_iterator(nullptr_t) _NOEXCEPT
+      : __ptr_(nullptr) {}
 
+  _LIBCPP_CONSTEXPR_SINCE_CXX26
   _LIBCPP_HIDE_FROM_ABI explicit __forward_list_const_iterator(__begin_node_pointer __p) _NOEXCEPT
       : __ptr_(__traits::__as_iter_node(__p)) {}
 
+  _LIBCPP_CONSTEXPR_SINCE_CXX26
   _LIBCPP_HIDE_FROM_ABI explicit __forward_list_const_iterator(__node_pointer __p) _NOEXCEPT
       : __ptr_(__traits::__as_iter_node(__p)) {}
 
@@ -454,30 +475,32 @@ public:
   typedef typename pointer_traits<__node_pointer>::difference_type difference_type;
   typedef __rebind_pointer_t<__node_pointer, const value_type> pointer;
 
-  _LIBCPP_HIDE_FROM_ABI __forward_list_const_iterator() _NOEXCEPT : __ptr_(nullptr) {}
-  _LIBCPP_HIDE_FROM_ABI __forward_list_const_iterator(__forward_list_iterator<__node_pointer> __p) _NOEXCEPT
-      : __ptr_(__p.__ptr_) {}
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __forward_list_const_iterator() _NOEXCEPT : __ptr_(nullptr) {}
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
+  __forward_list_const_iterator(__forward_list_iterator<__node_pointer> __p) _NOEXCEPT : __ptr_(__p.__ptr_) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __get_unsafe_node_pointer()->__get_value(); }
-  _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI reference operator*() const {
+    return __get_unsafe_node_pointer()->__get_value();
+  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
     return pointer_traits<pointer>::pointer_to(__get_unsafe_node_pointer()->__get_value());
   }
 
-  _LIBCPP_HIDE_FROM_ABI __forward_list_const_iterator& operator++() {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __forward_list_const_iterator& operator++() {
     __ptr_ = __traits::__as_iter_node(__ptr_->__next_);
     return *this;
   }
-  _LIBCPP_HIDE_FROM_ABI __forward_list_const_iterator operator++(int) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __forward_list_const_iterator operator++(int) {
     __forward_list_const_iterator __t(*this);
     ++(*this);
     return __t;
   }
 
-  friend _LIBCPP_HIDE_FROM_ABI bool
+  friend _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI bool
   operator==(const __forward_list_const_iterator& __x, const __forward_list_const_iterator& __y) {
     return __x.__ptr_ == __y.__ptr_;
   }
-  friend _LIBCPP_HIDE_FROM_ABI bool
+  friend _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI bool
   operator!=(const __forward_list_const_iterator& __x, const __forward_list_const_iterator& __y) {
     return !(__x == __y);
   }
@@ -501,48 +524,51 @@ protected:
 
   _LIBCPP_COMPRESSED_PAIR(__begin_node, __before_begin_, __node_allocator, __alloc_);
 
-  _LIBCPP_HIDE_FROM_ABI __begin_node_pointer __before_begin() _NOEXCEPT {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __begin_node_pointer __before_begin() _NOEXCEPT {
     return pointer_traits<__begin_node_pointer>::pointer_to(__before_begin_);
   }
-  _LIBCPP_HIDE_FROM_ABI __begin_node_pointer __before_begin() const _NOEXCEPT {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __begin_node_pointer __before_begin() const _NOEXCEPT {
     return pointer_traits<__begin_node_pointer>::pointer_to(const_cast<__begin_node&>(__before_begin_));
   }
 
   typedef __forward_list_iterator<__node_pointer> iterator;
   typedef __forward_list_const_iterator<__node_pointer> const_iterator;
 
-  _LIBCPP_HIDE_FROM_ABI __forward_list_base() _NOEXCEPT_(is_nothrow_default_constructible<__node_allocator>::value)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __forward_list_base()
+      _NOEXCEPT_(is_nothrow_default_constructible<__node_allocator>::value)
       : __before_begin_(__begin_node()) {}
-  _LIBCPP_HIDE_FROM_ABI explicit __forward_list_base(const allocator_type& __a)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit __forward_list_base(const allocator_type& __a)
       : __before_begin_(__begin_node()), __alloc_(__node_allocator(__a)) {}
-  _LIBCPP_HIDE_FROM_ABI explicit __forward_list_base(const __node_allocator& __a)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit __forward_list_base(const __node_allocator& __a)
       : __before_begin_(__begin_node()), __alloc_(__a) {}
 
 public:
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
   __forward_list_base(__forward_list_base&& __x) noexcept(is_nothrow_move_constructible<__node_allocator>::value);
-  _LIBCPP_HIDE_FROM_ABI __forward_list_base(__forward_list_base&& __x, const allocator_type& __a);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
+  __forward_list_base(__forward_list_base&& __x, const allocator_type& __a);
 #  endif // _LIBCPP_CXX03_LANG
 
   __forward_list_base(const __forward_list_base&)            = delete;
   __forward_list_base& operator=(const __forward_list_base&) = delete;
 
-  _LIBCPP_HIDE_FROM_ABI ~__forward_list_base();
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI ~__forward_list_base();
 
 protected:
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __forward_list_base& __x) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __forward_list_base& __x) {
     __copy_assign_alloc(__x, integral_constant<bool, __node_traits::propagate_on_container_copy_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base& __x)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base& __x)
       _NOEXCEPT_(!__node_traits::propagate_on_container_move_assignment::value ||
                  is_nothrow_move_assignable<__node_allocator>::value) {
     __move_assign_alloc(__x, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   }
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __create_node(__node_pointer __next, _Args&&... __args) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __node_pointer
+  __create_node(__node_pointer __next, _Args&&... __args) {
     __allocation_guard<__node_allocator> __guard(__alloc_, 1);
     // Begin the lifetime of the node itself. Note that this doesn't begin the lifetime of the value
     // held inside the node, since we need to use the allocator's construct() method for that.
@@ -557,7 +583,7 @@ protected:
     return __guard.__release_ptr();
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __delete_node(__node_pointer __node) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __delete_node(__node_pointer __node) {
     // For the same reason as above, we use the allocator's destroy() method for the value_type,
     // but not for the node itself.
     __node_traits::destroy(__alloc_, std::addressof(__node->__get_value()));
@@ -566,7 +592,7 @@ protected:
   }
 
 public:
-  _LIBCPP_HIDE_FROM_ABI void swap(__forward_list_base& __x)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(__forward_list_base& __x)
 #  if _LIBCPP_STD_VER >= 14
       _NOEXCEPT;
 #  else
@@ -574,18 +600,21 @@ public:
 #  endif
 
 protected:
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
 
 private:
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __forward_list_base&, false_type) {}
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __forward_list_base& __x, true_type) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __forward_list_base&, false_type) {
+  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void
+  __copy_assign_alloc(const __forward_list_base& __x, true_type) {
     if (__alloc_ != __x.__alloc_)
       clear();
     __alloc_ = __x.__alloc_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base&, false_type) _NOEXCEPT {}
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base& __x, true_type)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void
+  __move_assign_alloc(__forward_list_base&, false_type) _NOEXCEPT {}
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base& __x, true_type)
       _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
     __alloc_ = std::move(__x.__alloc_);
   }
@@ -594,14 +623,15 @@ private:
 #  ifndef _LIBCPP_CXX03_LANG
 
 template <class _Tp, class _Alloc>
-inline __forward_...
[truncated]

@winner245 winner245 force-pushed the constexpr-forward_list branch from 57338e2 to c6a1c3d Compare March 2, 2025 16:52
@winner245 winner245 force-pushed the constexpr-forward_list branch 2 times, most recently from 7307e6f to ca3436e Compare March 3, 2025 22:13
@winner245
Copy link
Contributor Author

I've just updated this patch mainly for GCC to correctly handle fancy pointers in constant evaluations. Before this update, the patch passed all stage 1 CIs except for GCC: stage1 (generic-gcc, gcc-14, g++-14). The reason for the failure is that the pointer_traits<Ptr>::pointer_to implementation in GCC is not fully constexpr.

In Clang, pointer_traits<Ptr>::pointer_to is fully constexpr since C++20 for both the raw pointer and fancy pointer specializations. So my implementation does not have any problems with Clang. However, for GCC, only the raw pointer specialization pointer_traits<T*>::pointer_to is constexpr since C++20, while the fancy pointer specialization is never made constexpr (In fact, the standard only requires that the raw pointer version is constexpr)! This leads to failure in constant evaluations in GCC.

To fix this, I take a somewhat tedious approach, which manually expands pointer_traits<Ptr>::pointer_to according to the pointer types of Ptr: for raw pointer, I do the simple static_cast; for fancy pointer types (e.g., min_pointer<T>), I directlty call Ptr::pointer_to on the pointer instead of pointer_traits<Ptr>::pointer_to, assuming Ptr provides a constexpr member
pointer_to (otherwise we have an expected failure, as in the case of calling pointer_traits<Ptr>::pointer_to directly). I am not sure if there is a better solution. This seems to have solved the constant evaluation failure with GCC. I would appreciate it if you have better suggestions.

@frederick-vs-ja
Copy link
Contributor

(In fact, the standard only requires that the raw pointer version is constexpr)

This is LWG3454 whose proposed resolution is speculatively implemented by libc++ and MSVC STL.

However, for GCC, only the raw pointer specialization pointer_traits<T*>::pointer_to is constexpr since C++20,

But how did this happen? libc++ is consistently marking that function constexpr for both Clang and GCC.

Comment on lines 302 to 312
_LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI static __begin_node_pointer __as_iter_node(__node_pointer __p) {
# ifdef _LIBCPP_CXX03_LANG
return static_cast<__begin_node_pointer>(__p);
# else
if constexpr (std::is_pointer<__begin_node_pointer>::value) {
return static_cast<__begin_node_pointer>(__p);
} else {
return __p ? __begin_node_pointer::pointer_to(*static_cast<__begin_node*>(std::addressof(*__p)))
: static_cast<__begin_node_pointer>(nullptr);
}
# endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be helpful to introduce __static_fancy_pointer_cast function template (in <__memory/pointer_traits.h> or a new internal header). The current changes don't seem correct to me (as libc++ backports C++11 fancy pointer mechanisms to C++03 mode). Let's see whether putting it into free functions would help.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the suggestion! Adding a __static_fancy_pointer_cast function template indeed makes a lot of sense, especially since it could also be beneficial for making <list> constexpr. Following your suggestion, I have introduced a __static_fancy_pointer_cast function template in <__memory/pointer_traits.h>.

@winner245
Copy link
Contributor Author

However, for GCC, only the raw pointer specialization pointer_traits<T*>::pointer_to is constexpr since C++20,

But how did this happen? libc++ is consistently marking that function constexpr for both Clang and GCC.

Thanks for your clarification! From what you said, I just realized that the problem I mentioned might not exist. I was debugging the GCC CI failure on my local platform using GCC, and I encountered an error with pointer_traits::pointer_to in constant evaluations (https://godbolt.org/z/j37Pch1cM), which made me think the GCC CI failure was due to the same problem I encountered in my local GCC platform. But as you said, we are using libc++'s <__memory/pointer_traits.h>, which has consistently made pointer_traits::pointer_to constexpr for both raw pointer and fancy pointer types. Therefore, I shouldn't have any problem with pointer_traits::pointer_to. Am I right?

After a bit more investigation of c6a1c3d, I realized that the GCC CI failure might be most likely due to a missing cast in __as_iter_node(__node_pointer __p). It was

  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI 
   static __begin_node_pointer __as_iter_node(__node_pointer __p) {
    return __p ? pointer_traits<__begin_node_pointer>::pointer_to(*__p) 
               : static_cast<__begin_node_pointer>(nullptr);
  }

For GCC constant evaluation, I need an extra static_cast:

  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI 
   static __begin_node_pointer __as_iter_node(__node_pointer __p) {
    return __p ? pointer_traits<__begin_node_pointer>::pointer_to(*static_cast<__begin_node*>(std::addressof(*__p))) 
               : static_cast<__begin_node_pointer>(nullptr);
  }

My latest push already included this cast, which actually fixed my real issue with the GCC CI. So maybe I should switch back to pointer_traits::pointer_to with this extra cast, then I should resolve the real problem I had. I am going to give it a try.

@winner245 winner245 force-pushed the constexpr-forward_list branch 3 times, most recently from 03ebbf9 to 60984d6 Compare March 4, 2025 18:27
Comment on lines 298 to 299
_LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI static __begin_node_pointer
__as_iter_node(__begin_node_pointer __p) {
return __p;
}
_LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI static __begin_node_pointer __as_iter_node(__node_pointer __p) {
return std::__static_fancy_pointer_cast<__begin_node_pointer>(__p);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change? What's incorrect about the old version?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old version was roughly equivalent to static_cast<Base*>(static_cast<void*>(ptr_to_derived)) (except that it works for fancy pointers), which didn't work in constant evaluation and possibly had UB (Godbolt link).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm quite certain this didn't have UB. It's defined behaviour to reinterpret_cast<Base*>(derived) if Base is the first object after all. Anyways, I think we'd be better off if we don't try to do the round tripping through a void* if we can get away with it (which IMO is quite reasonable). That would allow us to collapse the entire __as_iter_node construct. Could we do that in a separate patch and rebase this on top to simplify this whole thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old version fails in constant evaluation. I fixed it without resorting to the void* pointer. Given that the redundant __as_iter_node identity cast has already been fixed for forward_list, is there anything else that needs to be addressed before this patch is landed? I've just rebased the patch to resolve conflicts due to the removal of __as_iter_node.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philnik777 I am not sure if I understood your comment correctly in the first place. Were you suggesting me clean up the unnecessary static_casts before this one? If so, I've submitted a separate patch #130310 for that.

@winner245 winner245 force-pushed the constexpr-forward_list branch from 60984d6 to cb8d353 Compare March 7, 2025 04:32
@winner245 winner245 force-pushed the constexpr-forward_list branch from cb8d353 to b8c5630 Compare March 20, 2025 04:32
@winner245 winner245 force-pushed the constexpr-forward_list branch from b8c5630 to ffa779e Compare May 8, 2025 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc++] P3372R3: constexpr forward_list
4 participants