On Tue, 5 May 2026, Jason Merrill wrote:
> On 5/5/26 4:34 PM, Patrick Palka wrote:
> > On Tue, 5 May 2026, Jason Merrill wrote:
> >
> > > On 5/5/26 3:53 PM, Patrick Palka wrote:
> > > > On Tue, 5 May 2026, Jason Merrill wrote:
> > > > > On 5/5/26 1:20 PM, Patrick Palka wrote:
> > > > > > +/* 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. */;
> > > > >
> > > > > We might move the COMPLETE_TYPE check...
> > > > >
> > > > > > + else if (r.is_true ())
> > > > > > + hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
> > > > > > + t, boolean_true_node);
> > > > > > + else if (r.is_false ()
> > > > >
> > > > > ...into another exception for the is_false case, since I don't think
> > > > > the
> > > > > TYPE_FIELDS can change in a way that would invalidate is_true().
> > > >
> > > > Sadly, it can! prune_lambda_captures can remove consteval-only
> > > > TYPE_FIELDS (corresponding to folded-away captures) before the closure
> > > > type has been laid out, so if we'd cache the result now we'd get the
> > > > wrong answer after pruning.
> > > >
> > > > Without this second COMPLETE_TYPE_P check we'd get a bogus error in
> > > > reflect/reflect_constant_array6.C due to treating the lambda as
> > > > consteval even after pruning.
> > >
> > > Ah, lambdas, always keeping us on our toes. But then we could skip
> > > walking
> > > the fields at all if we're going to return unknown no matter what we see.
> >
> > In that lambda case, where TYPE_FIELDS is set but TYPE_COMPLETE_P is
> > false, we won't always return unknown, we could return true if one of
> > its TYPE_FIELDS is consteval-only.
>
> Hmm, we currently return true but don't cache it? So we could be returning
> consteval-only about a type that later turns out not to be. That doesn't seem
> better to me than just returning unknown.
Always returning unknown for !COMPLETE_TYPE_P class types seems to break <meta>:
/scratchpad/gcc-master-build/x86_64-pc-linux-gnu/libstdc++-v3/include/meta:117:7:
error: ‘consteval’ function ‘virtual consteval const char*
std::meta::exception::what() const’ overriding non-‘consteval’ function
117 | what() const noexcept override
| ^~~~
In file included from
/home/patrick/code/gcc-master/libstdc++-v3/libsupc++/new:43,
from
/scratchpad/gcc-master-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/stl_construct.h:59,
from
/scratchpad/gcc-master-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:59,
from
/scratchpad/gcc-master-build/x86_64-pc-linux-gnu/libstdc++-v3/include/string:45,
from gcc/testsuite/g++.dg/reflect/reflect_constant_array6.C:5:
/scratchpad/gcc-master-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/exception.h:83:5:
note: overridden function is ‘virtual constexpr const char*
std::exception::what() const’
83 | what() const _GLIBCXX_TXN_SAFE_DYN noexcept { return
"std::exception"; }
| ^~~~
Reduced:
struct exception {
virtual void foo();
};
struct meta_exception : exception {
decltype(^^int) *p;
consteval virtual void foo() { }
};
because check_for_override is called before the class is fully laid out
and COMPLETE_TYPE_P is not yet set, but we still need consteval_only_p
to return true at this point. That doesn't seem ideal :/