libstdc++, testsuite: Implement C++26 P3450R1: Extend std::is_within_lifetime paper Now that r17-4186 fixed constexpr evaluation of static_cast downcasts to diagnose UB, this patch implements the C++26 P3450R1: Extend std::is_within_lifetime paper purely in library by using __builtin_constant_p to verify if the static_cast mandated by the standard is a constant expression. 2026-09-14 Jakub Jelinek <jakub@redhat.com> libstdc++-v3/ * include/bits/version.def (within_lifetime): Change value from 202306 to 202603. * include/bits/version.h: Regenerate. * include/std/type_traits: Implement C++26 P3450R1: Extend std::is_within_lifetime paper. (std::is_within_lifetime): Add _Up template argument defaulted to void and verify static_cast<const volatile _Up*>(__p) is a constant expression. gcc/testsuite/ * g++.dg/cpp26/within-lifetime1.C: Fix up C++26 paper number and name. * g++.dg/cpp26/within-lifetime2.C: Likewise. * g++.dg/cpp26/within-lifetime3.C: Likewise. * g++.dg/cpp26/within-lifetime4.C: Likewise. * g++.dg/cpp26/within-lifetime5.C: Likewise. * g++.dg/cpp26/within-lifetime6.C: Likewise. * g++.dg/cpp26/within-lifetime7.C: Likewise. * g++.dg/cpp26/within-lifetime8.C: Likewise. * g++.dg/cpp26/within-lifetime9.C: Likewise. * g++.dg/cpp26/within-lifetime10.C: New test. * g++.dg/cpp26/within-lifetime11.C: New test. * g++.dg/cpp26/within-lifetime12.C: New test. Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C index e5ce7bd..54e2152 100644 --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C
@@ -1,4 +1,4 @@ -// P3450R1 - Extend std::is_within_lifetime +// P2641R4 - Checking if a union alternative is active // { dg-do compile { target c++20 } } #if __has_builtin(__builtin_is_within_lifetime)
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime10.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime10.C new file mode 100644 index 0000000..45cbbaa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime10.C
@@ -0,0 +1,184 @@ +// P3450R1 - Extend std::is_within_lifetime +// { dg-do compile { target c++20 } } + +#ifndef USE_HEADER +#if __has_builtin(__builtin_is_within_lifetime) +namespace std { + template <typename U = void, typename T> + consteval bool + is_within_lifetime (const T *p) noexcept + { + return (__builtin_is_within_lifetime (p) + && __builtin_constant_p (static_cast <const volatile U *> (p) + && true)); + } +} +#endif +#endif + +consteval int +foo () +{ + char a = 0; + struct B {} b; + struct C : B {} c; + struct D : C {} d; + struct E : B {} e; + if (!std::is_within_lifetime (&a)) + return __LINE__; + if (!std::is_within_lifetime <char> (&a)) + return __LINE__; + if (!std::is_within_lifetime <char const> (&a)) + return __LINE__; + if (!std::is_within_lifetime <char volatile const> (&a)) + return __LINE__; + if (!std::is_within_lifetime (&b)) + return __LINE__; + if (!std::is_within_lifetime <B> (&b)) + return __LINE__; + if (std::is_within_lifetime <C> (&b)) + return __LINE__; + if (!std::is_within_lifetime (&c)) + return __LINE__; + if (!std::is_within_lifetime <B> (&c)) + return __LINE__; + if (!std::is_within_lifetime <C> (&c)) + return __LINE__; + B *pb = &b; + B *pc = &c; + B *pd = &d; + B *pe = &e; + if (!std::is_within_lifetime <B> (pb)) + return __LINE__; + if (!std::is_within_lifetime <B> (pc)) + return __LINE__; + if (!std::is_within_lifetime <B> (pd)) + return __LINE__; + if (!std::is_within_lifetime <B> (pe)) + return __LINE__; + if (std::is_within_lifetime <C> (pb)) + return __LINE__; + if (!std::is_within_lifetime <C> (pc)) + return __LINE__; + if (!std::is_within_lifetime <C> (pd)) + return __LINE__; + if (std::is_within_lifetime <C> (pe)) + return __LINE__; + if (std::is_within_lifetime <D> (pb)) + return __LINE__; + if (std::is_within_lifetime <D> (pc)) + return __LINE__; + if (!std::is_within_lifetime <D> (pd)) + return __LINE__; + if (std::is_within_lifetime <D> (pe)) + return __LINE__; + if (std::is_within_lifetime <E> (pb)) + return __LINE__; + if (std::is_within_lifetime <E> (pc)) + return __LINE__; + if (std::is_within_lifetime <E> (pd)) + return __LINE__; + if (!std::is_within_lifetime <E> (pe)) + return __LINE__; + C *pf = &c; + C *pg = &d; + if (!std::is_within_lifetime <B> (pf)) + return __LINE__; + if (!std::is_within_lifetime <B> (pg)) + return __LINE__; + if (!std::is_within_lifetime <C> (pf)) + return __LINE__; + if (!std::is_within_lifetime <C> (pg)) + return __LINE__; + if (std::is_within_lifetime <D> (pf)) + return __LINE__; + if (!std::is_within_lifetime <D> (pg)) + return __LINE__; + return 0; +} + +static_assert (foo () == 0); + +static constexpr char a = 0; +static constexpr struct B { int b; } b {}; +static constexpr struct C : B {} c {}; +static constexpr struct D : C {} d {}; +static constexpr struct E : B {} e {}; +static constexpr const B *pb = &b; +static constexpr const B *pc = &c; +static constexpr const B *pd = &d; +static constexpr const B *pe = &e; +static constexpr const C *pf = &c; +static constexpr const C *pg = &d; + +consteval int +bar () +{ + if (!std::is_within_lifetime (&a)) + return __LINE__; + if (!std::is_within_lifetime <char> (&a)) + return __LINE__; + if (!std::is_within_lifetime <char const> (&a)) + return __LINE__; + if (!std::is_within_lifetime <char volatile const> (&a)) + return __LINE__; + if (!std::is_within_lifetime (&b)) + return __LINE__; + if (!std::is_within_lifetime <B> (&b)) + return __LINE__; + if (std::is_within_lifetime <C> (&b)) + return __LINE__; + if (!std::is_within_lifetime (&c)) + return __LINE__; + if (!std::is_within_lifetime <B> (&c)) + return __LINE__; + if (!std::is_within_lifetime <C> (&c)) + return __LINE__; + if (!std::is_within_lifetime <B> (pb)) + return __LINE__; + if (!std::is_within_lifetime <B> (pc)) + return __LINE__; + if (!std::is_within_lifetime <B> (pd)) + return __LINE__; + if (!std::is_within_lifetime <B> (pe)) + return __LINE__; + if (std::is_within_lifetime <C> (pb)) + return __LINE__; + if (!std::is_within_lifetime <C> (pc)) + return __LINE__; + if (!std::is_within_lifetime <C> (pd)) + return __LINE__; + if (std::is_within_lifetime <C> (pe)) + return __LINE__; + if (std::is_within_lifetime <D> (pb)) + return __LINE__; + if (std::is_within_lifetime <D> (pc)) + return __LINE__; + if (!std::is_within_lifetime <D> (pd)) + return __LINE__; + if (std::is_within_lifetime <D> (pe)) + return __LINE__; + if (std::is_within_lifetime <E> (pb)) + return __LINE__; + if (std::is_within_lifetime <E> (pc)) + return __LINE__; + if (std::is_within_lifetime <E> (pd)) + return __LINE__; + if (!std::is_within_lifetime <E> (pe)) + return __LINE__; + if (!std::is_within_lifetime <B> (pf)) + return __LINE__; + if (!std::is_within_lifetime <B> (pg)) + return __LINE__; + if (!std::is_within_lifetime <C> (pf)) + return __LINE__; + if (!std::is_within_lifetime <C> (pg)) + return __LINE__; + if (std::is_within_lifetime <D> (pf)) + return __LINE__; + if (!std::is_within_lifetime <D> (pg)) + return __LINE__; + return 0; +} + +static_assert (bar () == 0);
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime11.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime11.C new file mode 100644 index 0000000..0624abe --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime11.C
@@ -0,0 +1,6 @@ +// P3450R1 - Extend std::is_within_lifetime +// { dg-do compile { target c++26 } } + +#include <type_traits> +#define USE_HEADER +#include "within-lifetime10.C"
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime12.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime12.C new file mode 100644 index 0000000..5381118 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime12.C
@@ -0,0 +1,46 @@ +// P3450R1 - Extend std::is_within_lifetime +// { dg-do compile { target c++20 } } + +#if __has_builtin(__builtin_is_within_lifetime) +namespace std { + template <typename U = void, typename T> + consteval bool + is_within_lifetime (const T *p) noexcept + { + return (__builtin_is_within_lifetime (p) + && __builtin_constant_p (static_cast <const volatile U *> (p) + && true)); +// { dg-error "invalid 'static_cast' from type 'void \\\(\\\*\\\)\\\(\\\)' to type 'const volatile void\\\*'" "" { target *-*-* } .-2 } +// { dg-error "invalid 'static_cast' from type 'const int\\\*' to type 'const volatile long int\\\*'" "" { target *-*-* } .-3 } +// { dg-error "invalid 'static_cast' from type 'const int\\\*' to type 'int \\\(\\\*\\\)\\\(\\\)'" "" { target *-*-* } .-4 } +// { dg-error "'A' is an inaccessible base of 'C'" "" { target *-*-* } .-5 } +// { dg-error "'A' is an inaccessible base of 'D'" "" { target *-*-* } .-6 } +// { dg-error "'A' is an ambiguous base of 'E'" "" { target *-*-* } .-7 } +// { dg-error "cannot convert from pointer to base class 'A' to pointer to derived class 'F' because the base is virtual" "" { target *-*-* } .-8 } + } +} +#endif + +template <typename U, typename T> +consteval bool +baz (T *p) +{ + return p ? std::is_within_lifetime <U, T> (p) : false; +} + +static_assert (!baz <void, int> (nullptr)); +bool a = baz <void, void ()> (nullptr); // { dg-message "required from here" } +bool b = baz <long, int> (nullptr); // { dg-message "required from here" } +bool c = baz <int (), int> (nullptr); // { dg-message "required from here" } + +struct A {}; +struct B : A {}; +struct C : protected A {}; +struct D : private A {}; +struct E : B, C, D {}; +struct F : virtual A {}; + +bool d = baz <C, A> (nullptr); // { dg-message "required from here" } +bool e = baz <D, A> (nullptr); // { dg-message "required from here" } +bool f = baz <E, A> (nullptr); // { dg-message "required from here" } +bool g = baz <F, A> (nullptr); // { dg-message "required from here" }
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C index dc05448..469e783 100644 --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C
@@ -1,4 +1,4 @@ -// P3450R1 - Extend std::is_within_lifetime +// P2641R4 - Checking if a union alternative is active // { dg-do compile { target c++20 } } #include "../cpp2a/construct_at.h"
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C index 36afd2e..972039c 100644 --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C
@@ -1,4 +1,4 @@ -// P3450R1 - Extend std::is_within_lifetime +// P2641R4 - Checking if a union alternative is active // { dg-do compile { target c++20 } } #include "../cpp2a/construct_at.h"
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C index b420dea..1cb895d 100644 --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C
@@ -1,4 +1,4 @@ -// P3450R1 - Extend std::is_within_lifetime +// P2641R4 - Checking if a union alternative is active // { dg-do compile { target c++20 } } template <typename T>
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C index 1f780cf..1b96fe2 100644 --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C
@@ -1,4 +1,4 @@ -// P3450R1 - Extend std::is_within_lifetime +// P2641R4 - Checking if a union alternative is active // { dg-do compile { target c++23 } } // { dg-skip-if "sizeof (bool) > 1" { powerpc-*-darwin* } }
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C index 6e9ddf9..ef2544e 100644 --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C
@@ -1,4 +1,4 @@ -// P3450R1 - Extend std::is_within_lifetime +// P2641R4 - Checking if a union alternative is active // { dg-do compile { target c++26 } } // { dg-skip-if "sizeof (bool) > 1" { powerpc-*-darwin* } }
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C index d920f5a..48098e8 100644 --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C
@@ -1,4 +1,4 @@ -// P3450R1 - Extend std::is_within_lifetime +// P2641R4 - Checking if a union alternative is active // { dg-do compile { target c++20 } } // { dg-options "" }
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime8.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime8.C index 98bfde2..5a28f92 100644 --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime8.C +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime8.C
@@ -1,4 +1,4 @@ -// P3450R1 - Extend std::is_within_lifetime +// P2641R4 - Checking if a union alternative is active // { dg-do compile { target c++20 } } namespace std {
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime9.C b/gcc/testsuite/g++.dg/cpp26/within-lifetime9.C index 905d544..d9751e4 100644 --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime9.C +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime9.C
@@ -1,4 +1,4 @@ -// P3450R1 - Extend std::is_within_lifetime +// P2641R4 - Checking if a union alternative is active // { dg-do compile { target c++20 } } namespace std {
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 36f8c9c..9c52358 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def
@@ -2520,8 +2520,10 @@ ftms = { name = within_lifetime; + // 202306 P2641R4 Checking if a union alternative is active + // 202603 P3450R1 Extend std::is_within_lifetime values = { - v = 202306; + v = 202603; cxxmin = 26; extra_cond = "__has_builtin(__builtin_is_within_lifetime)"; };
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index e79ea50..e000305 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h
@@ -2776,9 +2776,9 @@ #if !defined(__cpp_lib_within_lifetime) # if (__cplusplus > 202302L) && (__has_builtin(__builtin_is_within_lifetime)) -# define __glibcxx_within_lifetime 202306L +# define __glibcxx_within_lifetime 202603L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_within_lifetime) -# define __cpp_lib_within_lifetime 202306L +# define __cpp_lib_within_lifetime 202603L # endif # endif #endif /* !defined(__cpp_lib_within_lifetime) */
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index 921ef28..83dbad1 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits
@@ -4401,11 +4401,15 @@ }; #endif // C++11 -#if __cpp_lib_within_lifetime >= 202306L // C++ >= 26 - template<typename _Tp> +#if __cpp_lib_within_lifetime >= 202603L // C++ >= 26 + template<typename _Up = void, typename _Tp> consteval bool is_within_lifetime(const _Tp* __p) noexcept - { return __builtin_is_within_lifetime (__p); } + { + return (__builtin_is_within_lifetime (__p) + && __builtin_constant_p(static_cast<const volatile _Up*>(__p) + && true)); + } #endif /// @} group metaprogramming