https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120600

            Bug ID: 120600
           Summary: Inconsistent header name parsing in __has_include
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: preprocessor
          Assignee: unassigned at gcc dot gnu.org
          Reporter: luigighiron at gmail dot com
  Target Milestone: ---

The following code demonstrates how __has_include inconsistently parses header
names:

#define X stdio.h
#define Y (
#define Z __has_include(
int a=
#if __has_include Y<X>)
1
#else
0
#endif
;
int b=
#if Z<X>)
1
#else
0
#endif
;
#include<stdio.h>
int main(){
    printf("%i %i\n",a,b);
}

Assuming a file named X doesn't exist, 1 0 is printed by GCC. Furthermore, if
the opening parenthesis in the definition of Z is replaced by Y then 1 1 is
printed. That is, GCC only parses header names if the __has_include and opening
parenthesis are not separated. Interestingly, 0 1 is printed when using Clang
instead. MSVC prints 0 0 (with and without /Zc:preprocessor), and from my
testing will always attempt to parse a header name in __has_include. Parsing
header names inconsistently here seems like a bug, I don't see any reason for
the behavior to be this way.

Also, there is a divergence between compilers about uses of #include such as:

#define X stdio.h
#define NONE
#include NONE<X>

GCC has this include stdio.h, while Clang and MSVC (with and without
/Zc:preprocessor) have this include X. Though all compilers accept this as
including X:

#define X stdio.h
#define NONE
#include<X>NONE

The divergence of parsing header names when an empty macro is placed before the
header name is also reflected in __has_include. I think the sentence "The
identifiers __has_include, __has_embed, and __has_c_attribute shall not appear
in any context not mentioned in this subclause." from C23 could be interpreted
as making these cases undefined behavior, though macro expansion is described
as happening first so that is not how I interpreted it. Moreover, it would be
strange to support these uses at all if it is undefined.

Reply via email to