Hi Jakub
Sorry for the mess, this is indeed my first rodeo.
I thought when you are outside a directive, treating __has_include as
some rogue identifier was somewhat sensible, such as:
crash2.cpp:1:11: error: ‘__has_include’ used outside of preprocessing
directive
1 | int i = __has_include(<iostream
| ^~~~~
crash2.cpp:1:25: error: expected primary-expression before ‘<’ token
1 | int i = __has_include(<iostream
| ^
crash2.cpp:1:26: error: ‘iostream’ was not declared in this scope
1 | int i = __has_include(<iostream
| ^~~~
if [...] glue_header_name is dangerous (why, due to get_token_no_padding
rather than
_cpp_get_token_no_padding?), then at least skip until it sees the closing )
or and of line or something similar?
Yea, consuming the EOF seems way too much, as well as trying to concatenate
everything for multiple lines. I can test later if just changing to
__cpp_get_token_no_padding fixes the crash, but EOL or closing parenthesis
would be nice.
Let me know what you think, I would find it nice to fix the issue I just
caused.
Best regards,
Léo
On 2026-09-22 04:20, Jakub Jelinek wrote:
On Mon, Sep 21, 2026 at 07:58:12PM -0700, Andrea Pinski wrote:
After r17-4537-g059231fb8a42ea, two error messages instead of
just one is emitted. So let's look for both of them.
Pushed as obvious after testing to make sure the testcase passes
now for both C and C++.
gcc/testsuite/ChangeLog:
* c-c++-common/gomp/has-include-1.c: Add second dg-error.
It is worse than that.
On i686-linux (not on x86_64-linux) I'm also getting
+FAIL: c-c++-common/cpp/has-include-1.c -Wc++-compat (test for excess
errors)
+FAIL: c-c++-common/cpp/has-include-next-1.c -Wc++-compat (test for excess
errors)
regressions in C and
+FAIL: c-c++-common/cpp/has-include-1.c -std=c++11 (test for excess errors)
+FAIL: c-c++-common/cpp/has-include-1.c -std=c++14 (test for excess errors)
+FAIL: c-c++-common/cpp/has-include-1.c -std=c++17 (test for excess errors)
+FAIL: c-c++-common/cpp/has-include-1.c -std=c++20 (test for excess errors)
+FAIL: c-c++-common/cpp/has-include-1.c -std=c++23 (test for excess errors)
+FAIL: c-c++-common/cpp/has-include-1.c -std=c++26 (test for excess errors)
+FAIL: c-c++-common/cpp/has-include-1.c -std=c++29 (test for excess errors)
+FAIL: c-c++-common/cpp/has-include-1.c -std=c++98 (test for excess errors)
+FAIL: c-c++-common/cpp/has-include-next-1.c -std=c++11 (test for excess
errors)
+FAIL: c-c++-common/cpp/has-include-next-1.c -std=c++14 (test for excess
errors)
+FAIL: c-c++-common/cpp/has-include-next-1.c -std=c++17 (test for excess
errors)
+FAIL: c-c++-common/cpp/has-include-next-1.c -std=c++20 (test for excess
errors)
+FAIL: c-c++-common/cpp/has-include-next-1.c -std=c++23 (test for excess
errors)
+FAIL: c-c++-common/cpp/has-include-next-1.c -std=c++26 (test for excess
errors)
+FAIL: c-c++-common/cpp/has-include-next-1.c -std=c++29 (test for excess
errors)
+FAIL: c-c++-common/cpp/has-include-next-1.c -std=c++98 (test for excess
errors)
in C++. Excess errors:
/home/jakub/src/gcc/gcc/testsuite/c-c++-common/cpp/has-include-1.c:50:15:
error: missing ')' after '__has_include' operand
/home/jakub/src/gcc/gcc/testsuite/c-c++-common/cpp/has-include-1.c:51:1:
error: missing ')' after '__has_include' operand
/home/jakub/src/gcc/gcc/testsuite/c-c++-common/cpp/has-include-1.c:53:1:
error: missing ')' after '__has_include' operand
/home/jakub/src/gcc/gcc/testsuite/c-c++-common/cpp/has-include-1.c:57:1:
error: missing ')' after '__has_include' operand
and
/home/jakub/src/gcc/gcc/testsuite/c-c++-common/cpp/has-include-next-1.c:50:20:
error: missing ')' after '__has_include_next' operand
/home/jakub/src/gcc/gcc/testsuite/c-c++-common/cpp/has-include-next-1.c:51:1:
error: missing ')' after '__has_include_next' operand
/home/jakub/src/gcc/gcc/testsuite/c-c++-common/cpp/has-include-next-1.c:53:1:
error: missing ')' after '__has_include_next' operand
/home/jakub/src/gcc/gcc/testsuite/c-c++-common/cpp/has-include-next-1.c:57:1:
error: missing ')' after '__has_include_next' operand
IMHO we don't want 2 errors when there is just one.
The reason for that is introduced UB in libcpp, the callers call
builtin_has_include_1 with uninitialized paren and bracket vars
and expect the function to unconditionally initialize those, which it used
to do but no longer does.
I think the patch is doing a wrong thing, it shouldn't skip parsing what
comes after __has_include/__has_include_next/__has_embed when outside of a
directive, it should simply make sure to return NULL and if glue_header_name
in the < case is bad, do something smarter.
If something goes wrong during parsing e.g. in the FEs, we try to skip
to next closing ) or ; etc.
So, we really should check if the __has_* identifier is followed by ( or
not, and whether that is followed by string literal or header name,
in that case it should parse it as before, if it is < and glue_header_name
is dangerous (why, due to get_token_no_padding rather than
_cpp_get_token_no_padding?), then at least skip until it sees the closing )
or and of line or something similar?
Jakub