https://gcc.gnu.org/g:9e5fda8ec5716b6fa2b57bdfd56e2b91116a93ff
commit r17-5011-g9e5fda8ec5716b6fa2b57bdfd56e2b91116a93ff Author: Jakub Jelinek <[email protected]> Date: Fri Oct 9 10:06:25 2026 +0200 libcpp: Fix up parsing __has_include outside directive [PR121508] The r17-4537 change broke several tests especially by introducing non-determinism into the preprocessor. The builtin_has_include_1 function is expected to set *paren and *bracket flags, and early exit from the function without setting those flags means the callers then use uninitialized variables. Also, the early exit means that when (incorrectly, sure) one uses __has_include/__has_include_next or __has_embed outside of a preprocessing directive but with correct syntax, it is diagnosed as both using it outside of a preprocessing directive (correct) but also as a random sequence of unexpected tokens. This patch reverts that change. I'm guilty of introducing _cpp_get_token_no_padding 6 years ago instead of fixing up get_token_no_padding back then, I wasn't sure if the eating of CPP_EOF wasn't intentional in some cases, but clearly it is never intentional and always a bug. After all, get__Pragma_string has been fighting with it already by using paren = get_token_no_padding (pfile); if (paren->type == CPP_EOF) _cpp_backup_tokens (pfile, 1); glue_header_name and parse_include weren't doing this, but in the usual case when pfile->state.in_directive and not lexing a raw string, get_fresh_line_impl returns false and _cpp_lex_direct will just keep returning CPP_EOF at the end of the line many times until we clear the pfile->state.in_directive flag. So, I think we should just drop the dangerous get_token_no_padding function and simply never eat a CPP_EOF. 2026-10-09 Jakub Jelinek <[email protected]> PR preprocessor/121508 PR preprocessor/123339 * macro.cc (builtin_has_include_1): Revert 2026-09-21 changes. * directives.cc (get_token_no_padding): Remove. (glue_header_name): Use _cpp_get_token_no_padding instead of get_token_no_padding. (parse_include): Likewise. (get__Pragma_string): Likewise. Don't _cpp_backup_tokens on CPP_EOF. * c-c++-common/cpp/pr121508-1.c: New test. * c-c++-common/cpp/pr121508-2.c: New test. * c-c++-common/cpp/pr121508-3.c: New test. * c-c++-common/cpp/pr121508-4.c: New test. * c-c++-common/cpp/pr121508-5.c: New test. * c-c++-common/cpp/pr123339.c: New test. * c-c++-common/gomp/has-include-1.c: Remove spurious extra dg-error. Reviewed-by: Andrea Pinski <[email protected]> Diff: --- gcc/testsuite/c-c++-common/cpp/pr121508-1.c | 8 +++++++ gcc/testsuite/c-c++-common/cpp/pr121508-2.c | 8 +++++++ gcc/testsuite/c-c++-common/cpp/pr121508-3.c | 8 +++++++ gcc/testsuite/c-c++-common/cpp/pr121508-4.c | 8 +++++++ gcc/testsuite/c-c++-common/cpp/pr121508-5.c | 8 +++++++ gcc/testsuite/c-c++-common/cpp/pr123339.c | 9 ++++++++ gcc/testsuite/c-c++-common/gomp/has-include-1.c | 1 - libcpp/directives.cc | 29 +++++-------------------- libcpp/macro.cc | 7 ++---- 9 files changed, 56 insertions(+), 30 deletions(-) diff --git a/gcc/testsuite/c-c++-common/cpp/pr121508-1.c b/gcc/testsuite/c-c++-common/cpp/pr121508-1.c new file mode 100644 index 000000000000..d02a9dd271e2 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/pr121508-1.c @@ -0,0 +1,8 @@ +/* PR preprocessor/121508 */ +/* { dg-do compile } */ + +/* { dg-error "'__has_include' used outside of preprocessing directive" "" { target *-*-* } .+4 } */ +/* { dg-error "missing terminating '>' character" "" { target *-*-* } .+3 } */ +/* { dg-error "missing '\\\)' after '__has_include' operand" "" { target *-*-* } .+2 } */ +/* { dg-error "expected ',' or ';' at end of input" "" { target *-*-* } .+1 } */ +int i = __has_include(<__int16_t diff --git a/gcc/testsuite/c-c++-common/cpp/pr121508-2.c b/gcc/testsuite/c-c++-common/cpp/pr121508-2.c new file mode 100644 index 000000000000..884924126bde --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/pr121508-2.c @@ -0,0 +1,8 @@ +/* PR preprocessor/121508 */ +/* { dg-do compile } */ + +/* { dg-error "'__has_include' used outside of preprocessing directive" "" { target *-*-* } .+4 } */ +/* { dg-error "missing terminating '>' character" "" { target *-*-* } .+3 } */ +/* { dg-error "missing '\\\)' after '__has_include' operand" "" { target *-*-* } .+2 } */ +/* { dg-error "expected ',' or ';' at end of input" "" { target *-*-* } .+1 } */ +int i = __has_include(<__int16_t) diff --git a/gcc/testsuite/c-c++-common/cpp/pr121508-3.c b/gcc/testsuite/c-c++-common/cpp/pr121508-3.c new file mode 100644 index 000000000000..02cbca9b03a0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/pr121508-3.c @@ -0,0 +1,8 @@ +/* PR preprocessor/121508 */ +/* { dg-do compile } */ + +/* { dg-error "'__has_embed' used outside of preprocessing directive" "" { target *-*-* } .+4 } */ +/* { dg-error "missing terminating '>' character" "" { target *-*-* } .+3 } */ +/* { dg-error "expected '\\\)'" "" { target *-*-* } .+2 } */ +/* { dg-error "expected ',' or ';' at end of input" "" { target *-*-* } .+1 } */ +int i = __has_embed(<__int16_t diff --git a/gcc/testsuite/c-c++-common/cpp/pr121508-4.c b/gcc/testsuite/c-c++-common/cpp/pr121508-4.c new file mode 100644 index 000000000000..1c2ee78551a9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/pr121508-4.c @@ -0,0 +1,8 @@ +/* PR preprocessor/121508 */ +/* { dg-do compile } */ + +/* { dg-error "'__has_embed' used outside of preprocessing directive" "" { target *-*-* } .+4 } */ +/* { dg-error "missing terminating '>' character" "" { target *-*-* } .+3 } */ +/* { dg-error "expected '\\\)'" "" { target *-*-* } .+2 } */ +/* { dg-error "expected ',' or ';' at end of input" "" { target *-*-* } .+1 } */ +int i = __has_embed(<__int16_t) diff --git a/gcc/testsuite/c-c++-common/cpp/pr121508-5.c b/gcc/testsuite/c-c++-common/cpp/pr121508-5.c new file mode 100644 index 000000000000..f9be0f0444b3 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/pr121508-5.c @@ -0,0 +1,8 @@ +/* PR preprocessor/121508 */ +/* { dg-do compile } */ + +/* { dg-error "'__has_embed' used outside of preprocessing directive" "" { target *-*-* } .+4 } */ +/* { dg-error "missing terminating '>' character" "" { target *-*-* } .+3 } */ +/* { dg-error "expected '\\\)'" "" { target *-*-* } .+2 } */ +/* { dg-error "expected ',' or ';' at end of input" "" { target *-*-* } .+1 } */ +int i = __has_embed(<__int16_t + 1 + 2 + 3 diff --git a/gcc/testsuite/c-c++-common/cpp/pr123339.c b/gcc/testsuite/c-c++-common/cpp/pr123339.c new file mode 100644 index 000000000000..10312a8d6bac --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/pr123339.c @@ -0,0 +1,9 @@ +/* PR preprocessor/123339 */ +/* { dg-do compile } */ + +/* { dg-error "'__has_include' used outside of preprocessing directive" "" { target *-*-* } .+5 } */ +/* { dg-error "missing terminating '>' character" "" { target *-*-* } .+4 } */ +/* { dg-error "missing '\\\(' before '__has_include' operand" "" { target *-*-* } .+3 } */ +/* { dg-error "expected identifier or '\\\(' before numeric constant" "" { target c } .+2 } */ +/* { dg-error "expected unqualified-id before numeric constant" "" { target c++ } .+1 } */ +__has_include <"foo" diff --git a/gcc/testsuite/c-c++-common/gomp/has-include-1.c b/gcc/testsuite/c-c++-common/gomp/has-include-1.c index 9532208b84b8..bb1b90526857 100644 --- a/gcc/testsuite/c-c++-common/gomp/has-include-1.c +++ b/gcc/testsuite/c-c++-common/gomp/has-include-1.c @@ -4,6 +4,5 @@ void foo (void) { #pragma omp parallel if (__has_include ("<stdlib.h>")) /* { dg-error "used outside of preprocessing directive" } */ -/* { dg-error "a function" "" { target *-*-* } .-1 } */ ; } diff --git a/libcpp/directives.cc b/libcpp/directives.cc index 7aed4db44460..c0065ed51015 100644 --- a/libcpp/directives.cc +++ b/libcpp/directives.cc @@ -131,7 +131,6 @@ static void do_pragma_warning_or_error (cpp_reader *, bool error); static void do_pragma_warning (cpp_reader *); static void do_pragma_error (cpp_reader *); static void do_linemarker (cpp_reader *); -static const cpp_token *get_token_no_padding (cpp_reader *); static const cpp_token *get__Pragma_string (cpp_reader *); static void destringize_and_run (cpp_reader *, const cpp_string *, location_t); @@ -825,7 +824,7 @@ glue_header_name (cpp_reader *pfile) buffer = XNEWVEC (char, capacity); for (;;) { - token = get_token_no_padding (pfile); + token = _cpp_get_token_no_padding (pfile); if (token->type == CPP_GREATER) break; @@ -868,7 +867,7 @@ parse_include (cpp_reader *pfile, int *pangle_brackets, const cpp_token *header; /* Allow macro expansion. */ - header = get_token_no_padding (pfile); + header = _cpp_get_token_no_padding (pfile); *location = header->src_loc; if ((header->type == CPP_STRING && header->val.str.text[0] != 'R') || header->type == CPP_HEADER_NAME) @@ -2399,18 +2398,6 @@ do_pragma_error (cpp_reader *pfile) do_pragma_warning_or_error (pfile, true); } -/* Get a token but skip padding. */ -static const cpp_token * -get_token_no_padding (cpp_reader *pfile) -{ - for (;;) - { - const cpp_token *result = cpp_get_token (pfile); - if (result->type != CPP_PADDING) - return result; - } -} - /* Check syntax is "(string-literal)". Returns the string on success, or NULL on failure. */ static const cpp_token * @@ -2419,23 +2406,17 @@ get__Pragma_string (cpp_reader *pfile) const cpp_token *string; const cpp_token *paren; - paren = get_token_no_padding (pfile); - if (paren->type == CPP_EOF) - _cpp_backup_tokens (pfile, 1); + paren = _cpp_get_token_no_padding (pfile); if (paren->type != CPP_OPEN_PAREN) return NULL; - string = get_token_no_padding (pfile); - if (string->type == CPP_EOF) - _cpp_backup_tokens (pfile, 1); + string = _cpp_get_token_no_padding (pfile); if (string->type != CPP_STRING && string->type != CPP_WSTRING && string->type != CPP_STRING32 && string->type != CPP_STRING16 && string->type != CPP_UTF8STRING) return NULL; - paren = get_token_no_padding (pfile); - if (paren->type == CPP_EOF) - _cpp_backup_tokens (pfile, 1); + paren = _cpp_get_token_no_padding (pfile); if (paren->type != CPP_CLOSE_PAREN) return NULL; diff --git a/libcpp/macro.cc b/libcpp/macro.cc index 24493a45f860..736c360336d0 100644 --- a/libcpp/macro.cc +++ b/libcpp/macro.cc @@ -392,11 +392,8 @@ builtin_has_include_1 (cpp_reader *pfile, const char *name, bool *paren, bool *bracket, location_t *loc) { if (!pfile->state.in_directive) - { - cpp_error (pfile, CPP_DL_ERROR, - "%qs used outside of preprocessing directive", name); - return NULL; - } + cpp_error (pfile, CPP_DL_ERROR, + "%qs used outside of preprocessing directive", name); pfile->state.angled_headers = true; const auto sav_padding = pfile->state.directive_wants_padding;
