From 7d50c7b5c8dd87db0568af93a08f89af365f8bc0 Mon Sep 17 00:00:00 2001 From: Mital Ashok Date: Thu, 5 Sep 2024 19:39:52 +0100 Subject: [PATCH 1/2] [libc++] P2641R4: Checking if a `union` alternative is active (`std::is_within_lifetime`) --- libcxx/docs/FeatureTestMacroTable.rst | 2 +- libcxx/docs/ReleaseNotes/20.rst | 1 + libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/include/CMakeLists.txt | 1 + .../__type_traits/is_within_lifetime.h | 36 ++++++ libcxx/include/module.modulemap | 1 + libcxx/include/type_traits | 8 ++ libcxx/include/version | 4 +- libcxx/modules/std/type_traits.inc | 3 + .../meta/is_within_lifetime.verify.cpp | 32 +++++ .../type_traits.version.compile.pass.cpp | 6 +- .../version.version.compile.pass.cpp | 6 +- .../is_within_lifetime.compile.pass.cpp | 119 ++++++++++++++++++ .../generate_feature_test_macro_components.py | 3 +- 14 files changed, 214 insertions(+), 10 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 c909a4300db1a..ba927b36a44b5 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -446,7 +446,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/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst index 93d6027291ad9..c74181b9416c3 100644 --- a/libcxx/docs/ReleaseNotes/20.rst +++ b/libcxx/docs/ReleaseNotes/20.rst @@ -41,6 +41,7 @@ Implemented Papers - P2747R2: ``constexpr`` placement new (`Github `__) - P2609R3: Relaxing Ranges Just A Smidge (`Github `__) - P2985R0: A type trait for detecting virtual base classes (`Github `__) +- P2641R4: Checking if a ``union`` alternative is active (`Github `__) Improvements and New Features diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 8864b1ebe2889..c09fc6752040d 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -18,7 +18,7 @@ "`P2874R2 `__","Mandating Annex D Require No More","2023-06 (Varna)","","","" "`P2757R3 `__","Type-checking format args","2023-06 (Varna)","","","" "`P2637R3 `__","Member ``visit``","2023-06 (Varna)","|Complete|","19.0","" -"`P2641R4 `__","Checking if a ``union`` alternative is active","2023-06 (Varna)","","","" +"`P2641R4 `__","Checking if a ``union`` alternative is active","2023-06 (Varna)","|Complete|","20.0","" "`P1759R6 `__","Native handles and file streams","2023-06 (Varna)","|Complete|","18.0","" "`P2697R1 `__","Interfacing ``bitset`` with ``string_view``","2023-06 (Varna)","|Complete|","18.0","" "`P1383R2 `__","More ``constexpr`` for ```` and ````","2023-06 (Varna)","","","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 0f43916dae438..e47a489da3d96 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -822,6 +822,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..363f9c922a907 --- /dev/null +++ b/libcxx/include/__type_traits/is_within_lifetime.h @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// 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> +#include <__type_traits/is_function.h> + +#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 inline consteval bool is_within_lifetime(const _Tp* __p) noexcept { + if constexpr (is_function_v<_Tp>) { + // Avoid multiple diagnostics + static_assert(!is_function_v<_Tp>, "std::is_within_lifetime cannot explicitly specify T as a function type"); + return true; + } else { + 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 b/libcxx/include/module.modulemap index 3abc11723a5a9..f175bf91dc5c5 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -2008,6 +2008,7 @@ module std_private_type_traits_is_void [system export std_private_type_traits_integral_constant } module std_private_type_traits_is_volatile [system] { header "__type_traits/is_volatile.h" } +module std_private_type_traits_is_within_lifetime [system] { header "__type_traits/is_within_lifetime.h" } module std_private_type_traits_lazy [system] { header "__type_traits/lazy.h" } module std_private_type_traits_make_32_64_or_128_bit [system] { header "__type_traits/make_32_64_or_128_bit.h" } module std_private_type_traits_make_const_lvalue_ref [system] { header "__type_traits/make_const_lvalue_ref.h" } diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits index 5937d4fdc9e1a..39e22808e6ea2 100644 --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -416,6 +416,10 @@ namespace std template inline constexpr bool negation_v = negation::value; // 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 } */ @@ -517,6 +521,10 @@ namespace std # include <__type_traits/unwrap_ref.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 dc1d3fd268ce8..d5679dc7bd73b 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -539,7 +539,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 485a5ddf63aed..fd9ea15349e1c 100644 --- a/libcxx/modules/std/type_traits.inc +++ b/libcxx/modules/std/type_traits.inc @@ -314,6 +314,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..eef926660756b --- /dev/null +++ b/libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// LWG4138 +// std::is_within_lifetime shouldn't work when a function type is +// explicitly specified, even if it isn't evaluated + +#include +#include + +#include "test_macros.h" + +void fn(); + +int main(int, char**) { +#ifdef __cpp_lib_is_within_lifetime + constexpr bool _ = true ? false : std::is_within_lifetime(&fn); + // expected-error@*:* {{static assertion failed due to requirement '!is_function_v': std::is_within_lifetime cannot explicitly specify T as a function type}} +#else + // expected-no-diagnostics +#endif + return 0; +} 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 1cbf2699a95bc..22a1629637bd5 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 @@ -870,16 +870,16 @@ # 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 # if __cpp_lib_is_within_lifetime != 202306L # error "__cpp_lib_is_within_lifetime should have the value 202306L in c++26" # endif -# else // _LIBCPP_VERSION +# 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 a022c90e166c8..83cdb172fbcdf 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 @@ -7186,16 +7186,16 @@ # 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 # if __cpp_lib_is_within_lifetime != 202306L # error "__cpp_lib_is_within_lifetime should have the value 202306L in c++26" # endif -# else // _LIBCPP_VERSION +# 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..96d21249a499f --- /dev/null +++ b/libcxx/test/std/utilities/meta/meta.const.eval/is_within_lifetime.compile.pass.cpp @@ -0,0 +1,119 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// consteval bool is_within_lifetime(const T*) noexcept; // C++26 + +#include +#include + +#include "test_macros.h" + +#ifndef __cpp_lib_is_within_lifetime + +// Check that it doesn't exist if the feature test macro isn't defined (via ADL) +template +constexpr decltype(static_cast(is_within_lifetime(std::declval())), bool{}) is_within_lifetime_exists(int) { + return true; +} +template +constexpr bool is_within_lifetime_exists(long) { + return false; +} + +static_assert(!is_within_lifetime_exists*>(0), ""); + +#elif TEST_STD_VER < 26 +# error __cpp_lib_is_within_lifetime defined before C++26 +#else // defined(__cpp_lib_is_within_lifetime) && TEST_STD_VER >= 26 + +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-member +static_assert(!is_within_lifetime_exists); +static_assert(!is_within_lifetime_exists); // Doesn't match `const T*` + +constexpr int i = 0; +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_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)))); + +constexpr union { + int active; + int inactive; +} u{.active = 1}; +static_assert(std::is_within_lifetime(&u.active) && !std::is_within_lifetime(&u.inactive)); + +consteval bool f() { + union { + int active; + int inactive; + }; + if (std::is_within_lifetime(&active) || std::is_within_lifetime(&inactive)) + return false; + active = 1; + if (!std::is_within_lifetime(&active) || std::is_within_lifetime(&inactive)) + return false; + inactive = 1; + if (std::is_within_lifetime(&active) || !std::is_within_lifetime(&inactive)) + return false; + int j; + S s; + return std::is_within_lifetime(&j) && std::is_within_lifetime(&s); +} +static_assert(f()); + +# ifndef TEST_COMPILER_MSVC +// MSVC doesn't support consteval propagation :( +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), ""); +# endif // defined(TEST_COMPILER_MSVC) + +#endif // defined(__cpp_lib_is_within_lifetime) && TEST_STD_VER >= 26 diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 3bdd3adad15b4..f6817b24dfbf1 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -796,7 +796,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", From c12730b79158325d13ceb09c9c70c7251d41b232 Mon Sep 17 00:00:00 2001 From: Mital Ashok Date: Wed, 11 Sep 2024 11:18:04 +0100 Subject: [PATCH 2/2] Address comments --- .../__type_traits/is_within_lifetime.h | 4 +- .../meta/is_within_lifetime.verify.cpp | 22 +-- .../is_within_lifetime.compile.pass.cpp | 148 +++++++++++------- 3 files changed, 98 insertions(+), 76 deletions(-) diff --git a/libcxx/include/__type_traits/is_within_lifetime.h b/libcxx/include/__type_traits/is_within_lifetime.h index 363f9c922a907..870ea8560a090 100644 --- a/libcxx/include/__type_traits/is_within_lifetime.h +++ b/libcxx/include/__type_traits/is_within_lifetime.h @@ -20,11 +20,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER >= 26 && __has_builtin(__builtin_is_within_lifetime) template -_LIBCPP_HIDE_FROM_ABI inline consteval bool is_within_lifetime(const _Tp* __p) noexcept { +_LIBCPP_HIDE_FROM_ABI consteval bool is_within_lifetime(const _Tp* __p) noexcept { if constexpr (is_function_v<_Tp>) { // Avoid multiple diagnostics static_assert(!is_function_v<_Tp>, "std::is_within_lifetime cannot explicitly specify T as a function type"); - return true; + return false; } else { return __builtin_is_within_lifetime(__p); } 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 eef926660756b..140708776fdf3 100644 --- a/libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp +++ b/libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp @@ -6,7 +6,8 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23 +// UNSUPPORTED: clang-18, clang-19, gcc-14, apple-clang-16 // @@ -15,18 +16,11 @@ // explicitly specified, even if it isn't evaluated #include -#include -#include "test_macros.h" - -void fn(); - -int main(int, char**) { -#ifdef __cpp_lib_is_within_lifetime - constexpr bool _ = true ? false : std::is_within_lifetime(&fn); - // expected-error@*:* {{static assertion failed due to requirement '!is_function_v': std::is_within_lifetime cannot explicitly specify T as a function type}} -#else - // expected-no-diagnostics -#endif - return 0; +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@*:* {{static assertion failed due to requirement '!is_function_v': std::is_within_lifetime cannot explicitly specify T as a function type}} 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 96d21249a499f..d61c1b491d17c 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 @@ -6,7 +6,8 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23 +// UNSUPPORTED: clang-18, clang-19, gcc-14, apple-clang-16 // @@ -18,24 +19,6 @@ #include "test_macros.h" -#ifndef __cpp_lib_is_within_lifetime - -// Check that it doesn't exist if the feature test macro isn't defined (via ADL) -template -constexpr decltype(static_cast(is_within_lifetime(std::declval())), bool{}) is_within_lifetime_exists(int) { - return true; -} -template -constexpr bool is_within_lifetime_exists(long) { - return false; -} - -static_assert(!is_within_lifetime_exists*>(0), ""); - -#elif TEST_STD_VER < 26 -# error __cpp_lib_is_within_lifetime defined before C++26 -#else // defined(__cpp_lib_is_within_lifetime) && TEST_STD_VER >= 26 - 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); @@ -58,53 +41,101 @@ 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-member -static_assert(!is_within_lifetime_exists); -static_assert(!is_within_lifetime_exists); // Doesn't match `const T*` - -constexpr int i = 0; -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_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)))); - -constexpr union { - int active; - int inactive; -} u{.active = 1}; -static_assert(std::is_within_lifetime(&u.active) && !std::is_within_lifetime(&u.inactive)); +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() { - union { - int active; - int inactive; - }; - if (std::is_within_lifetime(&active) || std::is_within_lifetime(&inactive)) - return false; - active = 1; - if (!std::is_within_lifetime(&active) || std::is_within_lifetime(&inactive)) - return false; - inactive = 1; - if (std::is_within_lifetime(&active) || !std::is_within_lifetime(&inactive)) - return false; - int j; - S s; - return std::is_within_lifetime(&j) && std::is_within_lifetime(&s); + // 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()); -# ifndef TEST_COMPILER_MSVC -// MSVC doesn't support consteval propagation :( +// 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 > +template > constexpr bool check_escalated(int) { return false; } @@ -114,6 +145,3 @@ constexpr bool check_escalated(long) { } static_assert(check_escalated(0), ""); static_assert(check_escalated(0), ""); -# endif // defined(TEST_COMPILER_MSVC) - -#endif // defined(__cpp_lib_is_within_lifetime) && TEST_STD_VER >= 26