Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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*
---------------------------------------------------------- -----------------
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 29 additions & 0 deletions libcxx/include/__type_traits/is_within_lifetime.h
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions libcxx/include/module.modulemap.in
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
8 changes: 8 additions & 0 deletions libcxx/include/type_traits
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

*/
Expand Down Expand Up @@ -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 <version>

# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
Expand Down
4 changes: 3 additions & 1 deletion libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions libcxx/modules/std/type_traits.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 26 additions & 0 deletions libcxx/test/libcxx/utilities/meta/is_within_lifetime.verify.cpp
Original file line number Diff line number Diff line change
@@ -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

// <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}}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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

// <type_traits>

// template <class T>
// consteval bool is_within_lifetime(const T*) noexcept; // C++26

#include <cassert>
#include <type_traits>
#include <utility>

#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));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to verify destruction and reconstruction (via placement-new or construct_at) here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's necessary. The different ways to stop and start lifetimes seems like something the compiler should make sure work. This is more of a compiler than a library feature after all.

{
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), "");
3 changes: 2 additions & 1 deletion libcxx/utils/generate_feature_test_macro_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading