[libcxx-commits] [libcxx] [libc++] P3450R1: Extend `std::is_within_lifetime` (PR #201053)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jun 2 01:50:08 PDT 2026


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/201053

>From 49b16ceedaade93b6c749c0a3595d5f1d4ceb937 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 2 Jun 2026 16:16:00 +0800
Subject: [PATCH] [libc++] P3450R1: Extend `std::is_within_lifetime`

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/23.rst               |   1 +
 libcxx/docs/Status/Cxx2cPapers.csv            |   2 +-
 .../__type_traits/is_within_lifetime.h        |   4 +-
 libcxx/include/version                        |   4 +-
 .../meta/is_within_lifetime.verify.cpp        |  33 +++++-
 .../type_traits.version.compile.pass.cpp      |   4 +-
 .../version.version.compile.pass.cpp          |   4 +-
 .../is_within_lifetime.compile.pass.cpp       | 110 ++++++++++++++++--
 .../generate_feature_test_macro_components.py |   4 +-
 10 files changed, 142 insertions(+), 26 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 4eb15fa0eb131..32f19124c6b07 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -490,7 +490,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_is_virtual_base_of``                           ``202406L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_is_within_lifetime``                           ``202306L``
+    ``__cpp_lib_is_within_lifetime``                           ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_linalg``                                       *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e94897ffaf437..d3d45f3829490 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -51,6 +51,7 @@ Implemented Papers
 - P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P3450R1: Extend ``std::is_within_lifetime`` (`Github <https://llvm.org/PR189610>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 2132e80251657..6484a238163d6 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -198,7 +198,7 @@
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""
-"`P3450R1 <https://wg21.link/P3450R1>`__","Extend ``std::is_within_lifetime``","2026-03 (Croydon)","","","`#189610 <https://github.com/llvm/llvm-project/issues/189610>`__",""
+"`P3450R1 <https://wg21.link/P3450R1>`__","Extend ``std::is_within_lifetime``","2026-03 (Croydon)","|Complete|","23","`#189610 <https://github.com/llvm/llvm-project/issues/189610>`__",""
 "`P3982R2 <https://wg21.link/P3982R2>`__","Split ``strided_slice`` into ``extent_slice`` and ``range_slice`` for C++26","2026-03 (Croydon)","","","`#189611 <https://github.com/llvm/llvm-project/issues/189611>`__",""
 "`P4144R1 <https://wg21.link/P4144R1>`__","Remove ``span``'s ``initializer_list`` constructor for C++26","2026-03 (Croydon)","|Complete|","23","`#189612 <https://github.com/llvm/llvm-project/issues/189612>`__",""
 "`P3804R2 <https://wg21.link/P3804R2>`__","Iterating on ``parallel_scheduler``","2026-03 (Croydon)","","","`#189616 <https://github.com/llvm/llvm-project/issues/189616>`__",""
diff --git a/libcxx/include/__type_traits/is_within_lifetime.h b/libcxx/include/__type_traits/is_within_lifetime.h
index c789481cbd124..5f2c6456f8645 100644
--- a/libcxx/include/__type_traits/is_within_lifetime.h
+++ b/libcxx/include/__type_traits/is_within_lifetime.h
@@ -18,9 +18,9 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 26 && __has_builtin(__builtin_is_within_lifetime)
-template <class _Tp>
+template <class _Up = void, class _Tp>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI consteval bool is_within_lifetime(const _Tp* __p) noexcept {
-  return __builtin_is_within_lifetime(__p);
+  return __builtin_is_within_lifetime(__p) && __builtin_constant_p(static_cast<const volatile _Up*>(__p) && true);
 }
 #endif
 
diff --git a/libcxx/include/version b/libcxx/include/version
index 471eb3c104b53..c51bb0bb10cbd 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -162,7 +162,7 @@ __cpp_lib_is_scoped_enum                                202011L <type_traits>
 __cpp_lib_is_sufficiently_aligned                       202411L <memory>
 __cpp_lib_is_swappable                                  201603L <type_traits>
 __cpp_lib_is_virtual_base_of                            202406L <type_traits>
-__cpp_lib_is_within_lifetime                            202306L <type_traits>
+__cpp_lib_is_within_lifetime                            202603L <type_traits>
 __cpp_lib_jthread                                       201911L <stop_token> <thread>
 __cpp_lib_latch                                         201907L <latch>
 __cpp_lib_launder                                       201606L <new>
@@ -599,7 +599,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #   define __cpp_lib_is_virtual_base_of                 202406L
 # endif
 # if __has_builtin(__builtin_is_within_lifetime)
-#   define __cpp_lib_is_within_lifetime                 202306L
+#   define __cpp_lib_is_within_lifetime                 202603L
 # endif
 // # define __cpp_lib_linalg                               202311L
 # undef  __cpp_lib_mdspan
diff --git a/libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp b/libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp
index 0aa0f226d63ca..2db8173fa2cda 100644
--- a/libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp
+++ b/libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp
@@ -11,16 +11,41 @@
 
 // <type_traits>
 
+// template<class U = void, class T>
+//   consteval bool is_within_lifetime(const T* p) noexcept;
+// Mandates: static_cast<const volatile U*>(p) is well-formed.
+
 // LWG4138 <https://cplusplus.github.io/LWG/issue4138>
 // std::is_within_lifetime shouldn't work when a function type is
 // explicitly specified, even if it isn't evaluated
 
 #include <type_traits>
 
-template <class T>
+template <class U, class T>
 consteval bool checked_is_within_lifetime(T* p) {
-  return p ? std::is_within_lifetime<T>(p) : false;
+  return p ? std::is_within_lifetime<U, T>(p) : false;
 }
-static_assert(!checked_is_within_lifetime<int>(nullptr));
-static_assert(!checked_is_within_lifetime<void()>(nullptr));
+static_assert(!checked_is_within_lifetime<void, int>(nullptr));
+static_assert(!checked_is_within_lifetime<void, void()>(nullptr));
 // expected-error@*:* {{function pointer argument to '__builtin_is_within_lifetime' is not allowed}}
+
+static_assert(!checked_is_within_lifetime<long, int>(nullptr));
+// expected-error@*:* {{static_cast from 'const int *' to 'const volatile long *' is not allowed}}
+static_assert(!checked_is_within_lifetime<int(), int>(nullptr));
+// expected-error@*:* {{static_cast from 'const int *' to 'int (*)()' is not allowed}}
+
+struct B {};
+struct D1 : B {};
+struct D2 : protected B {};
+struct D3 : private B {};
+struct D4 : D1, D2, D3 {};
+struct D5 : virtual B {};
+
+static_assert(!checked_is_within_lifetime<D2, B>(nullptr));
+// expected-error@*:* {{cannot cast protected base class 'const B' to 'const volatile D2'}}
+static_assert(!checked_is_within_lifetime<D3, B>(nullptr));
+// expected-error@*:* {{cannot cast private base class 'const B' to 'const volatile D3'}}
+static_assert(!checked_is_within_lifetime<D4, B>(nullptr));
+// expected-error@*:* {{ambiguous cast from base 'B' to derived 'D4':}}
+static_assert(!checked_is_within_lifetime<D5, B>(nullptr));
+// expected-error@*:* {{cannot cast 'const B *' to 'const volatile D5 *' via virtual base 'B'}}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
index cb5c008f16bb3..c9f06ab95264c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
@@ -922,8 +922,8 @@
 #    ifndef __cpp_lib_is_within_lifetime
 #      error "__cpp_lib_is_within_lifetime should be defined in c++26"
 #    endif
-#    if __cpp_lib_is_within_lifetime != 202306L
-#      error "__cpp_lib_is_within_lifetime should have the value 202306L in c++26"
+#    if __cpp_lib_is_within_lifetime != 202603L
+#      error "__cpp_lib_is_within_lifetime should have the value 202603L in c++26"
 #    endif
 #  else
 #    ifdef __cpp_lib_is_within_lifetime
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 2aa52a64c6cf0..6a0c55fd236c8 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -7360,8 +7360,8 @@
 #    ifndef __cpp_lib_is_within_lifetime
 #      error "__cpp_lib_is_within_lifetime should be defined in c++26"
 #    endif
-#    if __cpp_lib_is_within_lifetime != 202306L
-#      error "__cpp_lib_is_within_lifetime should have the value 202306L in c++26"
+#    if __cpp_lib_is_within_lifetime != 202603L
+#      error "__cpp_lib_is_within_lifetime should have the value 202603L in c++26"
 #    endif
 #  else
 #    ifdef __cpp_lib_is_within_lifetime
diff --git a/libcxx/test/std/utilities/meta/meta.const.eval/is_within_lifetime.compile.pass.cpp b/libcxx/test/std/utilities/meta/meta.const.eval/is_within_lifetime.compile.pass.cpp
index f97b400f9a781..79f7611034f66 100644
--- a/libcxx/test/std/utilities/meta/meta.const.eval/is_within_lifetime.compile.pass.cpp
+++ b/libcxx/test/std/utilities/meta/meta.const.eval/is_within_lifetime.compile.pass.cpp
@@ -11,7 +11,7 @@
 
 // <type_traits>
 
-// template <class T>
+// template <class U = void, class T>
 //   consteval bool is_within_lifetime(const T*) noexcept; // C++26
 
 #include <cassert>
@@ -34,6 +34,9 @@ template <class T>
 concept is_within_lifetime_exists = requires(T t) { std::is_within_lifetime(t); };
 
 struct S {};
+struct D1 : S {};
+struct D2 : D1 {};
+struct D3 : S {};
 
 static_assert(is_within_lifetime_exists<int*>);
 static_assert(is_within_lifetime_exists<const int*>);
@@ -56,10 +59,55 @@ consteval bool f() {
     static_assert(std::is_within_lifetime(const_cast<int*>(&i)));
     static_assert(std::is_within_lifetime(static_cast<const void*>(&i)));
     static_assert(std::is_within_lifetime(static_cast<void*>(const_cast<int*>(&i))));
-    static_assert(std::is_within_lifetime<const int>(&i));
-    static_assert(std::is_within_lifetime<int>(const_cast<int*>(&i)));
-    static_assert(std::is_within_lifetime<const void>(static_cast<const void*>(&i)));
-    static_assert(std::is_within_lifetime<void>(static_cast<void*>(const_cast<int*>(&i))));
+    static_assert(std::is_within_lifetime<void, const int>(&i));
+    static_assert(std::is_within_lifetime<void, int>(const_cast<int*>(&i)));
+    static_assert(std::is_within_lifetime<void, const void>(static_cast<const void*>(&i)));
+    static_assert(std::is_within_lifetime<void, void>(static_cast<void*>(const_cast<int*>(&i))));
+  }
+  // Test well-definedness for casting to derived classes with constexpr variables whose lifetime is in a different
+  // constant expression
+  {
+    static constexpr S s;
+    static constexpr D1 d1;
+    static constexpr D2 d2;
+    static constexpr D3 d3;
+
+    static constexpr const S* pbase0 = &s;
+    static constexpr const S* pbase1 = &d1;
+    static constexpr const S* pbase2 = &d2;
+    static constexpr const S* pbase3 = &d3;
+
+    static_assert(std::is_within_lifetime<S>(pbase0));
+    static_assert(std::is_within_lifetime<S>(pbase1));
+    static_assert(std::is_within_lifetime<S>(pbase2));
+    static_assert(std::is_within_lifetime<S>(pbase3));
+
+    static_assert(!std::is_within_lifetime<D1>(pbase0));
+    static_assert(std::is_within_lifetime<D1>(pbase1));
+    static_assert(std::is_within_lifetime<D1>(pbase2));
+    static_assert(!std::is_within_lifetime<D1>(pbase3));
+
+    static_assert(!std::is_within_lifetime<D2>(pbase0));
+    static_assert(!std::is_within_lifetime<D2>(pbase1));
+    static_assert(std::is_within_lifetime<D2>(pbase2));
+    static_assert(!std::is_within_lifetime<D2>(pbase3));
+
+    static_assert(!std::is_within_lifetime<D3>(pbase0));
+    static_assert(!std::is_within_lifetime<D3>(pbase1));
+    static_assert(!std::is_within_lifetime<D3>(pbase2));
+    static_assert(std::is_within_lifetime<D3>(pbase3));
+
+    static constexpr const D1* pmid0 = &d1;
+    static constexpr const D1* pmid1 = &d2;
+
+    static_assert(std::is_within_lifetime<S>(pmid0));
+    static_assert(std::is_within_lifetime<S>(pmid1));
+
+    static_assert(std::is_within_lifetime<D1>(pmid0));
+    static_assert(std::is_within_lifetime<D1>(pmid1));
+
+    static_assert(!std::is_within_lifetime<D2>(pmid0));
+    static_assert(std::is_within_lifetime<D2>(pmid1));
   }
 
   {
@@ -78,10 +126,54 @@ consteval bool f() {
     assert(std::is_within_lifetime(const_cast<int*>(&i)));
     assert(std::is_within_lifetime(static_cast<const void*>(&i)));
     assert(std::is_within_lifetime(static_cast<void*>(const_cast<int*>(&i))));
-    assert(std::is_within_lifetime<const int>(&i));
-    assert(std::is_within_lifetime<int>(const_cast<int*>(&i)));
-    assert(std::is_within_lifetime<const void>(static_cast<const void*>(&i)));
-    assert(std::is_within_lifetime<void>(static_cast<void*>(const_cast<int*>(&i))));
+    assert((std::is_within_lifetime<void, const int>(&i)));
+    assert((std::is_within_lifetime<void, int>(const_cast<int*>(&i))));
+    assert((std::is_within_lifetime<void, const void>(static_cast<const void*>(&i))));
+    assert((std::is_within_lifetime<void, void>(static_cast<void*>(const_cast<int*>(&i)))));
+  }
+  // Test well-definedness for casting to derived classes with varibles inside the same constant expression
+  {
+    S s;
+    D1 d1;
+    D2 d2;
+    D3 d3;
+
+    S* pbase0 = &s;
+    S* pbase1 = &d1;
+    S* pbase2 = &d2;
+    S* pbase3 = &d3;
+
+    assert(std::is_within_lifetime<S>(pbase0));
+    assert(std::is_within_lifetime<S>(pbase1));
+    assert(std::is_within_lifetime<S>(pbase2));
+    assert(std::is_within_lifetime<S>(pbase3));
+
+    assert(!std::is_within_lifetime<D1>(pbase0));
+    assert(std::is_within_lifetime<D1>(pbase1));
+    assert(std::is_within_lifetime<D1>(pbase2));
+    assert(!std::is_within_lifetime<D1>(pbase3));
+
+    assert(!std::is_within_lifetime<D2>(pbase0));
+    assert(!std::is_within_lifetime<D2>(pbase1));
+    assert(std::is_within_lifetime<D2>(pbase2));
+    assert(!std::is_within_lifetime<D2>(pbase3));
+
+    assert(!std::is_within_lifetime<D3>(pbase0));
+    assert(!std::is_within_lifetime<D3>(pbase1));
+    assert(!std::is_within_lifetime<D3>(pbase2));
+    assert(std::is_within_lifetime<D3>(pbase3));
+
+    D1* pmid0 = &d1;
+    D1* pmid1 = &d2;
+
+    assert(std::is_within_lifetime<S>(pmid0));
+    assert(std::is_within_lifetime<S>(pmid1));
+
+    assert(std::is_within_lifetime<D1>(pmid0));
+    assert(std::is_within_lifetime<D1>(pmid1));
+
+    assert(!std::is_within_lifetime<D2>(pmid0));
+    assert(std::is_within_lifetime<D2>(pmid1));
   }
   // Anonymous union
   {
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 050dab93bea29..fff955853d159 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -876,10 +876,8 @@ def add_version_header(tc):
         },
         {
             "name": "__cpp_lib_is_within_lifetime",
-            # Note this name was changed from "__cpp_lib_within_lifetime" when the paper was adopted
-            # https://github.com/cplusplus/draft/commit/0facada4cadd97e1ba15bfaea76a804f1dc5c309
             "values": {
-                "c++26": 202306  # P2641R4 Checking if a union alternative is active
+                "c++26": 202603,
             },
             "headers": ["type_traits"],
             "test_suite_guard": "__has_builtin(__builtin_is_within_lifetime)",



More information about the libcxx-commits mailing list