Hi! The following patch attempts to implement the C++23 P2674R1 paper. As mentioned in the paper, since CWG2605 the trait isn't really implementable purely on the library side.
Because it is implemented completely on the compiler side, it just uses SCALAR_TYPE_P and so can e.g. accept __int128 even in -std=c++23 mode, even when std::is_scalar_v<__int128> is false in that case. And as an extention it (like Clang) accepts _Complex types and vector types. I must say I'm quite surprised that any array types are considered implicit-lifetime, even if their element type is not, but perhaps there is some reason for that. Because std::is_array_v<int[0]> is false, it returns false for that as well, dunno if that shouldn't be changed for implicit-lifetime. It accepts also VLAs. Tested on x86_64-linux, ok for trunk? 2025-08-28 Jakub Jelinek <[email protected]> gcc/cp/ * cp-trait.def: Implement C++23 P2674R1 - A trait for implicit lifetime types. (IS_IMPLICIT_LIFETIME): New unary trait. * semantics.cc (trait_expr_value): Handle CPTK_IS_IMPLICIT_LIFETIME. (finish_trait_expr): Likewise. * constraint.cc (diagnose_trait_expr): Likewise. gcc/testsuite/ * g++.dg/ext/is_implicit_lifetime.C: New test. libstdc++-v3/ * include/bits/version.def (is_implicit_lifetime): New. * include/bits/version.h: Regenerate. * include/std/type_traits (std::is_implicit_lifetime, std::is_implicit_lifetime_v): New trait. * src/c++23/std.cc.in (std::is_implicit_lifetime, std::is_implicit_lifetime_v): Export. * testsuite/20_util/is_implicit_lifetime/version.cc: New test. * testsuite/20_util/is_implicit_lifetime/value.cc: New test. --- gcc/cp/cp-trait.def.jj 2025-08-15 22:38:28.866872528 +0200 +++ gcc/cp/cp-trait.def 2025-08-28 14:40:24.828249392 +0200 @@ -76,6 +76,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1) DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1) DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1) +DEFTRAIT_EXPR (IS_IMPLICIT_LIFETIME, "__builtin_is_implicit_lifetime", 1) DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1) DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2) DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1) --- gcc/cp/semantics.cc.jj 2025-08-28 10:50:43.432763513 +0200 +++ gcc/cp/semantics.cc 2025-08-28 16:52:48.006806831 +0200 @@ -13591,6 +13591,38 @@ trait_expr_value (cp_trait_kind kind, tr case CPTK_IS_FUNCTION: return type_code1 == FUNCTION_TYPE; + case CPTK_IS_IMPLICIT_LIFETIME: + if (SCALAR_TYPE_P (type1) + || (type_code1 == ARRAY_TYPE + && !(TYPE_SIZE (type1) && integer_zerop (TYPE_SIZE (type1)))) + /* GNU extension. */ + || type_code1 == VECTOR_TYPE) + return true; + if (!CLASS_TYPE_P (type1)) + return false; + type1 = TYPE_MAIN_VARIANT (type1); + if (CP_AGGREGATE_TYPE_P (type1) + && (!CLASSTYPE_DESTRUCTOR (type1) + || !user_provided_p (CLASSTYPE_DESTRUCTOR (type1)))) + return true; + if (is_trivially_xible (BIT_NOT_EXPR, type1, NULL_TREE)) + { + if (is_trivially_xible (INIT_EXPR, type1, make_tree_vec (0))) + return true; + tree arg = make_tree_vec (1); + tree ctype1 = cp_build_qualified_type (type1, (cp_type_quals (type1) + | TYPE_QUAL_CONST)); + TREE_VEC_ELT (arg, 0) + = cp_build_reference_type (ctype1, /*rval=*/false); + if (is_trivially_xible (INIT_EXPR, type1, arg)) + return true; + TREE_VEC_ELT (arg, 0) + = cp_build_reference_type (type1, /*rval=*/true); + if (is_trivially_xible (INIT_EXPR, type1, arg)) + return true; + } + return false; + case CPTK_IS_INVOCABLE: return !error_operand_p (build_invoke (type1, type2, tf_none)); @@ -13910,6 +13942,7 @@ finish_trait_expr (location_t loc, cp_tr type to know whether an array is an aggregate, so use kind=4 here. */ case CPTK_IS_AGGREGATE: case CPTK_IS_FINAL: + case CPTK_IS_IMPLICIT_LIFETIME: if (!check_trait_type (type1, /* kind = */ 4)) return error_mark_node; break; --- gcc/cp/constraint.cc.jj 2025-08-15 22:38:29.017870706 +0200 +++ gcc/cp/constraint.cc 2025-08-28 13:02:19.197985274 +0200 @@ -3170,6 +3170,9 @@ diagnose_trait_expr (location_t loc, tre case CPTK_IS_FUNCTION: inform (loc, "%qT is not a function", t1); break; + case CPTK_IS_IMPLICIT_LIFETIME: + inform (decl_loc, "%qT is not an implicit lifetime type", t1); + break; case CPTK_IS_INVOCABLE: { if (!TREE_VEC_LENGTH (t2)) --- gcc/testsuite/g++.dg/ext/is_implicit_lifetime.C.jj 2025-08-28 14:14:33.475719420 +0200 +++ gcc/testsuite/g++.dg/ext/is_implicit_lifetime.C 2025-08-28 17:01:25.544140044 +0200 @@ -0,0 +1,122 @@ +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-add-options float16 } +// { dg-add-options float32 } +// { dg-add-options float64 } +// { dg-add-options float128 } + +struct A { int a, b, c; }; +class B { static int a; private: static int b; public: int c; }; +struct C { C () {} int a, b, c; }; +struct D { explicit D (int) {} int a, b, c; }; +struct E : public A { int d, e, f; }; +struct F : public C { using C::C; int d, e, f; }; +class G { int a, b; }; +struct H { private: int a, b; }; +struct I { protected: int a, b; }; +struct J { int a, b; void foo (); }; +struct K { int a, b; virtual void foo (); }; +struct L : virtual public A { int d, e; }; +struct M : protected A { int d, e; }; +struct N : private A { int d, e; }; +struct O { O () = delete; int a, b, c; }; +struct P { P () = default; int a, b, c; }; +struct Q { Q (); Q (const Q &); int a, b, c; }; +struct R { R (); R (const R &); R (R &&) = default; int a, b, c; }; +struct S { S (); ~S (); int a, b, c; }; +struct T { T (); ~T () = default; int a, b, c; }; +struct U { U (); U (const U &) = default; int a, b, c; }; +struct V { V () = default; V (const V &); int a, b, c; }; +enum W { W1 }; +enum class X : int { X1 }; +struct Y { int g; int foo (int); }; +struct Z; + +#define SA(X) static_assert ((X), #X) + +SA (!__builtin_is_implicit_lifetime (void)); +SA (!__builtin_is_implicit_lifetime (const void)); +SA (!__builtin_is_implicit_lifetime (volatile void)); +SA (__builtin_is_implicit_lifetime (char)); +SA (__builtin_is_implicit_lifetime (signed char)); +SA (__builtin_is_implicit_lifetime (const unsigned char)); +SA (__builtin_is_implicit_lifetime (short)); +SA (__builtin_is_implicit_lifetime (volatile unsigned short)); +SA (__builtin_is_implicit_lifetime (int)); +SA (__builtin_is_implicit_lifetime (unsigned int)); +SA (__builtin_is_implicit_lifetime (const volatile long)); +SA (__builtin_is_implicit_lifetime (unsigned long)); +SA (__builtin_is_implicit_lifetime (long long)); +SA (__builtin_is_implicit_lifetime (unsigned long long)); +#ifdef __SIZEOF_INT128__ +SA (__builtin_is_implicit_lifetime (__int128)); +SA (__builtin_is_implicit_lifetime (unsigned __int128)); +#endif +SA (__builtin_is_implicit_lifetime (float)); +SA (__builtin_is_implicit_lifetime (double)); +SA (__builtin_is_implicit_lifetime (long double volatile)); +#ifdef __STDCPP_FLOAT16_T__ +SA (__builtin_is_implicit_lifetime (_Float16)); +#endif +#ifdef __STDCPP_FLOAT32_T__ +SA (__builtin_is_implicit_lifetime (_Float32)); +#endif +#ifdef __STDCPP_FLOAT64_T__ +SA (__builtin_is_implicit_lifetime (const _Float64)); +#endif +#ifdef __STDCPP_FLOAT128_T__ +SA (__builtin_is_implicit_lifetime (_Float128)); +#endif +#ifdef __STDCPP_BFLOAT16_T__ +SA (__builtin_is_implicit_lifetime (decltype(0.bf16))); +#endif +SA (__builtin_is_implicit_lifetime (W)); +SA (__builtin_is_implicit_lifetime (const volatile X)); +SA (__builtin_is_implicit_lifetime (int *)); +SA (__builtin_is_implicit_lifetime (int (*) (int))); +SA (__builtin_is_implicit_lifetime (int (Y::*))); +SA (__builtin_is_implicit_lifetime (int (Y::*) (int))); +SA (!__builtin_is_implicit_lifetime (int &)); +SA (!__builtin_is_implicit_lifetime (char &&)); +SA (__builtin_is_implicit_lifetime (int [])); +SA (!__builtin_is_implicit_lifetime (int [0])); +SA (__builtin_is_implicit_lifetime (int [1])); +SA (__builtin_is_implicit_lifetime (const Y [42])); +SA (!__builtin_is_implicit_lifetime (int ())); +SA (!__builtin_is_implicit_lifetime (int () &)); +SA (!__builtin_is_implicit_lifetime (int () const)); +SA (!__builtin_is_implicit_lifetime (int (&) ())); +SA (!__builtin_is_implicit_lifetime (Z)); // { dg-error "invalid use of incomplete type 'struct Z'" } +SA (__builtin_is_implicit_lifetime (Z [])); +SA (__builtin_is_implicit_lifetime (Z [5])); +SA (__builtin_is_implicit_lifetime (A)); +SA (__builtin_is_implicit_lifetime (B)); +SA (__builtin_is_implicit_lifetime (C)); +SA (__builtin_is_implicit_lifetime (D)); +SA (__builtin_is_implicit_lifetime (E)); +SA (__builtin_is_implicit_lifetime (F)); +SA (__builtin_is_implicit_lifetime (G)); +SA (__builtin_is_implicit_lifetime (H)); +SA (__builtin_is_implicit_lifetime (I)); +SA (__builtin_is_implicit_lifetime (J)); +SA (!__builtin_is_implicit_lifetime (K)); +SA (!__builtin_is_implicit_lifetime (L)); +SA (__builtin_is_implicit_lifetime (M)); +SA (__builtin_is_implicit_lifetime (N)); +SA (__builtin_is_implicit_lifetime (O)); +SA (__builtin_is_implicit_lifetime (P)); +SA (!__builtin_is_implicit_lifetime (Q)); +SA (__builtin_is_implicit_lifetime (R)); +SA (!__builtin_is_implicit_lifetime (S)); +SA (__builtin_is_implicit_lifetime (S [3])); +SA (__builtin_is_implicit_lifetime (T)); +SA (__builtin_is_implicit_lifetime (U)); +SA (__builtin_is_implicit_lifetime (V)); +SA (__builtin_is_implicit_lifetime (_Complex double)); +SA (__builtin_is_implicit_lifetime (int [[gnu::vector_size (4 * sizeof (int))]])); + +void +foo (int n) +{ + SA (__builtin_is_implicit_lifetime (char [n])); +} --- libstdc++-v3/include/bits/version.def.jj 2025-08-23 15:00:05.175775859 +0200 +++ libstdc++-v3/include/bits/version.def 2025-08-28 17:06:44.190031458 +0200 @@ -2091,6 +2091,15 @@ ftms = { }; }; +ftms = { + name = is_implicit_lifetime; + values = { + v = 202302; + cxxmin = 23; + extra_cond = "__has_builtin(__builtin_is_implicit_lifetime)"; + }; +}; + // Standard test specifications. stds[97] = ">= 199711L"; stds[03] = ">= 199711L"; --- libstdc++-v3/include/bits/version.h.jj 2025-08-23 15:00:05.176775846 +0200 +++ libstdc++-v3/include/bits/version.h 2025-08-28 17:06:49.160883915 +0200 @@ -2343,4 +2343,14 @@ #endif /* !defined(__cpp_lib_constexpr_exceptions) && defined(__glibcxx_want_constexpr_exceptions) */ #undef __glibcxx_want_constexpr_exceptions +#if !defined(__cpp_lib_is_implicit_lifetime) +# if (__cplusplus >= 202100L) && (__has_builtin(__builtin_is_implicit_lifetime)) +# define __glibcxx_is_implicit_lifetime 202302L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_is_implicit_lifetime) +# define __cpp_lib_is_implicit_lifetime 202302L +# endif +# endif +#endif /* !defined(__cpp_lib_is_implicit_lifetime) && defined(__glibcxx_want_is_implicit_lifetime) */ +#undef __glibcxx_want_is_implicit_lifetime + #undef __glibcxx_want_all --- libstdc++-v3/include/std/type_traits.jj 2025-07-16 13:10:11.054411335 +0200 +++ libstdc++-v3/include/std/type_traits 2025-08-28 17:36:11.404329402 +0200 @@ -46,6 +46,7 @@ #define __glibcxx_want_is_aggregate #define __glibcxx_want_is_constant_evaluated #define __glibcxx_want_is_final +#define __glibcxx_want_is_implicit_lifetime #define __glibcxx_want_is_invocable #define __glibcxx_want_is_layout_compatible #define __glibcxx_want_is_nothrow_convertible @@ -4052,6 +4053,22 @@ template<typename _Ret, typename _Fn, ty # endif #endif +#ifdef __cpp_lib_is_implicit_lifetime // C++ >= 23 + /// True if the type is an implicit-lifetime type. + /// @since C++23 + + template<typename _Tp> + struct is_implicit_lifetime + : bool_constant<__builtin_is_implicit_lifetime(_Tp)> + { }; + + /// @ingroup variable_templates + /// @since C++23 + template<typename _Tp> + inline constexpr bool is_implicit_lifetime_v + = __builtin_is_implicit_lifetime(_Tp); +#endif + #ifdef __cpp_lib_reference_from_temporary // C++ >= 23 && ref_{converts,constructs}_from_temp /// True if _Tp is a reference type, a _Up value can be bound to _Tp in /// direct-initialization, and a temporary object would be bound to --- libstdc++-v3/src/c++23/std.cc.in.jj 2025-08-23 15:00:05.209775407 +0200 +++ libstdc++-v3/src/c++23/std.cc.in 2025-08-28 17:10:43.784942152 +0200 @@ -3216,6 +3216,10 @@ export namespace std using std::is_scoped_enum; using std::is_scoped_enum_v; #endif +#if __cpp_lib_is_implicit_lifetime + using std::is_implicit_lifetime; + using std::is_implicit_lifetime_v; +#endif } // <typeindex> --- libstdc++-v3/testsuite/20_util/is_implicit_lifetime/version.cc.jj 2025-08-28 17:16:17.590650275 +0200 +++ libstdc++-v3/testsuite/20_util/is_implicit_lifetime/version.cc 2025-08-28 17:17:07.215014852 +0200 @@ -0,0 +1,27 @@ +// Copyright (C) 2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do compile { target c++23 } } +// { dg-add-options no_pch } + +#include <version> + +#ifndef __cpp_lib_is_implicit_lifetime +# error "Feature test macro for is_implicit_lifetime is missing in <version>" +#elif __cpp_lib_is_implicit_lifetime < 202302L +# error "Feature test macro for is_implicit_lifetime has wrong value in <version>" +#endif --- libstdc++-v3/testsuite/20_util/is_implicit_lifetime/value.cc.jj 2025-08-28 17:16:20.746609864 +0200 +++ libstdc++-v3/testsuite/20_util/is_implicit_lifetime/value.cc 2025-08-28 17:40:30.406982275 +0200 @@ -0,0 +1,122 @@ +// Copyright (C) 2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do compile { target c++23 } } +// { dg-add-options no_pch } + +#include <type_traits> + +#ifndef __cpp_lib_is_implicit_lifetime +# error "Feature test macro for is_implicit_lifetime is missing in <type_traits>" +#elif __cpp_lib_is_implicit_lifetime < 202302L +# error "Feature test macro for is_implicit_lifetime has wrong value in <type_traits>" +#endif + +#include <testsuite_tr1.h> + +template<typename T> + concept Is_implicit_lifetime + = __gnu_test::test_category<std::is_implicit_lifetime, T>(true); + +struct A { int a, b, c; }; +class B { static int a; private: static int b; public: int c; }; +struct C { C () {} int a, b, c; }; +struct D { explicit D (int) {} int a, b, c; }; +struct E : public A { int d, e, f; }; +struct F : public C { using C::C; int d, e, f; }; +class G { int a, b; }; +struct H { private: int a, b; }; +struct I { protected: int a, b; }; +struct J { int a, b; void foo (); }; +struct K { int a, b; virtual void foo (); }; +struct L : virtual public A { int d, e; }; +struct M : protected A { int d, e; }; +struct N : private A { int d, e; }; +struct O { O () = delete; int a, b, c; }; +struct P { P () = default; int a, b, c; }; +struct Q { Q (); Q (const Q &); int a, b, c; }; +struct R { R (); R (const R &); R (R &&) = default; int a, b, c; }; +struct S { S (); ~S (); int a, b, c; }; +struct T { T (); ~T () = default; int a, b, c; }; +struct U { U (); U (const U &) = default; int a, b, c; }; +struct V { V () = default; V (const V &); int a, b, c; }; +enum W { W1 }; +enum class X : int { X1 }; +struct Y { int g; int foo (int); }; +struct Z; + +void +test01() +{ + static_assert( ! Is_implicit_lifetime<void> ); + static_assert( ! Is_implicit_lifetime<const void> ); + static_assert( ! Is_implicit_lifetime<volatile void> ); + static_assert( Is_implicit_lifetime<char> ); + static_assert( Is_implicit_lifetime<signed char> ); + static_assert( Is_implicit_lifetime<const unsigned char> ); + static_assert( Is_implicit_lifetime<short> ); + static_assert( Is_implicit_lifetime<volatile unsigned short> ); + static_assert( Is_implicit_lifetime<int> ); + static_assert( Is_implicit_lifetime<unsigned int> ); + static_assert( Is_implicit_lifetime<const volatile long> ); + static_assert( Is_implicit_lifetime<unsigned long> ); + static_assert( Is_implicit_lifetime<long long> ); + static_assert( Is_implicit_lifetime<unsigned long long> ); + static_assert( Is_implicit_lifetime<float> ); + static_assert( Is_implicit_lifetime<double> ); + static_assert( Is_implicit_lifetime<long double volatile> ); + static_assert( Is_implicit_lifetime<W> ); + static_assert( Is_implicit_lifetime<const volatile X> ); + static_assert( Is_implicit_lifetime<int *> ); + static_assert( Is_implicit_lifetime<int (*) (int)> ); + static_assert( Is_implicit_lifetime<int (Y::*)> ); + static_assert( Is_implicit_lifetime<int (Y::*) (int)> ); + static_assert( ! Is_implicit_lifetime<int &> ); + static_assert( ! Is_implicit_lifetime<char &&> ); + static_assert( Is_implicit_lifetime<int []> ); + static_assert( Is_implicit_lifetime<int [1]> ); + static_assert( Is_implicit_lifetime<const Y [42]> ); + static_assert( ! Is_implicit_lifetime<int ()> ); + static_assert( ! Is_implicit_lifetime<int () &> ); + static_assert( ! Is_implicit_lifetime<int () const> ); + static_assert( ! Is_implicit_lifetime<int (&) ()> ); + static_assert( Is_implicit_lifetime<Z []> ); + static_assert( Is_implicit_lifetime<Z [5]> ); + static_assert( Is_implicit_lifetime<A> ); + static_assert( Is_implicit_lifetime<B> ); + static_assert( Is_implicit_lifetime<C> ); + static_assert( Is_implicit_lifetime<D> ); + static_assert( Is_implicit_lifetime<E> ); + static_assert( Is_implicit_lifetime<F> ); + static_assert( Is_implicit_lifetime<G> ); + static_assert( Is_implicit_lifetime<H> ); + static_assert( Is_implicit_lifetime<I> ); + static_assert( Is_implicit_lifetime<J> ); + static_assert( ! Is_implicit_lifetime<K> ); + static_assert( ! Is_implicit_lifetime<L> ); + static_assert( Is_implicit_lifetime<M> ); + static_assert( Is_implicit_lifetime<N> ); + static_assert( Is_implicit_lifetime<O> ); + static_assert( Is_implicit_lifetime<P> ); + static_assert( ! Is_implicit_lifetime<Q> ); + static_assert( Is_implicit_lifetime<R> ); + static_assert( ! Is_implicit_lifetime<S> ); + static_assert( Is_implicit_lifetime<S [3]> ); + static_assert( Is_implicit_lifetime<T> ); + static_assert( Is_implicit_lifetime<U> ); + static_assert( Is_implicit_lifetime<V> ); +} Jakub
