On 1/21/26 5:17 PM, Jakub Jelinek wrote:
Hi!
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.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2026-01-21 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.
--- gcc/cp/decl.cc.jj 2026-01-15 16:33:46.976098487 +0100
+++ gcc/cp/decl.cc 2026-01-20 16:24:42.229486191 +0100
@@ -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)
I don't think we need to check special_memfn_p, other functions can't be
defaulted (and if they could they'd probably also be immediate-escalating).
OK without that.
+ check_consteval_only_fn (decl);
if (ctype == NULL_TREE || check)
return decl;
@@ -16498,7 +16502,7 @@ grokdeclarator (const cp_declarator *dec
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 *dec
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,
--- gcc/testsuite/g++.dg/reflect/pr123404.C.jj 2026-01-20 16:41:27.222324537
+0100
+++ gcc/testsuite/g++.dg/reflect/pr123404.C 2026-01-20 16:40:22.613427869
+0100
@@ -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 ();
+}
Jakub