[libcxx-commits] [libcxx] [libc++] P2641R4: Checking if a `union` alternative is active (`std::is_within_lifetime`) (PR #165243)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Oct 27 05:14:11 PDT 2025
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/165243
Address comments
Address comments
>From 7de4410f0538a54d1000eaf22ff85ec5438a9035 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Thu, 5 Sep 2024 19:39:52 +0100
Subject: [PATCH] [libc++] P2641R4: Checking if a `union` alternative is active
(`std::is_within_lifetime`)
Address comments
Address comments
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/include/CMakeLists.txt | 1 +
.../__type_traits/is_within_lifetime.h | 29 ++++
libcxx/include/type_traits | 4 +
libcxx/include/version | 4 +-
libcxx/modules/std/type_traits.inc | 3 +
.../meta/is_within_lifetime.verify.cpp | 26 ++++
.../type_traits.version.compile.pass.cpp | 4 +-
.../version.version.compile.pass.cpp | 4 +-
.../is_within_lifetime.compile.pass.cpp | 147 ++++++++++++++++++
.../generate_feature_test_macro_components.py | 3 +-
11 files changed, 220 insertions(+), 7 deletions(-)
create mode 100644 libcxx/include/__type_traits/is_within_lifetime.h
create mode 100644 libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp
create mode 100644 libcxx/test/std/utilities/meta/meta.const.eval/is_within_lifetime.compile.pass.cpp
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8fba6db871f08..ed0e61a682964 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -474,7 +474,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_is_virtual_base_of`` ``202406L``
---------------------------------------------------------- -----------------
- ``__cpp_lib_is_within_lifetime`` *unimplemented*
+ ``__cpp_lib_is_within_lifetime`` ``202306L``
---------------------------------------------------------- -----------------
``__cpp_lib_linalg`` *unimplemented*
---------------------------------------------------------- -----------------
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 37259a7e6e7dd..f4c78801f9c55 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -877,6 +877,7 @@ set(files
__type_traits/is_valid_expansion.h
__type_traits/is_void.h
__type_traits/is_volatile.h
+ __type_traits/is_within_lifetime.h
__type_traits/lazy.h
__type_traits/make_32_64_or_128_bit.h
__type_traits/make_const_lvalue_ref.h
diff --git a/libcxx/include/__type_traits/is_within_lifetime.h b/libcxx/include/__type_traits/is_within_lifetime.h
new file mode 100644
index 0000000000000..242f2adaf357b
--- /dev/null
+++ b/libcxx/include/__type_traits/is_within_lifetime.h
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___TYPE_TRAITS_IS_WITHIN_LIFETIME_H
+#define _LIBCPP___TYPE_TRAITS_IS_WITHIN_LIFETIME_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26 && __has_builtin(__builtin_is_within_lifetime)
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI consteval bool is_within_lifetime(const _Tp* __p) noexcept {
+ return __builtin_is_within_lifetime(__p);
+}
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_IS_WITHIN_LIFETIME_H
diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits
index a6e0c1867566b..a4b1fb8a66f3f 100644
--- a/libcxx/include/type_traits
+++ b/libcxx/include/type_traits
@@ -454,6 +454,10 @@ namespace std
template<class B> inline constexpr bool negation_v
= negation<B>::value; // since C++17
+ // [meta.const.eval], constant evaluation context
+ constexpr bool is_constant_evaluated() noexcept; // C++20
+ template<class T>
+ consteval bool is_within_lifetime(const T*) noexcept; // C++26
}
*/
diff --git a/libcxx/include/version b/libcxx/include/version
index 0fef1bb87cf60..2d5fc4d62384a 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -582,7 +582,9 @@ __cpp_lib_void_t 201411L <type_traits>
# if __has_builtin(__builtin_is_virtual_base_of)
# define __cpp_lib_is_virtual_base_of 202406L
# endif
-// # define __cpp_lib_is_within_lifetime 202306L
+# if __has_builtin(__builtin_is_within_lifetime)
+# define __cpp_lib_is_within_lifetime 202306L
+# endif
// # define __cpp_lib_linalg 202311L
# undef __cpp_lib_mdspan
# define __cpp_lib_mdspan 202406L
diff --git a/libcxx/modules/std/type_traits.inc b/libcxx/modules/std/type_traits.inc
index 6823c86ed153b..4e49ed8f255c7 100644
--- a/libcxx/modules/std/type_traits.inc
+++ b/libcxx/modules/std/type_traits.inc
@@ -330,6 +330,9 @@ export namespace std {
// [meta.const.eval], constant evaluation context
using std::is_constant_evaluated;
+#if _LIBCPP_STD_VER >= 26 && __has_builtin(__builtin_is_within_lifetime)
+ using std::is_within_lifetime;
+#endif
// [depr.meta.types]
using std::aligned_storage;
diff --git a/libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp b/libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp
new file mode 100644
index 0000000000000..ccb2cdfcab5c8
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+// UNSUPPORTED: clang-18, clang-19, gcc-14, apple-clang-16
+
+// <type_traits>
+
+// 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>
+consteval bool checked_is_within_lifetime(T* p) {
+ return p ? std::is_within_lifetime<T>(p) : false;
+}
+static_assert(!checked_is_within_lifetime<int>(nullptr));
+static_assert(!checked_is_within_lifetime<void()>(nullptr));
+// expected-error@*:* {{function pointer argument to '__builtin_is_within_lifetime' is not allowed}}
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 0074f3bf4cc57..cb5c008f16bb3 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
@@ -918,7 +918,7 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
+# if __has_builtin(__builtin_is_within_lifetime)
# ifndef __cpp_lib_is_within_lifetime
# error "__cpp_lib_is_within_lifetime should be defined in c++26"
# endif
@@ -927,7 +927,7 @@
# endif
# else
# ifdef __cpp_lib_is_within_lifetime
-# error "__cpp_lib_is_within_lifetime should not be defined because it is unimplemented in libc++!"
+# error "__cpp_lib_is_within_lifetime should not be defined when the requirement '__has_builtin(__builtin_is_within_lifetime)' is not met!"
# endif
# endif
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 05af1fb0cf14b..2077d821c048f 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
@@ -7256,7 +7256,7 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
+# if __has_builtin(__builtin_is_within_lifetime)
# ifndef __cpp_lib_is_within_lifetime
# error "__cpp_lib_is_within_lifetime should be defined in c++26"
# endif
@@ -7265,7 +7265,7 @@
# endif
# else
# ifdef __cpp_lib_is_within_lifetime
-# error "__cpp_lib_is_within_lifetime should not be defined because it is unimplemented in libc++!"
+# error "__cpp_lib_is_within_lifetime should not be defined when the requirement '__has_builtin(__builtin_is_within_lifetime)' is not met!"
# endif
# endif
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
new file mode 100644
index 0000000000000..d61c1b491d17c
--- /dev/null
+++ b/libcxx/test/std/utilities/meta/meta.const.eval/is_within_lifetime.compile.pass.cpp
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+// UNSUPPORTED: clang-18, clang-19, gcc-14, apple-clang-16
+
+// <type_traits>
+
+// template <class T>
+// consteval bool is_within_lifetime(const T*) noexcept; // C++26
+
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+
+ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<int*>())), bool);
+ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<const int*>())), bool);
+ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<void*>())), bool);
+ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<const void*>())), bool);
+
+ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<int*>()));
+ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<const int*>()));
+ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<void*>()));
+ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<const void*>()));
+
+template <class T>
+concept is_within_lifetime_exists = requires(T t) { std::is_within_lifetime(t); };
+
+struct S {};
+
+static_assert(is_within_lifetime_exists<int*>);
+static_assert(is_within_lifetime_exists<const int*>);
+static_assert(is_within_lifetime_exists<void*>);
+static_assert(is_within_lifetime_exists<const void*>);
+static_assert(!is_within_lifetime_exists<int>); // Not a pointer
+static_assert(!is_within_lifetime_exists<decltype(nullptr)>); // Not a pointer
+static_assert(!is_within_lifetime_exists<void() const>); // Not a pointer
+static_assert(!is_within_lifetime_exists<int S::*>); // Doesn't accept pointer-to-data-member
+static_assert(!is_within_lifetime_exists<void (S::*)()>); // Doesn't accept pointer-to-member-function
+static_assert(!is_within_lifetime_exists<void (*)()>); // Doesn't match `const T*`
+
+consteval bool f() {
+ // Test that it works with global variables whose lifetime is in a
+ // different constant expression
+ {
+ static constexpr int i = 0;
+ static_assert(std::is_within_lifetime(&i));
+ // (Even when cast to a different type)
+ 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 constexpr union {
+ int member1;
+ int member2;
+ } u{.member2 = 1};
+ static_assert(!std::is_within_lifetime(&u.member1) && std::is_within_lifetime(&u.member2));
+ }
+
+ // Test that it works for varibles inside the same constant expression
+ {
+ int i = 0;
+ assert(std::is_within_lifetime(&i));
+ // (Even when cast to a different type)
+ 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))));
+ }
+ // Anonymous union
+ {
+ union {
+ int member1;
+ int member2;
+ };
+ assert(!std::is_within_lifetime(&member1) && !std::is_within_lifetime(&member2));
+ member1 = 1;
+ assert(std::is_within_lifetime(&member1) && !std::is_within_lifetime(&member2));
+ member2 = 1;
+ assert(!std::is_within_lifetime(&member1) && std::is_within_lifetime(&member2));
+ }
+ // Variant members
+ {
+ struct X {
+ union {
+ int member1;
+ int member2;
+ };
+ } x;
+ assert(!std::is_within_lifetime(&x.member1) && !std::is_within_lifetime(&x.member2));
+ x.member1 = 1;
+ assert(std::is_within_lifetime(&x.member1) && !std::is_within_lifetime(&x.member2));
+ x.member2 = 1;
+ assert(!std::is_within_lifetime(&x.member1) && std::is_within_lifetime(&x.member2));
+ }
+ // Unions
+ {
+ union X {
+ int member1;
+ int member2;
+ } x;
+ assert(!std::is_within_lifetime(&x.member1) && !std::is_within_lifetime(&x.member2));
+ x.member1 = 1;
+ assert(std::is_within_lifetime(&x.member1) && !std::is_within_lifetime(&x.member2));
+ x.member2 = 1;
+ assert(!std::is_within_lifetime(&x.member1) && std::is_within_lifetime(&x.member2));
+ }
+ {
+ S s; // uninitialised
+ assert(std::is_within_lifetime(&s));
+ }
+
+ return true;
+}
+static_assert(f());
+
+// Check that it is a consteval (and consteval-propagating) function
+// (i.e., taking the address of below will fail because it will be an immediate function)
+template <typename T>
+constexpr void does_escalate(T p) {
+ std::is_within_lifetime(p);
+}
+template <typename T, void (*)(T) = &does_escalate<T>>
+constexpr bool check_escalated(int) {
+ return false;
+}
+template <typename T>
+constexpr bool check_escalated(long) {
+ return true;
+}
+static_assert(check_escalated<int*>(0), "");
+static_assert(check_escalated<void*>(0), "");
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f6f252751b3e3..c399942714473 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -863,7 +863,8 @@ def add_version_header(tc):
"c++26": 202306 # P2641R4 Checking if a union alternative is active
},
"headers": ["type_traits"],
- "unimplemented": True,
+ "test_suite_guard": "__has_builtin(__builtin_is_within_lifetime)",
+ "libcxx_guard": "__has_builtin(__builtin_is_within_lifetime)",
},
{
"name": "__cpp_lib_jthread",
More information about the libcxx-commits
mailing list