Dang.
That would seem to me a really sharp corner with reflection, then. It
was... not the easiest to figure out where my actual code was going
wrong.
And
I would say that there's a problem even when you do actually want the
variable as constexpr, since, as mentioned, even in a template context
GCC will eagerly evaluate the expression, before the template is ever
instantiated, if there are no dependent names involved. And that still
seems at least somewhat unideal.
Maybe
there could be some sort of marker placed on "stateful" consteval
functions such as is_complete_type, has_identifier, members_of, etc.
that then the compiler could look at and delay evaluation for? I'm not
sure that that would work though, since it would need to propagate
through to user-defined functions too I think.
To
further clarify how I came upon this, I figured out a way with
current-day GCC to track whether friend-injection has taken place to use
with my 'consteval_state' machinery (which was shown off
here), which is now exposing that code to more GCC behavior.
With
that code it's obviously clear that stateful things are happening, but I
would worry that these stateful functions are still going to be used in
more typical code, and that this issue could very much cause
unintuitive behavior for people.
But yeah, I'm not at all sure what the best way to fix it would be.
Thanks
On Sunday, April 5th, 2026 at 12:32 PM, Barry Revzin <barry.revzin@gmail.com> wrote:
Hello,
Yesterday I ran into really annoying behavior with GCC with this sort of code:
consteval bool is_defined() {
const auto is_complete = is_complete_type(^^to_define);
return is_complete;
}
consteval {
define_aggregate(^^to_define, {});
}
static_assert(is_complete_type(^^to_define));
static_assert(is_defined());
With the experimental Clang reflection branch, both static asserts succeed, but with GCC, then the final one does not: https://godbolt.org/z/TdsxWs3PW
It does, however, succeed if you leave the 'const' off of the 'is_complete' variable.
GCC seems to me to be taking advantage of the "potentially usable in constant expressions" (https://eel.is/c++draft/expr.const#def:potentially_usable_in_constant_expressions) feature and lifting the `is_complete` variable to be effectively 'constexpr', and evaluating 'is_complete_type(^^to_define)' right away. This will happen even in a template because GCC can see that the expression doesn't rely on any dependent names, and so will still eagerly evaluate it.
But in short, I'm wondering if this is at all standard behavior? It really really feels like it should be a bug, that something so innocuous as a 'const' specifier could so starkly change the behavior of your program. The problem is that I'm not sure that it is actually a bug in GCC.
Any clarification towards that would be greatly appreciated.
Thank you
Far from this being a gcc bug, I think the standard mandates gcc's behavior.
This is the legacy hack that allows this to work, before we had constexpr:
const int x = 42;
C<x> c;
And because the code doesn't express intentionality, we have this other legacy hack called trial evaluation. Now we have constexpr and constinit, but before we needed that same behavior, so the rules are that we try to constant-evaluate and if that works, great, and if doesn't, we just pretend we never tried.
The const rule is for integral and enumeration types, and includes bool. So when you write
const bool v = E;
We try to constant-evaluate E. If that succeeds, then it's as if you wrote constexpr instead of const.
Which of course isn't what you actually want here. I suppose we could exclude variables declared within consteval functions, since surely if you're writing a consteval function you can declare your variable constexpr if that's what you actually want. But that doesn't seem great to me either.
Barry