// RUN: %clang_cc1 -std=c++20 -Wno-unused %s -verify=expected,cxx20 -Wno-vla-cxx-extension // RUN: %clang_cc1 -std=c++23 -Wno-unused %s -verify=expected,sincecxx23 -Wno-vla-cxx-extension // RUN: %clang_cc1 -std=c++26 -Wno-unused %s -verify=expected,sincecxx23 -Wno-vla-cxx-extension // RUN: %clang_cc1 -std=c++26 -DINLINE_NAMESPACE -Wno-unused %s -verify=expected,sincecxx23 -Wno-vla-cxx-extension inline constexpr void* operator new(__SIZE_TYPE__, void* p) noexcept { return p; } namespace std { template constexpr T* construct_at(T* p, Args&&... args) { return ::new((void*)p) T(static_cast(args)...); } template constexpr void destroy_at(T* p) { p->~T(); } template struct allocator { constexpr T* allocate(__SIZE_TYPE__ n) { return static_cast(::operator new(n * sizeof(T))); } constexpr void deallocate(T* p, __SIZE_TYPE__) { ::operator delete(p); } }; using nullptr_t = decltype(nullptr); template struct integral_constant { static constexpr T value = v; }; template using bool_constant = integral_constant; using true_type = bool_constant; using false_type = bool_constant; template inline constexpr bool is_function_v = __is_function(T); #ifdef INLINE_NAMESPACE inline namespace __1 { #endif template requires (!is_function_v) // #std-constraint consteval bool is_within_lifetime(const T* p) noexcept { // #std-definition return __builtin_is_within_lifetime(p); } #ifdef INLINE_NAMESPACE } #endif } consteval bool test_union(int& i, char& c) { if (__builtin_is_within_lifetime(&i) || __builtin_is_within_lifetime(&c)) return false; std::construct_at(&c, 1); if (__builtin_is_within_lifetime(&i) || !__builtin_is_within_lifetime(&c)) return false; std::construct_at(&i, 3); if (!__builtin_is_within_lifetime(&i) || __builtin_is_within_lifetime(&c)) return false; return true; } static_assert([]{ union { int i; char c; } u; return test_union(u.i, u.c); }()); static_assert([]{ union { int i; char c; }; return test_union(i, c); }()); static_assert([]{ struct { union { int i; char c; }; } u; return test_union(u.i, u.c); }()); static_assert([]{ struct { union { int i; char c; } u; } r; return test_union(r.u.i, r.u.c); }()); consteval bool test_nested() { union { union { int i; char c; } u; long l; }; if (__builtin_is_within_lifetime(&l) || __builtin_is_within_lifetime(&u) || __builtin_is_within_lifetime(&u.i) || __builtin_is_within_lifetime(&u.c)) return false; std::construct_at(&l); if (!__builtin_is_within_lifetime(&l) || __builtin_is_within_lifetime(&u) || __builtin_is_within_lifetime(&u.i) || __builtin_is_within_lifetime(&u.c)) return false; std::construct_at(&u); std::construct_at(&u.i); if (__builtin_is_within_lifetime(&l) || !__builtin_is_within_lifetime(&u) || !__builtin_is_within_lifetime(&u.i) || __builtin_is_within_lifetime(&u.c)) return false; std::construct_at(&u.c); if (__builtin_is_within_lifetime(&l) || !__builtin_is_within_lifetime(&u) || __builtin_is_within_lifetime(&u.i) || !__builtin_is_within_lifetime(&u.c)) return false; return true; } static_assert(test_nested()); consteval bool test_dynamic(bool read_after_deallocate) { std::allocator a; int* p = a.allocate(1); // a.allocate starts the lifetime of an array, // the complete object of *p has started its lifetime if (__builtin_is_within_lifetime(p)) return false; std::construct_at(p); if (!__builtin_is_within_lifetime(p)) return false; std::destroy_at(p); if (__builtin_is_within_lifetime(p)) return false; a.deallocate(p, 1); if (read_after_deallocate) __builtin_is_within_lifetime(p); // expected-note {{read of heap allocated object that has been deleted}} return true; } static_assert(test_dynamic(false)); static_assert(test_dynamic(true)); // expected-error@-1 {{static assertion expression is not an integral constant expression}} // expected-note@-2 {{in call to 'test_dynamic(true)'}} consteval bool test_automatic(int read_dangling) { int* p; { int x = 0; p = &x; if (!__builtin_is_within_lifetime(p)) return false; } { int x = 0; if (read_dangling == 1) __builtin_is_within_lifetime(p); // expected-note {{read of object outside its lifetime is not allowed in a constant expression}} } if (read_dangling == 2) __builtin_is_within_lifetime(p); // expected-note {{read of object outside its lifetime is not allowed in a constant expression}} { int x[4]; p = &x[2]; if (!__builtin_is_within_lifetime(p)) return false; } if (read_dangling == 3) __builtin_is_within_lifetime(p); // expected-note {{read of object outside its lifetime is not allowed in a constant expression}} std::nullptr_t* q; { std::nullptr_t np = nullptr; q = &np; if (!__builtin_is_within_lifetime(q)) return false; } if (read_dangling == 4) __builtin_is_within_lifetime(q); // expected-note {{read of object outside its lifetime is not allowed in a constant expression}} return true; } static_assert(test_automatic(0)); static_assert(test_automatic(1)); // expected-error@-1 {{static assertion expression is not an integral constant expression}} // expected-note@-2 {{in call to 'test_automatic(1)'}} static_assert(test_automatic(2)); // expected-error@-1 {{static assertion expression is not an integral constant expression}} // expected-note@-2 {{in call to 'test_automatic(2)'}} static_assert(test_automatic(3)); // expected-error@-1 {{static assertion expression is not an integral constant expression}} // expected-note@-2 {{in call to 'test_automatic(3)'}} static_assert(test_automatic(4)); // expected-error@-1 {{static assertion expression is not an integral constant expression}} // expected-note@-2 {{in call to 'test_automatic(4)'}} consteval bool test_indeterminate() { int x; if (!__builtin_is_within_lifetime(&x)) return false; bool b = true; unsigned char c = __builtin_bit_cast(unsigned char, b); if (!__builtin_is_within_lifetime(&c)) return false; struct {} padding; unsigned char y = __builtin_bit_cast(unsigned char, padding); if (!__builtin_is_within_lifetime(&y)) return false; return true; } static_assert(test_indeterminate()); consteval bool test_volatile() { int x; if (!__builtin_is_within_lifetime(static_cast(&x)) || !__builtin_is_within_lifetime(static_cast(&x))) return false; volatile int y; if (!__builtin_is_within_lifetime(const_cast(&y)) || !__builtin_is_within_lifetime(const_cast(static_cast(&y)))) return false; return true; } static_assert(test_volatile()); constexpr bool self = __builtin_is_within_lifetime(&self); // expected-error@-1 {{constexpr variable 'self' must be initialized by a constant expression}} // expected-note@-2 {{'__builtin_is_within_lifetime' cannot be called with a pointer to an object whose lifetime has not yet begun}} // expected-error@-3 {{call to consteval function '__builtin_is_within_lifetime' is not a constant expression}} // expected-note@-4 {{initializer of 'self' is not a constant expression}} // expected-note@-5 {{declared here}} constexpr int external{}; static_assert(__builtin_is_within_lifetime(&external)); void not_constexpr() { __builtin_is_within_lifetime(&external); } void invalid_args() { __builtin_is_within_lifetime(static_cast(nullptr)); // expected-error@-1 {{call to consteval function '__builtin_is_within_lifetime' is not a constant expression}} // expected-note@-2 {{'__builtin_is_within_lifetime' cannot be called with a null pointer}} // FIXME: avoid function to pointer conversion on all consteval builtins __builtin_is_within_lifetime(0); // expected-error@-1 {{non-pointer argument to '__builtin_is_within_lifetime' is not allowed}} // expected-error@-2 {{cannot take address of consteval function '__builtin_is_within_lifetime' outside of an immediate invocation}} __builtin_is_within_lifetime(); // expected-error@-1 {{too few arguments to function call, expected 1, have 0}} // expected-error@-2 {{cannot take address of consteval function '__builtin_is_within_lifetime' outside of an immediate invocation}} __builtin_is_within_lifetime(1, 2); // expected-error@-1 {{too many arguments to function call, expected 1, have 2}} // expected-error@-2 {{cannot take address of consteval function '__builtin_is_within_lifetime' outside of an immediate invocation}} __builtin_is_within_lifetime(&external, &external); // expected-error@-1 {{too many arguments to function call, expected 1, have 2}} // expected-error@-2 {{cannot take address of consteval function '__builtin_is_within_lifetime' outside of an immediate invocation}} } constexpr struct { union { int i; char c; }; mutable int mi; // #x-mi } x1{ .c = 2 }; static_assert(!__builtin_is_within_lifetime(&x1.i)); static_assert(__builtin_is_within_lifetime(&x1.c)); static_assert(__builtin_is_within_lifetime(&x1.mi)); // expected-error@-1 {{static assertion expression is not an integral constant expression}} // expected-note@-2 {{read of mutable member 'mi' is not allowed in a constant expression}} // expected-note@#x-mi {{declared here}} constexpr struct NSDMI { // #NSDMI bool a = true; bool b = __builtin_is_within_lifetime(&a); // #NSDMI-read } x2; // expected-error@-1 {{constexpr variable 'x2' must be initialized by a constant expression}} // expected-note@#NSDMI-read {{'__builtin_is_within_lifetime' cannot be called with a pointer to an object whose lifetime has not yet begun}} // expected-note@-3 {{in call to 'NSDMI()'}} // expected-error@-4 {{call to immediate function 'NSDMI::NSDMI' is not a constant expression}} // expected-note@#NSDMI {{'NSDMI' is an immediate constructor because the default initializer of 'b' contains a call to a consteval function '__builtin_is_within_lifetime' and that call is not a constant expression}} // expected-note@#NSDMI-read {{'__builtin_is_within_lifetime' cannot be called with a pointer to an object whose lifetime has not yet begun}} // expected-note@-7 {{in call to 'NSDMI()'}} struct X3 { consteval X3() { __builtin_is_within_lifetime(this); // #X3-read } } x3; // expected-error@-1 {{call to consteval function 'X3::X3' is not a constant expression}} // expected-note@#X3-read {{'__builtin_is_within_lifetime' cannot be called with a pointer to an object whose lifetime has not yet begun}} // expected-note@-3 {{in call to 'X3()'}} constexpr int i = 2; static_assert(__builtin_is_within_lifetime(const_cast(&i))); static_assert(__builtin_is_within_lifetime(const_cast(&i))); static_assert(__builtin_is_within_lifetime(static_cast(&i))); constexpr int arr[2]{}; static_assert(__builtin_is_within_lifetime(arr)); static_assert(__builtin_is_within_lifetime(arr + 0)); static_assert(__builtin_is_within_lifetime(arr + 1)); void f() { __builtin_is_within_lifetime(&i + 1); // expected-error@-1 {{call to consteval function '__builtin_is_within_lifetime' is not a constant expression}} // expected-note@-2 {{'__builtin_is_within_lifetime' cannot be called with a one-past-the-end pointer}} __builtin_is_within_lifetime(arr + 2); // expected-error@-1 {{call to consteval function '__builtin_is_within_lifetime' is not a constant expression}} // expected-note@-2 {{'__builtin_is_within_lifetime' cannot be called with a one-past-the-end pointer}} } template consteval void disallow_function_types(bool b, const T* p) { if (b) { __builtin_is_within_lifetime(p); // expected-error {{function pointer argument to '__builtin_is_within_lifetime' is not allowed}} } } void g() { disallow_function_types(false, &f); // expected-note@-1 {{in instantiation of function template specialization 'disallow_function_types' requested here}} } struct OptBool { union { bool b; char c; }; // note: this assumes common implementation properties for bool and char: // * sizeof(bool) == sizeof(char), and // * the value representations for true and false are distinct // from the value representation for 2 constexpr OptBool() : c(2) { } constexpr OptBool(bool b) : b(b) { } constexpr auto has_value() const -> bool { if consteval { // cxx20-warning {{consteval if}} return __builtin_is_within_lifetime(&b); // during constant evaluation, cannot read from c } else { return c != 2; // during runtime, must read from c } } constexpr auto operator*() const -> const bool& { return b; } }; constexpr OptBool disengaged; constexpr OptBool engaged(true); static_assert(!disengaged.has_value()); static_assert(engaged.has_value()); static_assert(*engaged); namespace vlas { consteval bool f(int n) { int vla[n]; // cxx20-error {{variable of non-literal type}} return __builtin_is_within_lifetime(static_cast(&vla)); } static_assert(f(1)); consteval bool fail(int n) { int vla[n]; // cxx20-error {{variable of non-literal type}} return __builtin_is_within_lifetime(&vla); // expected-error {{variable length arrays are not supported in '__builtin_is_within_lifetime'}} } static_assert(fail(1)); // sincecxx23-error {{static assertion expression is not an integral constant expression}} consteval bool variably_modified(int n) { int(* p)[n]; return __builtin_is_within_lifetime(&p); } static_assert(variably_modified(1)); } // namespace vlas consteval bool partial_arrays() { int arr[2]; if (!__builtin_is_within_lifetime(&arr) || !__builtin_is_within_lifetime(&arr[0]) || !__builtin_is_within_lifetime(&arr[1])) return false; std::destroy_at(&arr[0]); if (!__builtin_is_within_lifetime(&arr) || __builtin_is_within_lifetime(&arr[0]) || !__builtin_is_within_lifetime(&arr[1])) return false; std::construct_at(&arr[0]); if (!__builtin_is_within_lifetime(&arr) || !__builtin_is_within_lifetime(&arr[0]) || !__builtin_is_within_lifetime(&arr[1])) return false; return true; } static_assert(partial_arrays()); consteval bool partial_members() { struct S { int x; int y; } s; if (!__builtin_is_within_lifetime(&s) || !__builtin_is_within_lifetime(&s.x) || !__builtin_is_within_lifetime(&s.y)) return false; std::destroy_at(&s.x); if (!__builtin_is_within_lifetime(&s) || __builtin_is_within_lifetime(&s.x) || !__builtin_is_within_lifetime(&s.y)) return false; std::construct_at(&s.x); if (!__builtin_is_within_lifetime(&s) || !__builtin_is_within_lifetime(&s.x) || !__builtin_is_within_lifetime(&s.y)) return false; return true; } struct NonTrivial { constexpr NonTrivial() {} constexpr NonTrivial(const NonTrivial&) {} constexpr ~NonTrivial() {} }; template constexpr T& unmove(T&& temp) { return static_cast(temp); } consteval bool test_temporaries() { static_assert(__builtin_is_within_lifetime(&unmove(0))); static_assert(__builtin_is_within_lifetime(&unmove(NonTrivial{}))); if (!__builtin_is_within_lifetime(&unmove(0))) return false; if (!__builtin_is_within_lifetime(&unmove(NonTrivial{}))) return false; return true; } static_assert(test_temporaries()); constexpr const int& temp = 0; static_assert(__builtin_is_within_lifetime(&temp)); template constexpr T* test_dangling() { T i; // expected-note 2 {{declared here}} return &i; // expected-warning 2 {{address of stack memory associated with local variable 'i' returned}} } static_assert(__builtin_is_within_lifetime(test_dangling())); // expected-note {{in instantiation of function template specialization}} // expected-error@-1 {{static assertion expression is not an integral constant expression}} // expected-note@-2 {{read of variable whose lifetime has ended}} static_assert(__builtin_is_within_lifetime(test_dangling())); // expected-note {{in instantiation of function template specialization}} // expected-error@-1 {{static assertion expression is not an integral constant expression}} // expected-note@-2 {{read of variable whose lifetime has ended}} template concept CanCallAndPassToIsWithinLifetime = std::bool_constant<__builtin_is_within_lifetime(F())>::value; static_assert(CanCallAndPassToIsWithinLifetime<[]{ return &i; }>); static_assert(!CanCallAndPassToIsWithinLifetime<[]{ return static_cast(nullptr); }>); static_assert(!CanCallAndPassToIsWithinLifetime<[]{ return static_cast(&f); }>); template constexpr std::true_type sfinae() requires CanCallAndPassToIsWithinLifetime { return {}; } template std::false_type sfinae() { return {}; } static_assert(decltype(sfinae<[]{ return &i; }>())::value); static_assert(!decltype(sfinae<[]{ return static_cast(nullptr); }>())::value); std::true_type(* not_immediate)() = &sfinae<[]{ return &i; }>; void test_std_error_message() { std::is_within_lifetime(static_cast(nullptr)); // expected-error@-1 {{call to consteval function 'std::is_within_lifetime' is not a constant expression}} // expected-note@-2 {{'std::is_within_lifetime' cannot be called with a null pointer}} // expected-note@-3 {{in call to 'is_within_lifetime(nullptr)'}} std::is_within_lifetime(&test_std_error_message); // expected-error@-1 {{no matching function for call to 'is_within_lifetime'}} // expected-note@#std-definition {{candidate template ignored: constraints not satisfied [with T = void ()]}} // expected-note@#std-constraint {{because '!is_function_v' evaluated to false}} std::is_within_lifetime(arr + 2); // expected-error@-1 {{call to consteval function 'std::is_within_lifetime' is not a constant expression}} // expected-note@-2 {{'std::is_within_lifetime' cannot be called with a one-past-the-end pointer}} // expected-note@-3 {{in call to 'is_within_lifetime(&arr[2])'}} } struct XStd { consteval XStd() { std::is_within_lifetime(this); // #XStd-read } } xstd; // expected-error@-1 {{call to consteval function 'XStd::XStd' is not a constant expression}} // expected-note@#XStd-read {{'std::is_within_lifetime' cannot be called with a pointer to an object whose lifetime has not yet begun}} // expected-note@#XStd-read {{in call to 'is_within_lifetime(&)'}} // expected-note@-4 {{in call to 'XStd()'}}