[gcc r17-2902] cobol: Avoid internal compiler error in generated lexer.

"James K. Lowden via Gcc-cvs" <[email protected]> Mon, 3 Aug 2026 18:29:49 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:a5407eb736feab6e8778c0899fbf4626ee506a60

commit r17-2902-ga5407eb736feab6e8778c0899fbf4626ee506a60
Author: James K. Lowden <[email protected]>
Date:   Mon Aug 3 14:06:08 2026 -0400

    cobol: Avoid internal compiler error in generated lexer.
    
    For some inputs the lexer returned: input buffer overflow, can't
    enlarge buffer because scanner uses REJECT.  In fact REJECT is not
    used, but the message can be engendered by variable-length patterns if
    they match very long input.
    
    Use the flex input() and unput() to read the stream in advance of
    pattern-matching, and restore it before the lexer continues.
    
    gcc/cobol/ChangeLog:
    
            * scan.l: Remove pattern that potentially matches until EOF.
            * scan_ante.h (skip_string): Recast in terms of input/unput.
            (yyinput): Declare function.
            (yyunput): Declare function.
            (is_refmod): Recast in terms of input/unput.

Diff:
---
 gcc/cobol/scan.l      | 12 ++++----
 gcc/cobol/scan_ante.h | 76 ++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 64 insertions(+), 24 deletions(-)

diff --git a/gcc/cobol/scan.l b/gcc/cobol/scan.l
index b2843b9c7adf..1de688ee74fe 100644
--- a/gcc/cobol/scan.l
+++ b/gcc/cobol/scan.l
@@ -168,6 +168,10 @@ NAMEQUALS {NAMEQUAL}({SPC}{NAMEQUAL})*
 
 STRING   [^\r\n""]+
 STRING1  [^\r\n'']+
+
+QSTRING1 ['']{STRING1}(['']{2}{STRING1})*['']
+QSTRING2 [""]{STRING}([""]{2}{STRING})*[""]
+                        
 			/* comma & semicolon must be followed by a space */
 COMMA    [,;][[:blank:]]*
 
@@ -1915,12 +1919,10 @@ USE({SPC}FOR)?		{ return USE; }
 
   {ISNT}{SPC}/OMITTED { return NOT; }
 
-  [(:)] 			{ return *yytext; }
+  [:)]	 			{ return *yytext; }
   [(]/[^(:)""'']*[:][^)]*[)] 	{ return LPAREN; /* parentheses around a colon */ }
-  [(][^:""'']*[:][^)]*[)] 	{ // does not match foo(bar)\n:  :-(  
-                                  int tok = is_refmod(yytext, yytext + yyleng)?
-                                      int(LPAREN) : '(';
-                                  myless(1);
+  [(] 				{
+                                  int tok = is_refmod()? int(LPAREN) : '(';
                                   return tok; 
                                 }
 
diff --git a/gcc/cobol/scan_ante.h b/gcc/cobol/scan_ante.h
index 402600edabc7..4c147cb994ee 100644
--- a/gcc/cobol/scan_ante.h
+++ b/gcc/cobol/scan_ante.h
@@ -1321,21 +1321,31 @@ static inline bool is_quote( const char ch ) {
   return ch == '\'' || ch == '"';
 }
 
-static const char*
-skip_string(const char* p, const char* pend, char delimiter) {
-  p++; // Skip opening delimiter
-  while (p < pend) {
-    if (p[0] == delimiter) {
-      if (p[1] == delimiter) {
-        p += 2; // doubled delimiter is escaped 
+static int yyinput();
+static void yyunput(int ch, char yytext_ptr[]);
+
+static std::string
+skip_string(char delimiter) {
+  int ch;
+  std::string found;
+
+  while ((ch = yyinput()) != EOF ) {
+    dbgmsg("%s:%d: input '%c'", __func__, __LINE__, ch);
+    found += ch;
+    if (ch == delimiter) {
+      if( (ch = yyinput()) == EOF ) break;
+      dbgmsg("%s:%d: input '%c'", __func__, __LINE__, ch);
+      if (ch == delimiter) {
+        found += ch;
+        continue;
       } else {
-        return ++p; // Found valid closing delimiter
+        unput(ch);
+        dbgmsg("%s:%d: unput '%c'", __func__, __LINE__, ch);
+        return found; // Found valid closing delimiter
       }
-    } else {
-      p++;
     }
   }
-  return pend;
+  return found;
 }
 
 /*
@@ -1349,16 +1359,44 @@ skip_string(const char* p, const char* pend, char delimiter) {
  * parentheses in the left part, e.g. ((LENGTH OF x/2) - (y/2)) : 1.
  */
 static bool
-is_refmod( const char input[], const char enput[] ) {
-  if( input == enput ) return false;
-  gcc_assert( *input == '(' );
-  int depth = 1;
+is_refmod() {
+  class yystr_t {
+    std::string text;
+  public:
+    ~yystr_t() {
+      while( ! text.empty() ) {
+        char ch = text.back();
+        text.pop_back();
+        unput(ch);
+        dbgmsg("%s:%d: unput '%c'", __func__, __LINE__, ch);
+      }
+    }
+    int input() {
+      int ch = yyinput();
+      dbgmsg("%s:%d: input '%c'", __func__, __LINE__, ch);
+      if( ch != EOF) text += ch;
+      return ch;
+    }
+    yystr_t& operator+=( const std::string& that ) {
+      text += that;
+      return *this;
+    }
+    const char * c_str() const { return text.c_str(); }
+    size_t size() const { return text.size(); }
+  } yystr;
+
+  gcc_assert( *yytext == '(' );
+
+  int ch, depth = 1;
   bool colon_at_depth1 = false;
 
-  for( const char *p = input + 1; p < enput; p++ ) {
-    char ch = *p;
+  while( (ch = yystr.input()) != EOF ) {
     if( is_quote(ch) ) {
-      p = skip_string(p, enput, ch) - 1;
+      std::string s = skip_string(ch);
+      if( s.empty() ) {
+        break;
+      }
+      yystr += s;
       continue;
     }
     if( ch == '(' ) {
@@ -1378,6 +1416,6 @@ is_refmod( const char input[], const char enput[] ) {
     }
   }
   dbgmsg("%s:%d: '%.*s' is %sa refmod", __func__, __LINE__,
-         int(enput - input), input, colon_at_depth1? "" : "not ");
+         int(yystr.size()), yystr.c_str(), colon_at_depth1? "" : "not ");
   return colon_at_depth1;
 }