]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Implement C++23 P2674R1 - A trait for implicit lifetime types
authorJakub Jelinek <jakub@redhat.com>
Tue, 21 Oct 2025 17:17:11 +0000 (19:17 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 21 Oct 2025 17:17:11 +0000 (19:17 +0200)
The following patch attempts to implement the compiler side of 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.

The library part has been split into a separate patch still pending
review; committing it now so that reflection can use it in its
std::meta::is_implicit_lifetime_type implementation.

2025-10-21  Jakub Jelinek  <jakub@redhat.com>

gcc/cp/
* cp-tree.h: Implement C++23 P2674R1 - A trait for implicit lifetime
types.
(implicit_lifetime_type_p): Declare.
* tree.cc (implicit_lifetime_type_p): New function.
* cp-trait.def (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.

gcc/cp/constraint.cc
gcc/cp/cp-trait.def
gcc/cp/cp-tree.h
gcc/cp/semantics.cc
gcc/cp/tree.cc
gcc/testsuite/g++.dg/ext/is_implicit_lifetime.C [new file with mode: 0644]

index 309ebc8132432109ee6f616ea60347a29805a3d1..925dc673d8f4a9b69a42fd6f6ff8b61700fbd3f7 100644 (file)
@@ -3170,6 +3170,9 @@ diagnose_trait_expr (location_t loc, tree expr, tree args)
     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))
index 5e4493a84a0fb4358e82806c61aaa76c1657bf02..c8201846074d0d7d8b8f7651f02f55a4d300fa41 100644 (file)
@@ -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)
index fcba9f5c0b02b475c95ba097e8e722b52632f894..8c211ab7874f38f1049a3662f83eb640af50dbba 100644 (file)
@@ -8376,6 +8376,7 @@ extern bool std_layout_type_p                     (const_tree);
 extern bool trivial_type_p                     (const_tree);
 extern bool trivially_relocatable_type_p       (tree);
 extern bool replaceable_type_p                 (tree);
+extern bool implicit_lifetime_type_p           (tree);
 extern bool trivially_copyable_p               (const_tree);
 extern bool type_has_unique_obj_representations (const_tree);
 extern bool scalarish_type_p                   (const_tree);
index c818b73953923315cb4036cf0b1862b68aa97bea..ad12155e6b336c1bbee98678c3bc1ccbdc1eaab1 100644 (file)
@@ -13595,6 +13595,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_IMPLICIT_LIFETIME:
+      return implicit_lifetime_type_p (type1);
+
     case CPTK_IS_INVOCABLE:
       return !error_operand_p (build_invoke (type1, type2, tf_none));
 
@@ -13914,6 +13917,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
        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;
index 814465c65292186e63e12568409c151d3ce9ebd8..3edb74a256f2ab3df85db2e5ba9d24eb4ce9cccb 100644 (file)
@@ -5097,6 +5097,42 @@ replaceable_type_p (tree t)
   return true;
 }
 
+/* Returns 1 iff type T is an implicit-lifetime type, as defined in
+   [basic.types.general] and [class.prop].  */
+
+bool
+implicit_lifetime_type_p (tree t)
+{
+  if (SCALAR_TYPE_P (t)
+      || (TREE_CODE (t) == ARRAY_TYPE
+         && !(TYPE_SIZE (t) && integer_zerop (TYPE_SIZE (t))))
+      /* GNU extension.  */
+      || TREE_CODE (t) == VECTOR_TYPE)
+    return true;
+  if (!CLASS_TYPE_P (t))
+    return false;
+  t = TYPE_MAIN_VARIANT (t);
+  if (CP_AGGREGATE_TYPE_P (t)
+      && (!CLASSTYPE_DESTRUCTOR (t)
+         || !user_provided_p (CLASSTYPE_DESTRUCTOR (t))))
+    return true;
+  if (is_trivially_xible (BIT_NOT_EXPR, t, NULL_TREE))
+    {
+      if (is_trivially_xible (INIT_EXPR, t, make_tree_vec (0)))
+       return true;
+      tree arg = make_tree_vec (1);
+      tree ct
+       = cp_build_qualified_type (t, (cp_type_quals (t) | TYPE_QUAL_CONST));
+      TREE_VEC_ELT (arg, 0) = cp_build_reference_type (ct, /*rval=*/false);
+      if (is_trivially_xible (INIT_EXPR, t, arg))
+       return true;
+      TREE_VEC_ELT (arg, 0) = t;
+      if (is_trivially_xible (INIT_EXPR, t, arg))
+       return true;
+    }
+  return false;
+}
+
 /* Returns 1 iff type T is a POD type, as defined in [basic.types].  */
 
 bool
diff --git a/gcc/testsuite/g++.dg/ext/is_implicit_lifetime.C b/gcc/testsuite/g++.dg/ext/is_implicit_lifetime.C
new file mode 100644 (file)
index 0000000..22c600d
--- /dev/null
@@ -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]));
+}