On Tue, 5 May 2026, Jason Merrill wrote:
> On 5/5/26 11:21 AM, Patrick Palka wrote:
> > On Tue, 5 May 2026, Patrick Palka wrote:
> >
> > > On Tue, 5 May 2026, Patrick Palka wrote:
> > >
> > > > On Tue, 5 May 2026, Jakub Jelinek wrote:
> > > >
> > > > > On Mon, May 04, 2026 at 09:25:06PM -0400, Patrick Palka wrote:
> > > > > > + else if (RECORD_OR_UNION_TYPE_P (t))
> > > > > > + {
> > > > > > + if (tree *slot = hash_map_safe_get
> > > > > > (consteval_only_class_cache, t))
> > > > > > + return *slot == boolean_true_node;
> > > > > > +
> > > > > > + if (class_set.add (t))
> > > > > > + /* Handle struct A { A* p; } etc. */
> > > > > > + return false;
> > > > > > +
> > > > > > + tristate r = COMPLETE_TYPE_P (t) ? false : tristate::unknown
> > > > > > ();
> > > > > > + for (tree member = TYPE_FIELDS (t); member; member =
> > > > > > DECL_CHAIN (member))
> > > > > > + if (TREE_CODE (member) == FIELD_DECL)
> > > > > > + {
> > > > > > + r = r || consteval_only_p_1 (TREE_TYPE (member),
> > > > > > class_set);
> > > > > > + if (r.is_true ())
> > > > > > + break;
> > > > > > + }
> > > > > > +
> > > > > > + if (COMPLETE_TYPE_P (t))
> > > > > > + {
> > > > > > + if (r.is_true ())
> > > > > > + hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
> > > > > > + t, boolean_true_node);
> > > > > > + else if (r.is_false ())
> > > > > > + hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
> > > > > > + t, boolean_false_node);
> > > > > > + }
> > > > > > +
> > > > > > + class_set.remove (t);
> > > > >
> > > > > Doesn't the above stmt result in really bad compile time complexity?
> > > > > If I have say
> > > > > struct S1;
> > > > > struct S2;
> > > > > struct S3 { S2 *a; };
> > > > > struct S4 { S3 *a; };
> > > > > struct S5 { S4 *a; };
> > > > > ...
> > > > > struct S9999 { S9998 *a; };
> > > > > struct S2 { S9999 *a; S1 *b; };
> > > > > then when asking if S2 is consteval-only, this will walk ~10000^2
> > > > > types
> > > > > rather than just ~10000 types. Wouldn't it be better to
> > > > > class_set.add (t) and never remove, but if we trigger that return
> > > > > false;
> > > > > in there, remember that we shouldn't cache r.is_false () results
> > > > > (perhaps
> > > > > with the exception when this was the non-recursive call)?
> > > >
> > > > Yes, and the current patch is buggy for:
> > > >
> > > > struct B { A* p; };
> > > > struct A {
> > > > B m;
> > > > meta::info n;
> > > > };
> > > >
> > > > We'd incorrectly deem B not consteval-only if we first walk it from A.
> > > >
> > > > It's apparently important for the testcase from this PR to return false
> > > > rather than unknown when detecting mutual recursion, otherwise we still
> > > > get a 4x slowdown.
> > >
> > > The reason it's important to optimistically return false upon hitting
> > > mutual recursion is so that we get a chance to cache the result.
> > > Otherwise we'll never cache the result for some mutually recursive
> > > types; consteval_only_p_1 will always return unknown.
> > >
> > > > But if we must return false in the case of mutual
> > > > recursion then indeed it's not safe to cache r.is_false () results
> > > > except for the outermost class because of scenarios like B above.
> > > >
> > > > To fix this efficiently I think we need to maintian both a class_seen
> > > > set and a class_stack. What do you think of the following?
> > > >
> > > > -- >8 --
> > > >
> > > > PR c++/125179
> > > >
> > > > gcc/cp/ChangeLog:
> > > >
> > > > * reflect.cc: Include <algorithm>.
> > > > (consteval_only_type_r): Remove this cp_walk_tree callback and
> > > > replace with ...
> > > > (consteval_only_p_state, consteval_only_class_cache)
> > > > (consteval_only_p_1): ... this recursive memoized
> > > > implementation.
> > > > (consteval_only_p): Define in terms of consteval_only_p_1.
> > > > ---
> > > > gcc/cp/reflect.cc | 138 ++++++++++++++++++++++++++++++++--------------
> > > > 1 file changed, 97 insertions(+), 41 deletions(-)
> > > >
> > > > diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
> > > > index 3b9f56ea5484..67a1e1b85895 100644
> > > > --- a/gcc/cp/reflect.cc
> > > > +++ b/gcc/cp/reflect.cc
> > > > @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see
> > > > <http://www.gnu.org/licenses/>. */
> > > > #include "config.h"
> > > > +#define INCLUDE_ALGORITHM
> > > > #include "system.h"
> > > > #include "coretypes.h"
> > > > #include "target.h"
> > > > @@ -8561,47 +8562,22 @@ splice (tree refl)
> > > > return refl;
> > > > }
> > > > -/* A walker for consteval_only_p. It cannot be a lambda, because we
> > > > - have to call this recursively, sigh. */
> > > > +/* State maintained during consteval_only_p_1 recursion. */
> > > > -static tree
> > > > -consteval_only_type_r (tree *tp, int *walk_subtrees, void *data)
> > > > +struct consteval_only_p_state
> > > > {
> > > > - tree t = *tp;
> > > > - /* Types can contain themselves recursively, hence this. */
> > > > - auto visited = static_cast<hash_set<tree> *>(data);
> > > > -
> > > > - if (!TYPE_P (t))
> > > > - return NULL_TREE;
> > > > -
> > > > - if (REFLECTION_TYPE_P (t))
> > > > - return t;
> > > > -
> > > > - if (typedef_variant_p (t))
> > > > - /* Tell cp_walk_subtrees to look through typedefs. */
> > > > - *walk_subtrees = 2;
> > > > -
> > > > - if (RECORD_OR_UNION_TYPE_P (t))
> > > > - {
> > > > - /* Don't walk template arguments; A<info>::type isn't a
> > > > consteval-only
> > > > - type. */
> > > > - *walk_subtrees = 0;
> > > > - /* So we have to walk the fields manually. */
> > > > - for (tree member = TYPE_FIELDS (t);
> > > > - member; member = DECL_CHAIN (member))
> > > > - if (TREE_CODE (member) == FIELD_DECL)
> > > > - if (tree r = cp_walk_tree (&TREE_TYPE (member),
> > > > - consteval_only_type_r, visited,
> > > > visited))
> > > > - return r;
> > > > - }
> > > > + /* The stack of class types we're recursively inside. */
> > > > + auto_vec<tree> class_stack;
> > > > + /* The set of class types we've seen. */
> > > > + hash_set<tree> class_seen;
> > > > + /* Whether we've seen mutual class recursion. */
> > > > + bool seen_mutual_recursion = false;
> > >
> > > > +};
> > > > - return NULL_TREE;
> > > > -}
> > > > +static tristate consteval_only_p_1 (tree, consteval_only_p_state&);
> > > > -/* True if T is a consteval-only type as per [basic.types.general]:
> > > > - "A type is consteval-only if it is either std::meta::info or a type
> > > > - compounded from a consteval-only type", or something that has
> > > > - a consteval-only type. */
> > > > +/* True if T is a consteval-only type as per [basic.types.general], or
> > > > + is a declaration with such a type, or a TREE_VEC thereof. */
> > > > bool
> > > > consteval_only_p (tree t)
> > > > @@ -8612,7 +8588,7 @@ consteval_only_p (tree t)
> > > > if (!TYPE_P (t))
> > > > t = TREE_TYPE (t);
> > > > - if (!t)
> > > > + if (!t || t == error_mark_node)
> > > > return false;
> > > > if (TREE_CODE (t) == TREE_VEC)
> > > > @@ -8634,9 +8610,89 @@ consteval_only_p (tree t)
> > > > which could be consteval-only, depending on T. */
> > > > t = complete_type (t);
> > > > - /* Classes with std::meta::info members are also consteval-only.
> > > > */
> > > > - hash_set<tree> visited;
> > > > - return !!cp_walk_tree (&t, consteval_only_type_r, &visited,
> > > > &visited);
> > > > + consteval_only_p_state state;
> > > > + return consteval_only_p_1 (t, state).is_true ();
> > > > +}
> > > > +
> > > > +/* A cache of the boolean result of consteval_only_p_1 for class types,
> > > > when
> > > > + the result is known. */
> > > > +
> > > > +static GTY((cache)) type_tree_cache_map *consteval_only_class_cache;
> > > > +
> > > > +/* Recursive workhorse of consteval_only_p. Returns true if T is
> > > > definitely
> > > > + consteval-only, false if it's definitely not, and unknown if we saw
> > > > an
> > > > + incomplete type and therefore don't know. */
> > > > +
> > > > +static tristate
> > > > +consteval_only_p_1 (tree t, consteval_only_p_state& state)
> > > > +{
> > > > + t = TYPE_MAIN_VARIANT (t);
> > > > +
> > > > + if (REFLECTION_TYPE_P (t))
> > > > + return true;
> > > > + else if (INDIRECT_TYPE_P (t))
> > > > + return consteval_only_p_1 (TREE_TYPE (t), state);
> > > > + else if (TREE_CODE (t) == ARRAY_TYPE)
> > > > + return consteval_only_p_1 (TREE_TYPE (t), state);
> > > > + else if (FUNC_OR_METHOD_TYPE_P (t))
> > > > + {
> > > > + tristate r = consteval_only_p_1 (TREE_TYPE (t), state);
> > > > + for (tree parm = TYPE_ARG_TYPES (t);
> > > > + parm != NULL_TREE && parm != void_list_node;
> > > > + parm = TREE_CHAIN (parm))
> > > > + {
> > > > + r = r || consteval_only_p_1 (TREE_VALUE (parm), state);
> > > > + if (r.is_true ())
> > > > + break;
> > > > + }
> > > > + return r;
> > > > + }
> > > > + else if (RECORD_OR_UNION_TYPE_P (t))
> > > > + {
> > > > + if (tree *slot = hash_map_safe_get (consteval_only_class_cache,
> > > > t))
> > > > + return *slot == boolean_true_node;
> > > > +
> > > > + if (state.class_seen.add (t))
> > > > + {
> > > > + auto begin = state.class_stack.begin ();
> > > > + auto end = state.class_stack.end ();
> > > > + auto it = std::find (begin, end, t);
> > > > + if (it != end && it != &state.class_stack.last ())
> > > > + state.seen_mutual_recursion = true;
> > > > +
> > > > + return false;
> > > > + }
> > > > + state.class_stack.safe_push (t);
> > > > +
> > > > + tristate r = COMPLETE_TYPE_P (t) ? false : tristate::unknown ();
> > > > + for (tree member = TYPE_FIELDS (t); member; member = DECL_CHAIN
> > > > (member))
> > > > + if (TREE_CODE (member) == FIELD_DECL)
> > > > + {
> > > > + r = r || consteval_only_p_1 (TREE_TYPE (member), state);
> > > > + if (r.is_true ())
> > > > + break;
> > > > + }
> > > > +
> > > > + if (!COMPLETE_TYPE_P (t))
> > > > + /* Until the type is laid out, we can't trust that its
> > > > TYPE_FIELDS
> > > > + won't change. */;
> > > > + else if (r.is_true ())
> > > > + hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
> > > > + t, boolean_true_node);
> > > > + else if (r.is_false ()
> > > > + && (state.class_stack.length () == 1
> > > > + || !state.seen_mutual_recursion))
> > >
> > > D'oh, this !state.seen_mutual_recursion exception for caching nested
> > > types is wrong.
> > >
> > > struct A {
> > > struct B { struct C *c; } b;
> > > struct C { B b; } c;
> > > };
> > >
> > > Here if we start walking from A, which is not mutually recursively, we
> > > first correctly deem B as unknown-consteval-only, but then go on to
> > > deem the nested C as not consteval only _and_ cache it (since there's
> > > no mutual recursion).
> > >
> > > So being optimistic about mutually recursive types (the
> > > 'if (class_seen.add (t)) return false' early exit) is at odds
> > > with caching the result for nested class types in general, unless
> > > we significantly complicate things which is probably not worth it.
> >
> > Oh, we can just remember if we ever took the early exit and only disable
> > false result caching for nested classes in that case. That should be
> > safe, I think (famous last words), and allow us to cache nested classes
> > in more cases. So for
> >
> > struct A {
> > struct B { int n; } b;
> > };
> >
> > the false result for B will be cached, but for
> >
> > struct A {
> > struct B { A* a; } b;
> > struct C { int n; } c;
> > };
> >
> > neither the false result for B or for C will be cached.
> >
> > Here is v4 that introduces an 'optimistic_p' flag to that effect. This
> > reduces the -freflection slowdown for the TU to 1.1x from 1.2x compared
> > to v3.
> >
> > -- >8 --
> >
> > PR c++/125179
> >
> > gcc/cp/ChangeLog:
> >
> > * reflect.cc: (consteval_only_type_r): Remove this cp_walk_tree
> > callback and replace with ...
> > (consteval_only_p_state, consteval_only_class_cache)
> > (consteval_only_p_1): ... this recursive memoized implementation.
> > (consteval_only_p): Define in terms of consteval_only_p_1.
> > ---
> > gcc/cp/reflect.cc | 136 ++++++++++++++++++++++++++++++++--------------
> > 1 file changed, 95 insertions(+), 41 deletions(-)
> >
> > diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
> > index 3b9f56ea5484..e9bd301fb84b 100644
> > --- a/gcc/cp/reflect.cc
> > +++ b/gcc/cp/reflect.cc
> > @@ -8561,47 +8561,23 @@ splice (tree refl)
> > return refl;
> > }
> > -/* A walker for consteval_only_p. It cannot be a lambda, because we
> > - have to call this recursively, sigh. */
> > +/* State maintained during consteval_only_p_1 recursion. */
> > -static tree
> > -consteval_only_type_r (tree *tp, int *walk_subtrees, void *data)
> > +struct consteval_only_p_state
> > {
> > - tree t = *tp;
> > - /* Types can contain themselves recursively, hence this. */
> > - auto visited = static_cast<hash_set<tree> *>(data);
> > -
> > - if (!TYPE_P (t))
> > - return NULL_TREE;
> > -
> > - if (REFLECTION_TYPE_P (t))
> > - return t;
> > -
> > - if (typedef_variant_p (t))
> > - /* Tell cp_walk_subtrees to look through typedefs. */
> > - *walk_subtrees = 2;
> > -
> > - if (RECORD_OR_UNION_TYPE_P (t))
> > - {
> > - /* Don't walk template arguments; A<info>::type isn't a
> > consteval-only
> > - type. */
> > - *walk_subtrees = 0;
> > - /* So we have to walk the fields manually. */
> > - for (tree member = TYPE_FIELDS (t);
> > - member; member = DECL_CHAIN (member))
> > - if (TREE_CODE (member) == FIELD_DECL)
> > - if (tree r = cp_walk_tree (&TREE_TYPE (member),
> > - consteval_only_type_r, visited, visited))
> > - return r;
> > - }
> > + /* The stack of class types we're recursively inside. */
> > + auto_vec<tree> class_stack;
> > + /* The set of class types we've seen. */
> > + hash_set<tree> class_seen;
> > + /* True if we've optimistically assumed an already-seen
> > + consteval-unknown class type is not consteval. */
> > + bool optimistic_p = false;
> > +};
> > - return NULL_TREE;
> > -}
> > +static tristate consteval_only_p_1 (tree, consteval_only_p_state&);
>
> How about making this a member function of consteval_only_p_state instead of a
> free function with a reference parameter?
Good idea, done.
>
> > -/* True if T is a consteval-only type as per [basic.types.general]:
> > - "A type is consteval-only if it is either std::meta::info or a type
> > - compounded from a consteval-only type", or something that has
> > - a consteval-only type. */
> > +/* True if T is a consteval-only type as per [basic.types.general], or
> > + is a declaration with such a type, or a TREE_VEC thereof. */
> > bool
> > consteval_only_p (tree t)
> > @@ -8612,7 +8588,7 @@ consteval_only_p (tree t)
> > if (!TYPE_P (t))
> > t = TREE_TYPE (t);
> > - if (!t)
> > + if (!t || t == error_mark_node)
> > return false;
> > if (TREE_CODE (t) == TREE_VEC)
> > @@ -8634,9 +8610,87 @@ consteval_only_p (tree t)
> > which could be consteval-only, depending on T. */
> > t = complete_type (t);
> > - /* Classes with std::meta::info members are also consteval-only. */
> > - hash_set<tree> visited;
> > - return !!cp_walk_tree (&t, consteval_only_type_r, &visited, &visited);
> > + consteval_only_p_state state;
> > + return consteval_only_p_1 (t, state).is_true ();
> > +}
> > +
> > +/* A cache of the boolean result of consteval_only_p_1 for class types,
> > when
> > + the result is known. */
> > +
> > +static GTY((cache)) type_tree_cache_map *consteval_only_class_cache;
>
> Since you aren't actually returning the tree value, I guess you're using
> type_tree_cache_map mostly because it's an existing type and there isn't one
> for type to bool?
Yeah, though in an earlier version of the patch the mapped values were
tri-state so using bool wasn't an option. I'm not keen on creating
a new map type just for this case, at least as part of this patch, the
memory savings should be negligible due to padding and type_tree_cache_map
is heavily used and well tested.
>
> Also, from gty.texi:
>
> > Note that caches should generally use @code{deletable} instead;
> > @code{cache} is only preferable if the value is impractical to
> > recompute from the key when needed.
>
> This seems like a ((deletable)) case to me, so we could just use
> hash_map<tree,bool>.
Using ((deletable)) results in a 10x -freflection slowdown for the
testcase from the PR, vs 1.1x with ((cache)), I suspect because it gets
cleared every time we complete a class.
>
> > +
> > +/* Recursive workhorse of consteval_only_p. Returns true if T is
> > definitely
> > + consteval-only, false if it's definitely not, and unknown if we saw an
> > + incomplete type and therefore don't know. */
> > +
> > +static tristate
> > +consteval_only_p_1 (tree t, consteval_only_p_state& state)
> > +{
> > + t = TYPE_MAIN_VARIANT (t);
> > +
> > + if (REFLECTION_TYPE_P (t))
> > + return true;
> > + else if (INDIRECT_TYPE_P (t))
> > + return consteval_only_p_1 (TREE_TYPE (t), state);
> > + else if (TREE_CODE (t) == ARRAY_TYPE)
> > + return consteval_only_p_1 (TREE_TYPE (t), state);
> > + else if (FUNC_OR_METHOD_TYPE_P (t))
> > + {
> > + tristate r = consteval_only_p_1 (TREE_TYPE (t), state);
> > + for (tree parm = TYPE_ARG_TYPES (t);
> > + parm != NULL_TREE && parm != void_list_node;
> > + parm = TREE_CHAIN (parm))
> > + {
> > + r = r || consteval_only_p_1 (TREE_VALUE (parm), state);
> > + if (r.is_true ())
>
> Let's move the is_true check before the || so that we don't look at the first
> parm when the return type is consteval-only.
Fixed.
Here's v5 which uses a member function and fixes the above. The cache
is still marked as ((cache)) instead of ((deletable)).
-- >8 --
PR c++/125179
gcc/cp/ChangeLog:
* reflect.cc: (consteval_only_type_r): Remove this cp_walk_tree
callback and replace with ...
(consteval_only_walker): ... this recursive memoized
implementation.
(consteval_only_p): Define in terms of consteval_only_walker.
---
gcc/cp/reflect.cc | 134 ++++++++++++++++++++++++++++++++--------------
1 file changed, 93 insertions(+), 41 deletions(-)
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index 3b9f56ea5484..6e7000f7de81 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -8561,47 +8561,26 @@ splice (tree refl)
return refl;
}
-/* A walker for consteval_only_p. It cannot be a lambda, because we
- have to call this recursively, sigh. */
+/* A cache of the known boolean result of consteval_only_p_walker::walk for
+ class types. */
-static tree
-consteval_only_type_r (tree *tp, int *walk_subtrees, void *data)
-{
- tree t = *tp;
- /* Types can contain themselves recursively, hence this. */
- auto visited = static_cast<hash_set<tree> *>(data);
-
- if (!TYPE_P (t))
- return NULL_TREE;
+static GTY((cache)) type_tree_cache_map *consteval_only_class_cache;
- if (REFLECTION_TYPE_P (t))
- return t;
-
- if (typedef_variant_p (t))
- /* Tell cp_walk_subtrees to look through typedefs. */
- *walk_subtrees = 2;
-
- if (RECORD_OR_UNION_TYPE_P (t))
- {
- /* Don't walk template arguments; A<info>::type isn't a consteval-only
- type. */
- *walk_subtrees = 0;
- /* So we have to walk the fields manually. */
- for (tree member = TYPE_FIELDS (t);
- member; member = DECL_CHAIN (member))
- if (TREE_CODE (member) == FIELD_DECL)
- if (tree r = cp_walk_tree (&TREE_TYPE (member),
- consteval_only_type_r, visited, visited))
- return r;
- }
+struct consteval_only_p_walker
+{
+ /* The stack of class types we're recursively inside. */
+ auto_vec<tree> class_stack;
+ /* The set of class types we've seen. */
+ hash_set<tree> class_seen;
+ /* True if we've optimistically assumed an already-seen
+ consteval-unknown class type is not consteval. */
+ bool optimistic_p = false;
- return NULL_TREE;
-}
+ tristate walk (tree);
+};
-/* True if T is a consteval-only type as per [basic.types.general]:
- "A type is consteval-only if it is either std::meta::info or a type
- compounded from a consteval-only type", or something that has
- a consteval-only type. */
+/* True if T is a consteval-only type as per [basic.types.general], or
+ is a declaration with such a type, or a TREE_VEC thereof. */
bool
consteval_only_p (tree t)
@@ -8612,7 +8591,7 @@ consteval_only_p (tree t)
if (!TYPE_P (t))
t = TREE_TYPE (t);
- if (!t)
+ if (!t || t == error_mark_node)
return false;
if (TREE_CODE (t) == TREE_VEC)
@@ -8634,9 +8613,82 @@ consteval_only_p (tree t)
which could be consteval-only, depending on T. */
t = complete_type (t);
- /* Classes with std::meta::info members are also consteval-only. */
- hash_set<tree> visited;
- return !!cp_walk_tree (&t, consteval_only_type_r, &visited, &visited);
+ consteval_only_p_walker walker;
+ return walker.walk (t).is_true ();
+}
+
+/* Recursive workhorse of consteval_only_p. Returns true if T is definitely
+ consteval-only, false if it's definitely not, and unknown if we saw an
+ incomplete type and therefore don't know. */
+
+tristate
+consteval_only_p_walker::walk (tree t)
+{
+ t = TYPE_MAIN_VARIANT (t);
+
+ if (REFLECTION_TYPE_P (t))
+ return true;
+ else if (INDIRECT_TYPE_P (t))
+ return walk (TREE_TYPE (t));
+ else if (TREE_CODE (t) == ARRAY_TYPE)
+ return walk (TREE_TYPE (t));
+ else if (FUNC_OR_METHOD_TYPE_P (t))
+ {
+ tristate r = walk (TREE_TYPE (t));
+ for (tree parm = TYPE_ARG_TYPES (t);
+ parm != NULL_TREE && parm != void_list_node;
+ parm = TREE_CHAIN (parm))
+ {
+ if (r.is_true ())
+ break;
+ r = r || walk (TREE_VALUE (parm));
+ }
+ return r;
+ }
+ else if (RECORD_OR_UNION_TYPE_P (t))
+ {
+ if (tree *slot = hash_map_safe_get (consteval_only_class_cache, t))
+ return *slot == boolean_true_node;
+
+ if (class_seen.add (t))
+ {
+ /* Optimistically assume this already seen consteval-unknown class is
+ not consteval only, for sake of mutually recursive classes. */
+ optimistic_p = true;
+ return false;
+ }
+ class_stack.safe_push (t);
+
+ tristate r = COMPLETE_TYPE_P (t) ? false : tristate::unknown ();
+ for (tree member = TYPE_FIELDS (t); member; member = DECL_CHAIN (member))
+ if (TREE_CODE (member) == FIELD_DECL)
+ {
+ r = r || walk (TREE_TYPE (member));
+ if (r.is_true ())
+ break;
+ }
+
+ if (!COMPLETE_TYPE_P (t))
+ /* Until the type is laid out, we can't trust that its TYPE_FIELDS
+ won't change. */;
+ else if (r.is_true ())
+ hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
+ t, boolean_true_node);
+ else if (r.is_false ()
+ /* The optimistic assumption above is at odds with caching
+ 'false' results for a nested class type. */
+ && (class_stack.length () == 1 || !optimistic_p))
+ hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
+ t, boolean_false_node);
+
+ class_stack.pop ();
+ return r;
+ }
+ else if (TYPE_PTRMEM_P (t))
+ return (walk (TYPE_PTRMEM_CLASS_TYPE (t))
+ || walk (TYPE_PTRMEM_POINTED_TO_TYPE (t)));
+ else
+ return false;
}
/* A walker for check_out_of_consteval_use_r. It cannot be a lambda, because
--
2.54.0.rc1.54.g60f07c4f5c
>
> > + break;
> > + }
> > + return r;
> > + }
> > + else if (RECORD_OR_UNION_TYPE_P (t))
> > + {
> > + if (tree *slot = hash_map_safe_get (consteval_only_class_cache, t))
> > + return *slot == boolean_true_node;
> > +
> > + if (state.class_seen.add (t))
> > + {
> > + /* Optimistically assume this already seen consteval-unknown class
> > is
> > + not consteval only, for sake of mutually recursive classes. */
> > + state.optimistic_p = true;
> > + return false;
> > + }
> > + state.class_stack.safe_push (t);
> > +
> > + tristate r = COMPLETE_TYPE_P (t) ? false : tristate::unknown ();
> > + for (tree member = TYPE_FIELDS (t); member; member = DECL_CHAIN
> > (member))
> > + if (TREE_CODE (member) == FIELD_DECL)
> > + {
> > + r = r || consteval_only_p_1 (TREE_TYPE (member), state);
> > + if (r.is_true ())
> > + break;
> > + }
> > +
> > + if (!COMPLETE_TYPE_P (t))
> > + /* Until the type is laid out, we can't trust that its TYPE_FIELDS
> > + won't change. */;
> > + else if (r.is_true ())
> > + hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
> > + t, boolean_true_node);
> > + else if (r.is_false ()
> > + /* The optimistic assumption above is at odds with caching
> > + 'false' results for a nested class type. */
> > + && (state.class_stack.length () == 1 || !state.optimistic_p))
> > + hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
> > + t, boolean_false_node);
> > +
> > + state.class_stack.pop ();
> > + return r;
> > + }
> > + else if (TYPE_PTRMEM_P (t))
> > + return (consteval_only_p_1 (TYPE_PTRMEM_CLASS_TYPE (t), state)
> > + || consteval_only_p_1 (TYPE_PTRMEM_POINTED_TO_TYPE (t), state));
> > + else
> > + return false;
> > }
> > /* A walker for check_out_of_consteval_use_r. It cannot be a lambda,
> > because
>
>