https://gcc.gnu.org/g:2be174f14c0bea9c0fe979467104d7d9027fe093
commit r17-1997-g2be174f14c0bea9c0fe979467104d7d9027fe093 Author: Jakub Jelinek <[email protected]> Date: Tue Jun 30 09:26:29 2026 +0200 c++: Fix up __builtin_is_implicit_lifetime [PR126007] https://eel.is/c++draft/class.prop#8.2 says that implicit lifetime class shall have at least one trivial eligible constructor. We currently walk all ctors and if it is default/copy/move ctor which is trivial and not deleted, return true, but as the following testcase shows, we should check also for unsatisfied constraints. 2026-06-30 Jakub Jelinek <[email protected]> PR c++/126007 * tree.cc (eligible_special_member_function_p): New function. (implicit_lifetime_type_p): Use true instead of 1 in function comment. Add some further comments. Use eligible_special_member_function_p instead of !DECL_DELETED_FN. * g++.dg/ext/is_implicit_lifetime3.C: New test. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/tree.cc | 25 ++++++++++++++++++++++-- gcc/testsuite/g++.dg/ext/is_implicit_lifetime3.C | 11 +++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index dc885e47c01e..e543cf145195 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -4919,7 +4919,25 @@ trivially_copy_constructible_p (tree t) return is_trivially_xible (INIT_EXPR, t, arg); } -/* Returns 1 iff type T is an implicit-lifetime type, as defined in +/* Returns true iff FN (which is a special member function) is + an eligible special member function, as defined in [special]/6. + The "no special member function of the same kind whose associated + constraints, if any, are satisfied is more constrained" condition + is not checked, this is checked during add_method instead. */ + +static bool +eligible_special_member_function_p (tree fn) +{ + /* The function is not deleted. */ + if (DECL_DELETED_FN (fn)) + return false; + /* The associated constraints, if any, are satisfied. */ + if (!constraints_satisfied_p (fn)) + return false; + return true; +} + +/* Returns true iff type T is an implicit-lifetime type, as defined in [basic.types.general] and [class.prop]. */ bool @@ -4934,6 +4952,7 @@ implicit_lifetime_type_p (tree t) if (!CLASS_TYPE_P (t)) return false; t = TYPE_MAIN_VARIANT (t); + /* It is an aggregate whose destructor is not user-provided. */ if (CP_AGGREGATE_TYPE_P (t) && (!CLASSTYPE_DESTRUCTOR (t) || !user_provided_p (CLASSTYPE_DESTRUCTOR (t)))) @@ -4946,6 +4965,8 @@ implicit_lifetime_type_p (tree t) return false; if (CLASSTYPE_LAZY_DESTRUCTOR (t)) lazily_declare_fn (sfk_destructor, t); + /* It has at least one trivial eligible constructor and a trivial, + non-deleted destructor. */ tree fn = CLASSTYPE_DESTRUCTOR (t); if (!fn || DECL_DELETED_FN (fn)) return false; @@ -4955,7 +4976,7 @@ implicit_lifetime_type_p (tree t) fn = *iter; if ((default_ctor_p (fn) || copy_fn_p (fn) || move_fn_p (fn)) && trivial_fn_p (fn) - && !DECL_DELETED_FN (fn)) + && eligible_special_member_function_p (fn)) return true; } return false; diff --git a/gcc/testsuite/g++.dg/ext/is_implicit_lifetime3.C b/gcc/testsuite/g++.dg/ext/is_implicit_lifetime3.C new file mode 100644 index 000000000000..c3b734e9d06e --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/is_implicit_lifetime3.C @@ -0,0 +1,11 @@ +// PR c++/126007 +// { dg-do compile { target c++20 } } + +template <bool B> +struct S { + S () requires B = default; + S (const S &) {} +}; + +static_assert (!__builtin_is_implicit_lifetime (S<false>)); +static_assert (__builtin_is_implicit_lifetime (S<true>));
