https://gcc.gnu.org/g:de19477814821362e0a2b3c10fc1d7cf62be60cc
commit r16-7060-gde19477814821362e0a2b3c10fc1d7cf62be60cc Author: Jakub Jelinek <[email protected]> Date: Tue Jan 27 10:23:43 2026 +0100 c++: Don't error on non-consteval defaulted special members in consteval-only classes [PR123404] As discussed earlier, the following testcase is incorrectly rejected. While check_consteval_only_fn -> immediate_escalating_function_p knows that defaulted special members are immediate-scalating: /* -- a defaulted special member function that is not declared with the consteval specifier */ special_function_kind sfk = special_memfn_p (fn); if (sfk != sfk_none && DECL_DEFAULTED_FN (fn)) return true; it returns false anyway, because the call is too early and DECL_DEFAULTED_FN is not set yet (unlike DECL_DELETED_FN). For DECL_DEFAULTED_FN there is quite more code, involving diagnostics for invalid uses of = delete etc. later in grokfield: else if (init == ridpointers[(int)RID_DEFAULT]) { if (defaultable_fn_check (value)) { DECL_DEFAULTED_FN (value) = 1; DECL_INITIALIZED_IN_CLASS_P (value) = 1; DECL_DECLARED_INLINE_P (value) = 1; /* grokfndecl set this to error_mark_node, but we want to leave it unset until synthesize_method. */ DECL_INITIAL (value) = NULL_TREE; } } but that is after the else if (init == ridpointers[(int)RID_DEFAULT]) initialized = SD_DEFAULTED; ... value = grokdeclarator (declarator, declspecs, FIELD, initialized, &attrlist); call in the same function where grokdeclarator calls grokfndecl. As for defaulted special member functions there is nothing to diagnose, those are always immediate-escalating or explicitly consteval and neither of those is diagnosed, the following patch just passes not just whether a fn is deleted, but whole initialized, so both whether it is deleted or defaulted, and just doesn't call check_consteval_only_fn in that case. During pt.cc check_consteval_only_fn call DECL_DEFAULTED_FN is already set before we test it. 2026-01-27 Jakub Jelinek <[email protected]> PR c++/123404 * decl.cc (grokfndecl): Replace bool deletedp argument with int initialized. Test initialized == SD_DELETED instead of deletedp. Don't call check_consteval_only_fn for defaulted special member fns. (grokdeclarator): Pass initialized rather than initialized == SD_DELETED to grokfndecl. * g++.dg/reflect/pr123404.C: New test. Diff: --- gcc/cp/decl.cc | 14 +++++++----- gcc/testsuite/g++.dg/reflect/pr123404.C | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index f1ba57cd177e..3bbc28c913d8 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -12103,7 +12103,7 @@ grokfndecl (tree ctype, int friendp, int publicp, int inlinep, - bool deletedp, + int initialized, bool xobj_func_p, special_function_kind sfk, bool funcdef_flag, @@ -12330,7 +12330,7 @@ grokfndecl (tree ctype, = !xobj_func_p && ctype && TREE_CODE (type) == FUNCTION_TYPE; DECL_FUNCTION_XOBJ_FLAG (decl) = xobj_func_p; - if (deletedp) + if (initialized == SD_DELETED) DECL_DELETED_FN (decl) = 1; if (ctype && funcdef_flag) @@ -12679,7 +12679,11 @@ grokfndecl (tree ctype, if (DECL_CONSTRUCTOR_P (decl) && !grok_ctor_properties (ctype, decl)) return NULL_TREE; - check_consteval_only_fn (decl); + /* Don't call check_consteval_only_fn for defaulted special member + functions. Those are immediate-escalating functions but at this point + DECL_DEFAULTED_P has not been set. */ + if (initialized != SD_DEFAULTED || special_memfn_p (decl) == sfk_none) + check_consteval_only_fn (decl); if (ctype == NULL_TREE || check) return decl; @@ -16498,7 +16502,7 @@ grokdeclarator (const cp_declarator *declarator, friendp ? -1 : 0, friendp, publicp, inlinep | (2 * constexpr_p) | (4 * concept_p) | (8 * consteval_p), - initialized == SD_DELETED, + initialized, is_xobj_member_function, sfk, funcdef_flag, late_return_type_p, template_count, in_namespace, @@ -16836,7 +16840,7 @@ grokdeclarator (const cp_declarator *declarator, publicp, inlinep | (2 * constexpr_p) | (4 * concept_p) | (8 * consteval_p), - initialized == SD_DELETED, + initialized, is_xobj_member_function, sfk, funcdef_flag, late_return_type_p, diff --git a/gcc/testsuite/g++.dg/reflect/pr123404.C b/gcc/testsuite/g++.dg/reflect/pr123404.C new file mode 100644 index 000000000000..3f137b342217 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/pr123404.C @@ -0,0 +1,39 @@ +// PR c++/123404 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +struct S { + decltype (^^::) a = ^^::; + consteval S () {} + S (const S &) = default; // { dg-bogus "function of consteval-only type must be declared 'consteval'" } + S (S &&) = default; // { dg-bogus "function of consteval-only type must be declared 'consteval'" } + S &operator= (const S &) = default; // { dg-bogus "function of consteval-only type must be declared 'consteval'" } + S &operator= (S &&) = default; // { dg-bogus "function of consteval-only type must be declared 'consteval'" } + consteval const char *what () { return "what"; } +}; + +template <typename T, T V> +struct U +{ + T a = V; + consteval U () {} + U (const U &) = default; // { dg-bogus "function of consteval-only type must be declared 'consteval'" } + U (U &&) = default; // { dg-bogus "function of consteval-only type must be declared 'consteval'" } + U &operator= (const U &) = default; // { dg-bogus "function of consteval-only type must be declared 'consteval'" } + U &operator= (U &&) = default; // { dg-bogus "function of consteval-only type must be declared 'consteval'" } + consteval const char *what () { return "what"; } +}; + +consteval +{ + S s; + S t; + t = s; + S u = t; + u.what (); + U <decltype (^^::), ^^::> v; + U <decltype (^^::), ^^::> w; + w = v; + U <decltype (^^::), ^^::> x = w; + x.what (); +}
