Resolved: c++ constexpr typed as nested class


This works: (A)
This fails to compile: (B)
This also fails to compile, but gives a good reason: (C)
C failing makes sense and has a good error message. B feels like it should work, Foo and all it’s contents should be complete and defined at that point in the file. Basically my question is: do I report a clang bug that B should work, or a feature request for a better error message? If Foo is truly not complete by virtue of being a member of an incomplete type, then I should think the error message should be similar to that of C.
I just upgraded clang to the bleeding edge (16.0.0-++20221021052626+7dd2f4bc009d-1~exp1~20221021172738.418) and got the same result.

Best Answer:

The problem with (B) is distinct from the one with (C). In (B) the completeness of Foo is not in question. Foo is complete as soon as the closing } of its definition is reached and since the member declaration of tru is placed after that, Foo is complete for its purpose.
There is also no problem in (B) with Bar being incomplete at the declaration of tru. While that’s true, incompleteness does not prevent looking up members which were declared prior, like the nested class Foo.
The problem is, as the error message is pointing out, whether or not the constructor Foo::Foo(bool) is defined at the point of the declaration of tru. This is an important question, because tru is initialized in that declaration using the constructor in question and it is marked constexpr, requiring that the initialization be a constant expression. Calling a (constexpr) function in a constant expression is only allowed if the function is defined (not only declared) prior to the expression.
Consider for example
You will get the same or a similar error message here. In this case it is more obvious what the issue is. When static constexpr Foo tru { true }; is reached and the compiler tries to evaluate the (compile-time constant) value of Foo, it hasn’t seen the definition of the constructor yet, so it can’t know how to determine the value of tru.
Now in your example (B) it seems that Bar::Foo::Foo(bool) is defined before it is used in the constant expression for tru and I think if one follows the current standard by exact wording, then this is true. However, there is a complication which changes this in practice and in the probable intent of the standard:
The body of a function defined inside a class is special in that it is a so-called complete-class context. In such a context it is possible for name lookup to find not only preceding declarations as is normally the case in C++, but also declarations for all members of the class (and enclosing classes), irregardless of whether they are declared only later.
So for example the following is allowed:
Although X is not declared yet when Foo::Foo(bool) is defined and uses X, the compiler has to accept it and figure out that X is the static member declared at the end.
In order to achieve this lookup behavior, the compiler practically must rewrite the code to
Now “normal” lookup rules can find X as expected.
But if we apply this rewriting to your example (B) we get my first example in this answer and as we determined it cannot work with the constant expression evaluation. So in practice you can’t use a member function of the same class (including nested or enclosing classes) in a constant expression evaluation for a static data member inside the class definition itself.
That the current standard wording doesn’t describe this behavior properly is an issue with the standard, not the compiler. My impression is that Clang is implementing it as intended. While it may sometimes be possible to consider the constructor defined where it is lexically placed for the purpose of constant expression evaluation if e.g. all names used in its definition can already be found at that point, in general it is impossible, because you could create infinite recursive type dependencies this way.

If you have better answer, please add a comment about this, thank you!