On Thu, Aug 28, 2025 at 08:11:04PM +0200, Tomasz Kaminski wrote: > Similar test, for Q being a base class, this is also aggregate in C++20. > And if you could reorganize the library test so satic_assert follows the > type definition, > that would be great.
Done. 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 20:36:08.805719765 +0200 @@ -0,0 +1,139 @@ +// { 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; +struct AA { Q a; Q b; }; +struct AB { Q a; Q b; ~AB () = default; }; +struct AC { Q a; Q b; ~AC () {} }; +struct AD : public Q {}; +struct AE : public Q { ~AE () = default; }; +struct AF : public Q { ~AF () {} }; + +#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))]])); +SA (__builtin_is_implicit_lifetime (AA)); +SA (__builtin_is_implicit_lifetime (AB)); +SA (!__builtin_is_implicit_lifetime (AC)); +#if __cplusplus >= 201703L +SA (__builtin_is_implicit_lifetime (AD)); +SA (__builtin_is_implicit_lifetime (AE)); +#else +SA (!__builtin_is_implicit_lifetime (AD)); +SA (!__builtin_is_implicit_lifetime (AE)); +#endif +SA (!__builtin_is_implicit_lifetime (AF)); + +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 20:33:41.480591119 +0200 @@ -0,0 +1,129 @@ +// 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); + +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> ); +enum W { W1 }; +static_assert( Is_implicit_lifetime<W> ); +enum class X : int { X1 }; +static_assert( Is_implicit_lifetime<const volatile X> ); +static_assert( Is_implicit_lifetime<int *> ); +static_assert( Is_implicit_lifetime<int (*) (int)> ); +struct Y { int g; int foo (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 (&) ()> ); +struct Z; +static_assert( Is_implicit_lifetime<Z []> ); +static_assert( Is_implicit_lifetime<Z [5]> ); +struct A { int a, b, c; }; +static_assert( Is_implicit_lifetime<A> ); +class B { static int a; private: static int b; public: int c; }; +static_assert( Is_implicit_lifetime<B> ); +struct C { C () {} int a, b, c; }; +static_assert( Is_implicit_lifetime<C> ); +struct D { explicit D (int) {} int a, b, c; }; +static_assert( Is_implicit_lifetime<D> ); +struct E : public A { int d, e, f; }; +static_assert( Is_implicit_lifetime<E> ); +struct F : public C { using C::C; int d, e, f; }; +static_assert( Is_implicit_lifetime<F> ); +class G { int a, b; }; +static_assert( Is_implicit_lifetime<G> ); +struct H { private: int a, b; }; +static_assert( Is_implicit_lifetime<H> ); +struct I { protected: int a, b; }; +static_assert( Is_implicit_lifetime<I> ); +struct J { int a, b; void foo (); }; +static_assert( Is_implicit_lifetime<J> ); +struct K { int a, b; virtual void foo (); }; +static_assert( ! Is_implicit_lifetime<K> ); +struct L : virtual public A { int d, e; }; +static_assert( ! Is_implicit_lifetime<L> ); +struct M : protected A { int d, e; }; +static_assert( Is_implicit_lifetime<M> ); +struct N : private A { int d, e; }; +static_assert( Is_implicit_lifetime<N> ); +struct O { O () = delete; int a, b, c; }; +static_assert( Is_implicit_lifetime<O> ); +struct P { P () = default; int a, b, c; }; +static_assert( Is_implicit_lifetime<P> ); +struct Q { Q (); Q (const Q &); int a, b, c; }; +static_assert( ! Is_implicit_lifetime<Q> ); +struct R { R (); R (const R &); R (R &&) = default; int a, b, c; }; +static_assert( Is_implicit_lifetime<R> ); +struct S { S (); ~S (); int a, b, c; }; +static_assert( ! Is_implicit_lifetime<S> ); +static_assert( Is_implicit_lifetime<S [3]> ); +struct T { T (); ~T () = default; int a, b, c; }; +static_assert( Is_implicit_lifetime<T> ); +struct U { U (); U (const U &) = default; int a, b, c; }; +static_assert( Is_implicit_lifetime<U> ); +struct V { V () = default; V (const V &); int a, b, c; }; +static_assert( Is_implicit_lifetime<V> ); +struct AA { Q a; Q b; }; +static_assert( Is_implicit_lifetime<AA> ); +struct AB { Q a; Q b; ~AB () = default; }; +static_assert( Is_implicit_lifetime<AB> ); +struct AC { Q a; Q b; ~AC () {} }; +static_assert( ! Is_implicit_lifetime<AC> ); +struct AD : public Q {}; +static_assert( Is_implicit_lifetime<AD> ); +struct AE : public Q { ~AE () = default; }; +static_assert( Is_implicit_lifetime<AE> ); +struct AF : public Q { ~AF () {} }; +static_assert( ! Is_implicit_lifetime<AF> ); Jakub
