Skip to content

Commit 7de4410

Browse files
MitalAshokphilnik777
authored andcommitted
[libc++] P2641R4: Checking if a union alternative is active (std::is_within_lifetime)
Address comments Address comments
1 parent 63b83ea commit 7de4410

File tree

11 files changed

+220
-7
lines changed

11 files changed

+220
-7
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ Status
474474
---------------------------------------------------------- -----------------
475475
``__cpp_lib_is_virtual_base_of`` ``202406L``
476476
---------------------------------------------------------- -----------------
477-
``__cpp_lib_is_within_lifetime`` *unimplemented*
477+
``__cpp_lib_is_within_lifetime`` ``202306L``
478478
---------------------------------------------------------- -----------------
479479
``__cpp_lib_linalg`` *unimplemented*
480480
---------------------------------------------------------- -----------------

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,7 @@ set(files
877877
__type_traits/is_valid_expansion.h
878878
__type_traits/is_void.h
879879
__type_traits/is_volatile.h
880+
__type_traits/is_within_lifetime.h
880881
__type_traits/lazy.h
881882
__type_traits/make_32_64_or_128_bit.h
882883
__type_traits/make_const_lvalue_ref.h
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
14+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
15+
# pragma GCC system_header
16+
#endif
17+
18+
_LIBCPP_BEGIN_NAMESPACE_STD
19+
20+
#if _LIBCPP_STD_VER >= 26 && __has_builtin(__builtin_is_within_lifetime)
21+
template <class _Tp>
22+
_LIBCPP_HIDE_FROM_ABI consteval bool is_within_lifetime(const _Tp* __p) noexcept {
23+
return __builtin_is_within_lifetime(__p);
24+
}
25+
#endif
26+
27+
_LIBCPP_END_NAMESPACE_STD
28+
29+
#endif // _LIBCPP___TYPE_TRAITS_IS_WITHIN_LIFETIME_H

libcxx/include/type_traits

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,10 @@ namespace std
454454
template<class B> inline constexpr bool negation_v
455455
= negation<B>::value; // since C++17
456456
457+
// [meta.const.eval], constant evaluation context
458+
constexpr bool is_constant_evaluated() noexcept; // C++20
459+
template<class T>
460+
consteval bool is_within_lifetime(const T*) noexcept; // C++26
457461
}
458462
459463
*/

libcxx/include/version

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,9 @@ __cpp_lib_void_t 201411L <type_traits>
582582
# if __has_builtin(__builtin_is_virtual_base_of)
583583
# define __cpp_lib_is_virtual_base_of 202406L
584584
# endif
585-
// # define __cpp_lib_is_within_lifetime 202306L
585+
# if __has_builtin(__builtin_is_within_lifetime)
586+
# define __cpp_lib_is_within_lifetime 202306L
587+
# endif
586588
// # define __cpp_lib_linalg 202311L
587589
# undef __cpp_lib_mdspan
588590
# 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
@@ -330,6 +330,9 @@ export namespace std {
330330

331331
// [meta.const.eval], constant evaluation context
332332
using std::is_constant_evaluated;
333+
#if _LIBCPP_STD_VER >= 26 && __has_builtin(__builtin_is_within_lifetime)
334+
using std::is_within_lifetime;
335+
#endif
333336

334337
// [depr.meta.types]
335338
using std::aligned_storage;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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, c++11, c++14, c++17, c++20, c++23
10+
// UNSUPPORTED: clang-18, clang-19, gcc-14, apple-clang-16
11+
12+
// <type_traits>
13+
14+
// LWG4138 <https://cplusplus.github.io/LWG/issue4138>
15+
// std::is_within_lifetime shouldn't work when a function type is
16+
// explicitly specified, even if it isn't evaluated
17+
18+
#include <type_traits>
19+
20+
template <class T>
21+
consteval bool checked_is_within_lifetime(T* p) {
22+
return p ? std::is_within_lifetime<T>(p) : false;
23+
}
24+
static_assert(!checked_is_within_lifetime<int>(nullptr));
25+
static_assert(!checked_is_within_lifetime<void()>(nullptr));
26+
// expected-error@*:* {{function pointer argument to '__builtin_is_within_lifetime' is not allowed}}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@
918918
# endif
919919
# endif
920920

921-
# if !defined(_LIBCPP_VERSION)
921+
# if __has_builtin(__builtin_is_within_lifetime)
922922
# ifndef __cpp_lib_is_within_lifetime
923923
# error "__cpp_lib_is_within_lifetime should be defined in c++26"
924924
# endif
@@ -927,7 +927,7 @@
927927
# endif
928928
# else
929929
# ifdef __cpp_lib_is_within_lifetime
930-
# error "__cpp_lib_is_within_lifetime should not be defined because it is unimplemented in libc++!"
930+
# error "__cpp_lib_is_within_lifetime should not be defined when the requirement '__has_builtin(__builtin_is_within_lifetime)' is not met!"
931931
# endif
932932
# endif
933933

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7256,7 +7256,7 @@
72567256
# endif
72577257
# endif
72587258

7259-
# if !defined(_LIBCPP_VERSION)
7259+
# if __has_builtin(__builtin_is_within_lifetime)
72607260
# ifndef __cpp_lib_is_within_lifetime
72617261
# error "__cpp_lib_is_within_lifetime should be defined in c++26"
72627262
# endif
@@ -7265,7 +7265,7 @@
72657265
# endif
72667266
# else
72677267
# ifdef __cpp_lib_is_within_lifetime
7268-
# error "__cpp_lib_is_within_lifetime should not be defined because it is unimplemented in libc++!"
7268+
# error "__cpp_lib_is_within_lifetime should not be defined when the requirement '__has_builtin(__builtin_is_within_lifetime)' is not met!"
72697269
# endif
72707270
# endif
72717271

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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, c++11, c++14, c++17, c++20, c++23
10+
// UNSUPPORTED: clang-18, clang-19, gcc-14, apple-clang-16
11+
12+
// <type_traits>
13+
14+
// template <class T>
15+
// consteval bool is_within_lifetime(const T*) noexcept; // C++26
16+
17+
#include <type_traits>
18+
#include <cassert>
19+
20+
#include "test_macros.h"
21+
22+
ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<int*>())), bool);
23+
ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<const int*>())), bool);
24+
ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<void*>())), bool);
25+
ASSERT_SAME_TYPE(decltype(std::is_within_lifetime(std::declval<const void*>())), bool);
26+
27+
ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<int*>()));
28+
ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<const int*>()));
29+
ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<void*>()));
30+
ASSERT_NOEXCEPT(std::is_within_lifetime(std::declval<const void*>()));
31+
32+
template <class T>
33+
concept is_within_lifetime_exists = requires(T t) { std::is_within_lifetime(t); };
34+
35+
struct S {};
36+
37+
static_assert(is_within_lifetime_exists<int*>);
38+
static_assert(is_within_lifetime_exists<const int*>);
39+
static_assert(is_within_lifetime_exists<void*>);
40+
static_assert(is_within_lifetime_exists<const void*>);
41+
static_assert(!is_within_lifetime_exists<int>); // Not a pointer
42+
static_assert(!is_within_lifetime_exists<decltype(nullptr)>); // Not a pointer
43+
static_assert(!is_within_lifetime_exists<void() const>); // Not a pointer
44+
static_assert(!is_within_lifetime_exists<int S::*>); // Doesn't accept pointer-to-data-member
45+
static_assert(!is_within_lifetime_exists<void (S::*)()>); // Doesn't accept pointer-to-member-function
46+
static_assert(!is_within_lifetime_exists<void (*)()>); // Doesn't match `const T*`
47+
48+
consteval bool f() {
49+
// Test that it works with global variables whose lifetime is in a
50+
// different constant expression
51+
{
52+
static constexpr int i = 0;
53+
static_assert(std::is_within_lifetime(&i));
54+
// (Even when cast to a different type)
55+
static_assert(std::is_within_lifetime(const_cast<int*>(&i)));
56+
static_assert(std::is_within_lifetime(static_cast<const void*>(&i)));
57+
static_assert(std::is_within_lifetime(static_cast<void*>(const_cast<int*>(&i))));
58+
static_assert(std::is_within_lifetime<const int>(&i));
59+
static_assert(std::is_within_lifetime<int>(const_cast<int*>(&i)));
60+
static_assert(std::is_within_lifetime<const void>(static_cast<const void*>(&i)));
61+
static_assert(std::is_within_lifetime<void>(static_cast<void*>(const_cast<int*>(&i))));
62+
}
63+
64+
{
65+
static constexpr union {
66+
int member1;
67+
int member2;
68+
} u{.member2 = 1};
69+
static_assert(!std::is_within_lifetime(&u.member1) && std::is_within_lifetime(&u.member2));
70+
}
71+
72+
// Test that it works for varibles inside the same constant expression
73+
{
74+
int i = 0;
75+
assert(std::is_within_lifetime(&i));
76+
// (Even when cast to a different type)
77+
assert(std::is_within_lifetime(const_cast<int*>(&i)));
78+
assert(std::is_within_lifetime(static_cast<const void*>(&i)));
79+
assert(std::is_within_lifetime(static_cast<void*>(const_cast<int*>(&i))));
80+
assert(std::is_within_lifetime<const int>(&i));
81+
assert(std::is_within_lifetime<int>(const_cast<int*>(&i)));
82+
assert(std::is_within_lifetime<const void>(static_cast<const void*>(&i)));
83+
assert(std::is_within_lifetime<void>(static_cast<void*>(const_cast<int*>(&i))));
84+
}
85+
// Anonymous union
86+
{
87+
union {
88+
int member1;
89+
int member2;
90+
};
91+
assert(!std::is_within_lifetime(&member1) && !std::is_within_lifetime(&member2));
92+
member1 = 1;
93+
assert(std::is_within_lifetime(&member1) && !std::is_within_lifetime(&member2));
94+
member2 = 1;
95+
assert(!std::is_within_lifetime(&member1) && std::is_within_lifetime(&member2));
96+
}
97+
// Variant members
98+
{
99+
struct X {
100+
union {
101+
int member1;
102+
int member2;
103+
};
104+
} x;
105+
assert(!std::is_within_lifetime(&x.member1) && !std::is_within_lifetime(&x.member2));
106+
x.member1 = 1;
107+
assert(std::is_within_lifetime(&x.member1) && !std::is_within_lifetime(&x.member2));
108+
x.member2 = 1;
109+
assert(!std::is_within_lifetime(&x.member1) && std::is_within_lifetime(&x.member2));
110+
}
111+
// Unions
112+
{
113+
union X {
114+
int member1;
115+
int member2;
116+
} x;
117+
assert(!std::is_within_lifetime(&x.member1) && !std::is_within_lifetime(&x.member2));
118+
x.member1 = 1;
119+
assert(std::is_within_lifetime(&x.member1) && !std::is_within_lifetime(&x.member2));
120+
x.member2 = 1;
121+
assert(!std::is_within_lifetime(&x.member1) && std::is_within_lifetime(&x.member2));
122+
}
123+
{
124+
S s; // uninitialised
125+
assert(std::is_within_lifetime(&s));
126+
}
127+
128+
return true;
129+
}
130+
static_assert(f());
131+
132+
// Check that it is a consteval (and consteval-propagating) function
133+
// (i.e., taking the address of below will fail because it will be an immediate function)
134+
template <typename T>
135+
constexpr void does_escalate(T p) {
136+
std::is_within_lifetime(p);
137+
}
138+
template <typename T, void (*)(T) = &does_escalate<T>>
139+
constexpr bool check_escalated(int) {
140+
return false;
141+
}
142+
template <typename T>
143+
constexpr bool check_escalated(long) {
144+
return true;
145+
}
146+
static_assert(check_escalated<int*>(0), "");
147+
static_assert(check_escalated<void*>(0), "");

0 commit comments

Comments
 (0)