Skip to content

Commit e88424c

Browse files
committed
[libc++] P2641R4: Checking if a union alternative is active (std::is_within_lifetime)
1 parent 4d819da commit e88424c

File tree

14 files changed

+214
-10
lines changed

14 files changed

+214
-10
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ Status
446446
---------------------------------------------------------- -----------------
447447
``__cpp_lib_is_virtual_base_of`` ``202406L``
448448
---------------------------------------------------------- -----------------
449-
``__cpp_lib_is_within_lifetime`` *unimplemented*
449+
``__cpp_lib_is_within_lifetime`` ``202306L``
450450
---------------------------------------------------------- -----------------
451451
``__cpp_lib_linalg`` *unimplemented*
452452
---------------------------------------------------------- -----------------

libcxx/docs/ReleaseNotes/20.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Implemented Papers
4141
- P2747R2: ``constexpr`` placement new (`Github <https://github.com/llvm/llvm-project/issues/105427>`__)
4242
- P2609R3: Relaxing Ranges Just A Smidge (`Github <https://github.com/llvm/llvm-project/issues/105253>`__)
4343
- P2985R0: A type trait for detecting virtual base classes (`Github <https://github.com/llvm/llvm-project/issues/105432>`__)
44+
- P2641R4: Checking if a ``union`` alternative is active (`Github <https://github.com/llvm/llvm-project/issues/105381>`__)
4445

4546

4647
Improvements and New Features

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"`P2874R2 <https://wg21.link/P2874R2>`__","Mandating Annex D Require No More","2023-06 (Varna)","","",""
1919
"`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","",""
2020
"`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19.0",""
21-
"`P2641R4 <https://wg21.link/P2641R4>`__","Checking if a ``union`` alternative is active","2023-06 (Varna)","","",""
21+
"`P2641R4 <https://wg21.link/P2641R4>`__","Checking if a ``union`` alternative is active","2023-06 (Varna)","|Complete|","20.0",""
2222
"`P1759R6 <https://wg21.link/P1759R6>`__","Native handles and file streams","2023-06 (Varna)","|Complete|","18.0",""
2323
"`P2697R1 <https://wg21.link/P2697R1>`__","Interfacing ``bitset`` with ``string_view``","2023-06 (Varna)","|Complete|","18.0",""
2424
"`P1383R2 <https://wg21.link/P1383R2>`__","More ``constexpr`` for ``<cmath>`` and ``<complex>``","2023-06 (Varna)","","",""

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ set(files
822822
__type_traits/is_valid_expansion.h
823823
__type_traits/is_void.h
824824
__type_traits/is_volatile.h
825+
__type_traits/is_within_lifetime.h
825826
__type_traits/lazy.h
826827
__type_traits/make_32_64_or_128_bit.h
827828
__type_traits/make_const_lvalue_ref.h
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___TYPE_TRAITS_IS_WITHIN_LIFETIME_H
10+
#define _LIBCPP___TYPE_TRAITS_IS_WITHIN_LIFETIME_H
11+
12+
#include <__config>
13+
#include <__type_traits/is_function.h>
14+
15+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
16+
# pragma GCC system_header
17+
#endif
18+
19+
_LIBCPP_BEGIN_NAMESPACE_STD
20+
21+
#if _LIBCPP_STD_VER >= 26 && __has_builtin(__builtin_is_within_lifetime)
22+
template <class _Tp>
23+
_LIBCPP_HIDE_FROM_ABI inline consteval bool is_within_lifetime(const _Tp* __p) noexcept {
24+
if constexpr (is_function_v<_Tp>) {
25+
// Avoid multiple diagnostics
26+
static_assert(!is_function_v<_Tp>, "std::is_within_lifetime<T> cannot explicitly specify T as a function type");
27+
return true;
28+
} else {
29+
return __builtin_is_within_lifetime(__p);
30+
}
31+
}
32+
#endif
33+
34+
_LIBCPP_END_NAMESPACE_STD
35+
36+
#endif // _LIBCPP___TYPE_TRAITS_IS_WITHIN_LIFETIME_H

libcxx/include/module.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2008,6 +2008,7 @@ module std_private_type_traits_is_void [system
20082008
export std_private_type_traits_integral_constant
20092009
}
20102010
module std_private_type_traits_is_volatile [system] { header "__type_traits/is_volatile.h" }
2011+
module std_private_type_traits_is_within_lifetime [system] { header "__type_traits/is_within_lifetime.h" }
20112012
module std_private_type_traits_lazy [system] { header "__type_traits/lazy.h" }
20122013
module std_private_type_traits_make_32_64_or_128_bit [system] { header "__type_traits/make_32_64_or_128_bit.h" }
20132014
module std_private_type_traits_make_const_lvalue_ref [system] { header "__type_traits/make_const_lvalue_ref.h" }

libcxx/include/type_traits

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,10 @@ namespace std
416416
template<class B>
417417
inline constexpr bool negation_v = negation<B>::value; // C++17
418418
419+
// [meta.const.eval], constant evaluation context
420+
constexpr bool is_constant_evaluated() noexcept; // C++20
421+
template<class T>
422+
consteval bool is_within_lifetime(const T*) noexcept; // C++26
419423
}
420424
421425
*/
@@ -517,6 +521,10 @@ namespace std
517521
# include <__type_traits/unwrap_ref.h>
518522
#endif
519523

524+
#if _LIBCPP_STD_VER >= 26
525+
# include <__type_traits/is_within_lifetime.h>
526+
#endif
527+
520528
#include <version>
521529

522530
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)

libcxx/include/version

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,9 @@ __cpp_lib_void_t 201411L <type_traits>
539539
# if __has_builtin(__builtin_is_virtual_base_of)
540540
# define __cpp_lib_is_virtual_base_of 202406L
541541
# endif
542-
// # define __cpp_lib_is_within_lifetime 202306L
542+
# if __has_builtin(__builtin_is_within_lifetime)
543+
# define __cpp_lib_is_within_lifetime 202306L
544+
# endif
543545
// # define __cpp_lib_linalg 202311L
544546
# undef __cpp_lib_mdspan
545547
# define __cpp_lib_mdspan 202406L

libcxx/modules/std/type_traits.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,9 @@ export namespace std {
314314

315315
// [meta.const.eval], constant evaluation context
316316
using std::is_constant_evaluated;
317+
#if _LIBCPP_STD_VER >= 26 && __has_builtin(__builtin_is_within_lifetime)
318+
using std::is_within_lifetime;
319+
#endif
317320

318321
// [depr.meta.types]
319322
using std::aligned_storage;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03
10+
11+
// <type_traits>
12+
13+
// LWG4138 <https://cplusplus.github.io/LWG/issue4138>
14+
// std::is_within_lifetime shouldn't work when a function type is
15+
// explicitly specified, even if it isn't evaluated
16+
17+
#include <type_traits>
18+
#include <cassert>
19+
20+
#include "test_macros.h"
21+
22+
void fn();
23+
24+
int main(int, char**) {
25+
#ifdef __cpp_lib_is_within_lifetime
26+
constexpr bool _ = true ? false : std::is_within_lifetime<void()>(&fn);
27+
// expected-error@*:* {{static assertion failed due to requirement '!is_function_v<void ()>': std::is_within_lifetime<T> cannot explicitly specify T as a function type}}
28+
#else
29+
// expected-no-diagnostics
30+
#endif
31+
return 0;
32+
}

libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -870,16 +870,16 @@
870870
# endif
871871
# endif
872872

873-
# if !defined(_LIBCPP_VERSION)
873+
# if __has_builtin(__builtin_is_within_lifetime)
874874
# ifndef __cpp_lib_is_within_lifetime
875875
# error "__cpp_lib_is_within_lifetime should be defined in c++26"
876876
# endif
877877
# if __cpp_lib_is_within_lifetime != 202306L
878878
# error "__cpp_lib_is_within_lifetime should have the value 202306L in c++26"
879879
# endif
880-
# else // _LIBCPP_VERSION
880+
# else
881881
# ifdef __cpp_lib_is_within_lifetime
882-
# error "__cpp_lib_is_within_lifetime should not be defined because it is unimplemented in libc++!"
882+
# error "__cpp_lib_is_within_lifetime should not be defined when the requirement '__has_builtin(__builtin_is_within_lifetime)' is not met!"
883883
# endif
884884
# endif
885885

libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7186,16 +7186,16 @@
71867186
# endif
71877187
# endif
71887188

7189-
# if !defined(_LIBCPP_VERSION)
7189+
# if __has_builtin(__builtin_is_within_lifetime)
71907190
# ifndef __cpp_lib_is_within_lifetime
71917191
# error "__cpp_lib_is_within_lifetime should be defined in c++26"
71927192
# endif
71937193
# if __cpp_lib_is_within_lifetime != 202306L
71947194
# error "__cpp_lib_is_within_lifetime should have the value 202306L in c++26"
71957195
# endif
7196-
# else // _LIBCPP_VERSION
7196+
# else
71977197
# ifdef __cpp_lib_is_within_lifetime
7198-
# error "__cpp_lib_is_within_lifetime should not be defined because it is unimplemented in libc++!"
7198+
# error "__cpp_lib_is_within_lifetime should not be defined when the requirement '__has_builtin(__builtin_is_within_lifetime)' is not met!"
71997199
# endif
72007200
# endif
72017201

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03
10+
11+
// <type_traits>
12+
13+
// template <class T>
14+
// consteval bool is_within_lifetime(const T*) noexcept; // C++26
15+
16+
#include <type_traits>
17+
#include <cassert>
18+
19+
#include "test_macros.h"
20+
21+
#ifndef __cpp_lib_is_constant_evaluated
22+
23+
// Check that it doesn't exist if the feature test macro isn't defined (via ADL)
24+
template <class T>
25+
constexpr decltype(static_cast<void>(is_within_lifetime(std::declval<T>())), bool{}) is_within_lifetime_exists(int) {
26+
return true;
27+
}
28+
template <class T>
29+
constexpr bool is_within_lifetime_exists(long) {
30+
return false;
31+
}
32+
33+
static_assert(!is_within_lifetime_exists<const std::integral_constant<bool, false>*>(0), "");
34+
35+
#elif TEST_STD_VER < 26
36+
# error __cpp_lib_is_constant_evaluated defined before C++26
37+
#else // defined(__cpp_lib_is_constant_evaluated) && TEST_STD_VER >= 26
38+
39+
ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<int*>())), bool);
40+
ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<const int*>())), bool);
41+
ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<void*>())), bool);
42+
ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<const void*>())), bool);
43+
44+
ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<int*>()));
45+
ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<const int*>()));
46+
ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<void*>()));
47+
ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<const void*>()));
48+
49+
template <class T>
50+
concept is_within_lifetime_exists = requires(T t) { std::is_within_lifetime(t); };
51+
52+
struct S {};
53+
54+
static_assert(is_within_lifetime_exists<int*>);
55+
static_assert(is_within_lifetime_exists<const int*>);
56+
static_assert(is_within_lifetime_exists<void*>);
57+
static_assert(is_within_lifetime_exists<const void*>);
58+
static_assert(!is_within_lifetime_exists<int>); // Not a pointer
59+
static_assert(!is_within_lifetime_exists<decltype(nullptr)>); // Not a pointer
60+
static_assert(!is_within_lifetime_exists<void() const>); // Not a pointer
61+
static_assert(!is_within_lifetime_exists<int S::*>); // Doesn't accept pointer-to-member
62+
static_assert(!is_within_lifetime_exists<void (S::*)()>);
63+
static_assert(!is_within_lifetime_exists<void (*)()>); // Doesn't match `const T*`
64+
65+
constexpr int i = 0;
66+
static_assert(std::is_within_lifetime(&i));
67+
static_assert(std::is_within_lifetime(const_cast<int*>(&i)));
68+
static_assert(std::is_within_lifetime(static_cast<const void*>(&i)));
69+
static_assert(std::is_within_lifetime(static_cast<void*>(const_cast<int*>(&i))));
70+
static_assert(std::is_within_lifetime<const int>(&i));
71+
static_assert(std::is_within_lifetime<int>(const_cast<int*>(&i)));
72+
static_assert(std::is_within_lifetime<const void>(static_cast<const void*>(&i)));
73+
static_assert(std::is_within_lifetime<void>(static_cast<void*>(const_cast<int*>(&i))));
74+
75+
constexpr union {
76+
int active;
77+
int inactive;
78+
} u{.active = 1};
79+
static_assert(std::is_within_lifetime(&u.active) && !std::is_within_lifetime(&u.inactive));
80+
81+
consteval bool f() {
82+
union {
83+
int active;
84+
int inactive;
85+
};
86+
if (std::is_within_lifetime(&active) || std::is_within_lifetime(&inactive))
87+
return false;
88+
active = 1;
89+
if (!std::is_within_lifetime(&active) || std::is_within_lifetime(&inactive))
90+
return false;
91+
inactive = 1;
92+
if (std::is_within_lifetime(&active) || !std::is_within_lifetime(&inactive))
93+
return false;
94+
int j;
95+
S s;
96+
return std::is_within_lifetime(&j) && std::is_within_lifetime(&s);
97+
}
98+
static_assert(f());
99+
100+
# ifndef TEST_COMPILER_MSVC
101+
// MSVC doesn't support consteval propagation :(
102+
template <typename T>
103+
constexpr void does_escalate(T p) {
104+
std::is_within_lifetime(p);
105+
}
106+
107+
template <typename T, void(T) = does_escalate<T>>
108+
constexpr bool check_escalated(int) {
109+
return false;
110+
}
111+
template <typename T>
112+
constexpr bool check_escalated(long) {
113+
return true;
114+
}
115+
static_assert(check_escalated<int*>(0), "");
116+
static_assert(check_escalated<void*>(0), "");
117+
# endif // defined(TEST_COMPILER_MSVC)
118+
119+
#endif // defined(__cpp_lib_is_constant_evaluated) && TEST_STD_VER >= 26

libcxx/utils/generate_feature_test_macro_components.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,8 @@ def add_version_header(tc):
796796
"c++26": 202306 # P2641R4 Checking if a union alternative is active
797797
},
798798
"headers": ["type_traits"],
799-
"unimplemented": True,
799+
"test_suite_guard": "__has_builtin(__builtin_is_within_lifetime)",
800+
"libcxx_guard": "__has_builtin(__builtin_is_within_lifetime)",
800801
},
801802
{
802803
"name": "__cpp_lib_jthread",

0 commit comments

Comments
 (0)