In addition to the paragraphs quoted in the question, there is paragraph 6.4.4.5/3:
The keywords
falseandtrueare constants of typeboolwith a value of
0 forfalseand 1 fortrue.
and 6.6/7:
An identifier that is:
[…]
— a predefined constant;
[…]
is a named constant
That makes false a named constant, which is relevant because a fuller quotation of 6.6/8 is
An integer constant expression shall have integer type and shall only have operands that are
integer constants, named and compound literal constants of integer type […]
(emphasis added).
Regardless, then, of GCC implementation details surrounding the constant false, when taken as an expression, false meets C23’s criteria for an integer constant expression. Its value is specified to be 0. It is therefore a valid null pointer constant.
GCC’s behavior here is contrary to the language spec.
I don’t really see how any of the above has changed in C23 either.
None of the points you were referring to changed in C23, but what did change is the type of false and true. In C17, these had type int. In C23, they have type bool (a.k.a. _Bool). As you observe, however, bool is an integer type, so GCC not allowing that as the type of a null pointer constant is non-conforming.
I note that GCC 8.5 does not accept _Bool as a valid type for a null pointer constant, either. Given this source:
void *test = (_Bool)0;
int main(void) {
}
, it fails with the same error you observed:
npctest.c:1:14: error: incompatible types when initializing type ‘void *’ using type ‘_Bool’
Thus, your observation appears to be a manifestation of a larger, relatively longstanding issue that is not specific to C23.
This issue is mentioned in the GCC bug tracker in comments on a related issue: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112556.