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 +_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/module.modulemap.in b/libcxx/include/module.modulemap.in index a86d6c6a43d0e..2a56b4f657ed3 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -350,6 +350,7 @@ module std_core [system] { header "__type_traits/is_volatile.h" export std_core.type_traits.integral_constant } + module is_within_lifetime { header "__type_traits/is_within_lifetime.h" } module lazy { header "__type_traits/lazy.h" } module make_32_64_or_128_bit { header "__type_traits/make_32_64_or_128_bit.h" } module make_const_lvalue_ref { header "__type_traits/make_const_lvalue_ref.h" } diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits index a6e0c1867566b..dab0c0640c389 100644 --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -454,6 +454,10 @@ namespace std template inline constexpr bool negation_v = negation::value; // since C++17 + // [meta.const.eval], constant evaluation context + constexpr bool is_constant_evaluated() noexcept; // C++20 + template + consteval bool is_within_lifetime(const T*) noexcept; // C++26 } */ @@ -559,6 +563,10 @@ namespace std # include <__type_traits/reference_converts_from_temporary.h> # endif +# if _LIBCPP_STD_VER >= 26 +# include <__type_traits/is_within_lifetime.h> +# endif + # include # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 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 # 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..ff3ecfbbc120c --- /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: gcc-15, apple-clang-17 + +// + +// LWG4138 +// std::is_within_lifetime shouldn't work when a function type is +// explicitly specified, even if it isn't evaluated + +#include + +template +consteval bool checked_is_within_lifetime(T* p) { + return p ? std::is_within_lifetime(p) : false; +} +static_assert(!checked_is_within_lifetime(nullptr)); +static_assert(!checked_is_within_lifetime(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..40c2273f1f862 --- /dev/null +++ b/libcxx/test/std/utilities/meta/meta.const.eval/is_within_lifetime.compile.pass.cpp @@ -0,0 +1,148 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-15, apple-clang-17 + +// + +// template +// consteval bool is_within_lifetime(const T*) noexcept; // C++26 + +#include +#include +#include + +#include "test_macros.h" + +ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval())), bool); +ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval())), bool); +ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval())), bool); +ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval())), bool); + +ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval())); +ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval())); +ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval())); +ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval())); + +template +concept is_within_lifetime_exists = requires(T t) { std::is_within_lifetime(t); }; + +struct S {}; + +static_assert(is_within_lifetime_exists); +static_assert(is_within_lifetime_exists); +static_assert(is_within_lifetime_exists); +static_assert(is_within_lifetime_exists); +static_assert(!is_within_lifetime_exists); // Not a pointer +static_assert(!is_within_lifetime_exists); // Not a pointer +static_assert(!is_within_lifetime_exists); // Not a pointer +static_assert(!is_within_lifetime_exists); // Doesn't accept pointer-to-data-member +static_assert(!is_within_lifetime_exists); // Doesn't accept pointer-to-member-function +static_assert(!is_within_lifetime_exists); // 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(&i))); + static_assert(std::is_within_lifetime(static_cast(&i))); + static_assert(std::is_within_lifetime(static_cast(const_cast(&i)))); + static_assert(std::is_within_lifetime(&i)); + static_assert(std::is_within_lifetime(const_cast(&i))); + static_assert(std::is_within_lifetime(static_cast(&i))); + static_assert(std::is_within_lifetime(static_cast(const_cast(&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(&i))); + assert(std::is_within_lifetime(static_cast(&i))); + assert(std::is_within_lifetime(static_cast(const_cast(&i)))); + assert(std::is_within_lifetime(&i)); + assert(std::is_within_lifetime(const_cast(&i))); + assert(std::is_within_lifetime(static_cast(&i))); + assert(std::is_within_lifetime(static_cast(const_cast(&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 +constexpr void does_escalate(T p) { + std::is_within_lifetime(p); +} +template > +constexpr bool check_escalated(int) { + return false; +} +template +constexpr bool check_escalated(long) { + return true; +} +static_assert(check_escalated(0), ""); +static_assert(check_escalated(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",