https://gcc.gnu.org/g:7898e14723b19f803a4be6a04a29eecb78d1e8d7

commit r16-6310-g7898e14723b19f803a4be6a04a29eecb78d1e8d7
Author: Jakub Jelinek <[email protected]>
Date:   Sat Dec 20 12:04:36 2025 +0100

    c++: Ignore access in is_implicit_lifetime trait decisions [PR122690]
    
    I've implemented the non-aggregate part of is_implicit_lifetime
    paper according to the paper's comment how it can be implemented, i.e.
    the std::conjunction from
    template<typename T>
    struct is_implicit_lifetime : std::disjunction<
        std::is_scalar<T>,
        std::is_array<T>,
        std::is_aggregate<T>,
        std::conjunction<
            std::is_trivially_destructible<T>,
            std::disjunction<
                std::is_trivially_default_constructible<T>,
                std::is_trivially_copy_constructible<T>,
                std::is_trivially_move_constructible<T>>>> {};
    in the paper.  But as reported in PR122690, the actual wording in the
    paper is different from that, the
    https://eel.is/c++draft/class.prop#16.2 part of it:
    "it has at least one trivial eligible constructor and a trivial,
    non-deleted destructor" doesn't talk anything about accessibility
    of those ctors or dtors, only triviality, not being deleted and eligibility.
    My understanding is that GCC handles the last 2 bullets of
    https://eel.is/c++draft/special#6 by not adding ctors ineligible because
    of those into the overload at all, and for testing deleted cdtors
    I need to lazily declare them in case such synthetization makes them
    deleted.
    So, this patch first checks for the easy cases (where the flags on the
    type say the dtor is non-trivial or all the 3 special member ctors are
    non-trivial) and if not, lazily declares them if needed and checks if they
    are trivial and non-deleted.
    
    2025-12-20  Jakub Jelinek  <[email protected]>
    
            PR c++/122690
            * tree.cc (implicit_lifetime_type_p): Don't test is_trivially_xible,
            instead try to lazily declare dtor and default, copy and move ctors
            if needed and check for their triviality and whether they are
            deleted.
    
            * g++.dg/ext/is_implicit_lifetime2.C: New test.

Diff:
---
 gcc/cp/tree.cc                                   | 28 +++++---
 gcc/testsuite/g++.dg/ext/is_implicit_lifetime2.C | 91 ++++++++++++++++++++++++
 2 files changed, 108 insertions(+), 11 deletions(-)

diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 046f60a9859a..809e30af75da 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -4908,18 +4908,24 @@ implicit_lifetime_type_p (tree t)
       && (!CLASSTYPE_DESTRUCTOR (t)
          || !user_provided_p (CLASSTYPE_DESTRUCTOR (t))))
     return true;
-  if (is_trivially_xible (BIT_NOT_EXPR, t, NULL_TREE))
+  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+    return false;
+  if (TYPE_HAS_COMPLEX_DFLT (t)
+      && TYPE_HAS_COMPLEX_COPY_CTOR (t)
+      && TYPE_HAS_COMPLEX_MOVE_CTOR (t))
+    return false;
+  if (CLASSTYPE_LAZY_DESTRUCTOR (t))
+    lazily_declare_fn (sfk_destructor, t);
+  tree fn = CLASSTYPE_DESTRUCTOR (t);
+  if (!fn || DECL_DELETED_FN (fn))
+    return false;
+  for (ovl_iterator iter (get_class_binding (t, complete_ctor_identifier));
+       iter; ++iter)
     {
-      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))
+      fn = *iter;
+      if ((default_ctor_p (fn) || copy_fn_p (fn) || move_fn_p (fn))
+         && trivial_fn_p (fn)
+         && !DECL_DELETED_FN (fn))
        return true;
     }
   return false;
diff --git a/gcc/testsuite/g++.dg/ext/is_implicit_lifetime2.C 
b/gcc/testsuite/g++.dg/ext/is_implicit_lifetime2.C
new file mode 100644
index 000000000000..b0973fea7e8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_implicit_lifetime2.C
@@ -0,0 +1,91 @@
+// PR c++/122690
+// { dg-do compile { target c++11 } }
+
+class A {
+  int i;
+public:
+  A () = default;
+  A (int i) : i(i) { }
+  A (A const &x) : i(x.i) {}
+  A (A &&x) : i(x.i) {}
+};
+class B {
+  int i;
+protected:
+  B () = default;
+public:
+  B (int i) : i(i) { }
+  B (B const &x) : i(x.i) {}
+  B (B &&x) : i(x.i) {}
+};
+class C {
+  int i;
+private:
+  C () = default;
+public:
+  C (int i) : i(i) { }
+  C (C const &x) : i(x.i) {}
+  C (C &&x) : i(x.i) {}
+};
+class D {
+  int i;
+public:
+  D (D const &) = default;
+  D () : i(0) {}
+  D (int i) : i(i) { }
+  D (D &&x) : i(x.i) {}
+};
+class E {
+  int i;
+protected:
+  E (E const &) = default;
+public:
+  E () : i(0) {}
+  E (int i) : i(i) { }
+  E (E &&x) : i(x.i) {}
+};
+class F {
+  int i;
+private:
+  F (F const &) = default;
+public:
+  F () : i(0) {}
+  F (int i) : i(i) { }
+  F (F &&x) : i(x.i) {}
+};
+class G {
+  int i;
+public:
+  G (G &&) = default;
+  G () : i(0) {}
+  G (int i) : i(i) { }
+  G (const G &x) : i(x.i) {}
+};
+class H {
+  int i;
+protected:
+  H (H &&) = default;
+public:
+  H () : i(0) {}
+  H (int i) : i(i) { }
+  H (const H &x) : i(x.i) {}
+};
+class I {
+  int i;
+private:
+  I (I &&) = default;
+public:
+  I () : i(0) {}
+  I (int i) : i(i) { }
+  I (const I &x) : i(x.i) {}
+};
+
+static_assert (__builtin_is_implicit_lifetime (A), "");
+static_assert (__builtin_is_implicit_lifetime (B), "");
+static_assert (__builtin_is_implicit_lifetime (C), "");
+static_assert (__builtin_is_implicit_lifetime (D), "");
+static_assert (__builtin_is_implicit_lifetime (E), "");
+static_assert (__builtin_is_implicit_lifetime (F), "");
+static_assert (__builtin_is_implicit_lifetime (G), "");
+static_assert (__builtin_is_implicit_lifetime (H), "");
+static_assert (__builtin_is_implicit_lifetime (I), "");

Reply via email to