https://gcc.gnu.org/g:b8c8ee7caec2c828190472ffec272b4f51f35393

commit r14-12935-gb8c8ee7caec2c828190472ffec272b4f51f35393
Author: Jakub Jelinek <[email protected]>
Date:   Mon Sep 21 12:41:35 2026 -0600

    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
            * 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.
    
    Reviewed-by: Andrea Pinski <[email protected]>
    (cherry picked from commit 9e5fda8ec5716b6fa2b57bdfd56e2b91116a93ff)

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 +++++++++
 libcpp/directives.cc                        | 29 +++++------------------------
 7 files changed, 54 insertions(+), 24 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/libcpp/directives.cc b/libcpp/directives.cc
index 479f8c716e8b..9aced277b07f 100644
--- a/libcpp/directives.cc
+++ b/libcpp/directives.cc
@@ -127,7 +127,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);
@@ -750,7 +749,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;
@@ -792,7 +791,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)
@@ -1831,18 +1830,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 *
@@ -1851,23 +1838,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;

Reply via email to