Why isn't the keyword false an integer constant expression in gcc C23?

2.5k views Asked by At

Latest gcc 13.x (trunk) gives a compiler error (gcc -std=c23) for this code:

int* p = false;

error: incompatible types when initializing type 'int *' using type '_Bool'

How can this be correct?


C23 6.2.5 §8-9 (Types - the definition of integer types):

The type bool and the unsigned integer types that correspond to the standard signed integer types are the standard unsigned integer types.

The standard signed integer types and standard unsigned integer types are collectively called the standard integer types;

C23 6.6 §8 (Constant expressions - the definition of an integer constant expression):

An integer constant expression shall have integer type...

C23 6.3.2.3 (Pointers - the definition of null pointer constant)

An integer constant expression with the value 0, such an expression cast to type void *, or the predefined constant nullptr is called a null pointer constant

C23 6.5.16.1 (Simple assignment):

Constraints
/--/

  • the left operand is an atomic, qualified, or unqualified pointer, and the right operand is a null pointer constant

Conclusion:

  • int* p = false; is a valid form of assignment,
  • because false is a null pointer constant,
  • since it is an integer constant expression,
  • since it has type bool, which is one of the standard integer types.

Did I misunderstand anything in the standard or is this a gcc bug? clang gives a warning but still produces an executable. I don't really see how any of the above has changed in C23 either.

Including stdbool.h and compiling under gcc -std=c17 -pedantic-errors makes it compile cleanly as expected.

2

There are 2 answers

2
John Bollinger On BEST ANSWER

In addition to the paragraphs quoted in the question, there is paragraph 6.4.4.5/3:

The keywords false and true are constants of type bool with a value of 0 for false and 1 for true.

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.

8
dbush On

As it turns out, the way false is defined in gcc's c2x mode, it has a different type. It's still an integer constant expression, but starting with C23 it has type _Bool instead of type int.

Passing the given line through the preprocessor in c2x mode gives the following:

int* p =  ((_Bool)+0u);

The abstract of C23 mentioned that the following was applied:

N2393 _Bool Definitions For true and false.

And this document states the following:

  1. REFERENCE IMPLEMENTATION To add minimal support for the proposed changes, an implementation would have to add definitions that are equivalent to the following lines to their startup code:

    # define false (( bool ) +0)
    # define true (( bool ) +1)
    

So the definition of true and false changes in C23.

The above causes an error in gcc going back to at least 4.9.

In contrast, the preprocessor outputs the following in c11 mode:

int* p = 0;

Which is an integer constant expression with the value 0.

Interestingly, the following compiles with a warning on all versions of gcc:

int x = 0;
int* p = x;

Output:

<source>:7:14: warning: initialization of 'int *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
    7 |     int* p = x;
      |              ^

While the following produces the same error as above:

bool x = 0;
int* p = x;

Granted, the above is not a case of a constant expression, but it shows how gcc handles conversions from int to a pointer type as opposed to _Bool.

So given the rules on integer to pointer conversion, I would say this is a bug in gcc.