From 1d0ff3020c45072cabcf7e9d20df0f4c304ba491 Mon Sep 17 00:00:00 2001
From: "James K. Lowden" <jklowden@symas.com>
Date: Fri, 17 Jan 2025 18:53:40 -0500
Subject: [PATCH] WIP: bring location to CDF

---
 gcc/cobol/UAT/testsuite.src/syn_misc.at |  13 +-
 gcc/cobol/cbldiag.h                     |  15 +-
 gcc/cobol/cdf.y                         | 301 +++++++++++-------------
 gcc/cobol/cdfval.h                      |   2 +
 gcc/cobol/lexio.h                       |   4 +-
 gcc/cobol/scan_ante.h                   |   4 +
 gcc/cobol/scan_post.h                   |   2 +-
 gcc/cobol/symbols.h                     |  11 +-
 gcc/cobol/util.cc                       |  61 ++++-
 9 files changed, 222 insertions(+), 191 deletions(-)

diff --git a/gcc/cobol/UAT/testsuite.src/syn_misc.at b/gcc/cobol/UAT/testsuite.src/syn_misc.at
index 88a317f23c69..33c12c93e4ac 100644
--- a/gcc/cobol/UAT/testsuite.src/syn_misc.at
+++ b/gcc/cobol/UAT/testsuite.src/syn_misc.at
@@ -826,13 +826,14 @@ AT_DATA([prog.cob], [
            CONTINUE
            .
 ])
-AT_CHECK([$COMPILE_ONLY -D X prog.cob], [1], [], [prog.cob:4:32: warning: CDF syntax error: no such variable 'BANANA'
-    4 |        PROCEDURE      DIVISION.
-      |                                ^
+AT_CHECK([$COMPILE_ONLY -D X prog.cob], [1], [], [prog.cob:5:14: error: CDF syntax error: no such variable 'BANANA'
+    5 |        >> IF BANANA
+      |              ^
 No BANANA here.
-prog.cob:4:32: warning: name already in dictionary: X
-prog.cob:4:32: warning: X previously defined in :1
-prog.cob:4:32: error: >>CDF parser failed
+prog.cob:9:20: error: name already in dictionary: X
+    9 |          >> DEFINE X 1
+      |                    ^
+prog.cob:9:20: error: X previously defined in :1
 prog.cob:10:1: error: syntax error, unexpected invalid token
    10 |        >> END-IF
       | ^
diff --git a/gcc/cobol/cbldiag.h b/gcc/cobol/cbldiag.h
index 2a4234a5e25e..47892177c6cc 100644
--- a/gcc/cobol/cbldiag.h
+++ b/gcc/cobol/cbldiag.h
@@ -41,10 +41,12 @@ bool yywarn( const char fmt[], ... );
 struct  YYLTYPE;
 #endif
 #ifndef YDFLTYPE
-struct YDFLTYPE;
+struct  YDFLTYPE;
 #endif
+
 // an error at a location, called from the parser for semantic errors
-void error_msg( const YYLTYPE& loc, const char gmsgid[], ... );
+template <typename LOC>
+void error_msg( const LOC& loc, const char gmsgid[], ... );
 
 // for CDF and other warnings that refer back to an earlier line
 // (not in diagnostic framework yet)
@@ -52,7 +54,7 @@ void yyerrorvl( int line, const char *filename, const char fmt[], ... );
 
 void cbl_unimplementedw(const char *gmsgid, ...); // warning
 void cbl_unimplemented(const char *gmsgid, ...);  // error
-void cbl_unimplemented_at( const YYLTYPE& loc, const char *gmsgid, ... );
+void cbl_unimplemented_at( const  YYLTYPE& loc, const char *gmsgid, ... );
 
 /*
  * dbgmsg and dbgerr produce messages not intended for the user.  They cannot
@@ -63,3 +65,10 @@ void cbl_unimplemented_at( const YYLTYPE& loc, const char *gmsgid, ... );
 void dbgerr( const char fmt[], ... );
 void dbgmsg( const char fmt[], ... );
 
+template <typename LOC>
+void gcc_location_set( const LOC& loc );
+
+template <typename LOC>
+void location_dump( const char func[], int line,
+		    const char tag[], const LOC& loc);
+
diff --git a/gcc/cobol/cdf.y b/gcc/cobol/cdf.y
index cd96e963f70a..799b7ecda80e 100644
--- a/gcc/cobol/cdf.y
+++ b/gcc/cobol/cdf.y
@@ -65,6 +65,33 @@ extern char *yytext;
 static int ydflex(void);
 
 #define PROGRAM current_program_index()
+
+const YYLTYPE& cobol_location();
+static YYLTYPE location_set( const YYLTYPE& loc );
+
+
+#define YYLLOC_DEFAULT(Current, Rhs, N) 				\
+  do {									\
+      if (N)                                                            \
+        {                                                               \
+          (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;        \
+          (Current).first_column = YYRHSLOC (Rhs, 1).first_column;      \
+          (Current).last_line    = YYRHSLOC (Rhs, N).last_line;         \
+          (Current).last_column  = YYRHSLOC (Rhs, N).last_column;       \
+	  location_dump("parse.c", N,					\
+			"rhs N  ", YYRHSLOC (Rhs, N));			\
+        }                                                               \
+      else                                                              \
+        {                                                               \
+          (Current).first_line   =					\
+	  (Current).last_line    = YYRHSLOC (Rhs, 0).last_line;		\
+          (Current).first_column =					\
+	  (Current).last_column  = YYRHSLOC (Rhs, 0).last_column;	\
+        }                                                               \
+      location_dump("parse.c", __LINE__, "current", (Current));		\
+      gcc_location_set( location_set(Current) );			\
+  } while (0)
+
 %}
 
 %code requires {
@@ -100,8 +127,6 @@ static int ydflex(void);
   }
 #pragma GCC diagnostic pop
 
-  void yyerror( const char fmt[], ... );
-
   bool operator==( const cdfval_base_t& lhs, int rhs );
   bool operator||( const cdfval_base_t& lhs, const cdfval_base_t& rhs );
   bool operator&&( const cdfval_base_t& lhs, const cdfval_base_t& rhs );
@@ -148,18 +173,6 @@ static class exception_turns_t {
     auto elem = exceptions.find(type);
     if( elem != exceptions.end() ) return false; // cannot add twice
 
-    std::set<size_t> uniq;
-
-    for( auto file : files ) {
-      auto inserted = uniq.insert(file);
-      if( ! inserted.second ) {
-        auto prev = cbl_file_of(symbol_at(*inserted.first));
-        yyerror("%s:%d: %s No file-name shall be specified more than once "
-              "for one exception condition", __func__, __LINE__, prev->name);
-        return false;
-      }
-    }
-
     exceptions[type] = files;
     return true;
   }
@@ -209,7 +222,7 @@ apply_cdf_turn( exception_turns_t& turns ) {
   }
   if( getenv("SHOW_PARSE") ) enabled_exceptions.dump();
   return true;
-}
+}			
 %}
 
 %union {
@@ -219,7 +232,7 @@ apply_cdf_turn( exception_turns_t& turns ) {
     cdf_arg_t     cdfarg;
     cdfval_base_t cdfval;
     cbl_file_t *file;
-    std::set<cbl_file_t*> *files;
+    std::set<size_t> *files;
 }
 
 %printer { fprintf(yyo, "'%s'", $$ ); } <string>
@@ -284,6 +297,7 @@ apply_cdf_turn( exception_turns_t& turns ) {
 %define api.token.prefix{YDF_}
 
 %locations
+%define parse.error verbose
 %%
 top:		partials { YYACCEPT; }
 	|	copy '.'
@@ -295,7 +309,7 @@ top:		partials { YYACCEPT; }
 		  YYACCEPT;
 		}
 	|	copy error {
-		  yyerror("COPY directive must end in a '.'");
+		  error_msg(@error, "COPY directive must end in a '.'");
 		  YYACCEPT;
 		}
 	|	completes { YYACCEPT; }
@@ -355,14 +369,14 @@ partial:	cdf_if            /* text */
 cdf_define:	CDF_DEFINE cdf_constant NAME as cdf_expr[value] override
 		{
 		  if( keyword_tok($NAME) ) {
-		    yyerror("%s is a COBOL keyword", $NAME);
+		    error_msg(@NAME, "%s is a COBOL keyword", $NAME);
 		    YYERROR;
 		  }
 		  if( !cdfval_add( $NAME, cdfval_t($value), $override) ) {
-		    yyerror("name already in dictionary: %s", $NAME);
+		    error_msg(@NAME, "name already in dictionary: %s", $NAME);
 		    const cdfval_t& entry = dictionary[$NAME];
 		    assert(entry.filename);
-		    yyerror("%s previously defined in %s:%d",
+		    error_msg(@NAME, "%s previously defined in %s:%d",
 		             $NAME, entry.filename, entry.lineno);
 		    YYERROR;
 		  }
@@ -370,7 +384,7 @@ cdf_define:	CDF_DEFINE cdf_constant NAME as cdf_expr[value] override
 	|	CDF_DEFINE cdf_constant NAME '=' cdf_expr[value] override
 		{  /* accept, but as error */
 		  if( scanner_parsing() ) {
-		    yyerror("CDF syntax error: %s = value invalid", $NAME);
+		    error_msg(@NAME, "CDF syntax error: %s = value invalid", $NAME);
 		  }
 		}
 	|	CDF_DEFINE cdf_constant NAME as OFF
@@ -394,13 +408,13 @@ cdf_define:	CDF_DEFINE cdf_constant NAME as cdf_expr[value] override
 	|	CDF_DEFINE FEATURE as ON {
 		  auto feature = cbl_gcobol_feature_t($2);
 		  if( ! cobol_gcobol_feature_set(feature, true) ) {
-		    yyerror(">>DEFINE %EBCDIC-MODE is invalid within program body");
+		    error_msg(@FEATURE, ">>DEFINE %EBCDIC-MODE is invalid within program body");
 		  }
 		}
 	|	CDF_DEFINE FEATURE as OFF {
 		  auto feature = cbl_gcobol_feature_t($2);
 		  if( ! cobol_gcobol_feature_set(feature, false) ) {
-		    yyerror(">>DEFINE %EBCDIC-MODE is invalid within program body");
+		    error_msg(@FEATURE, ">>DEFINE %EBCDIC-MODE is invalid within program body");
 		  }
 		}
 		;
@@ -438,10 +452,8 @@ except_name:	EXCEPTION_NAME[ec] {
 	|	EXCEPTION_NAME[ec] filenames {
 		  assert($ec != ec_none_e);
 		  std::list<size_t> files;
-		  std::transform( $filenames->begin(), $filenames->end(),
-		                  std::back_inserter(files),
-		                  []( const cbl_file_t* f ) {
-		                      return symbol_index(symbol_elem_of(f)); } );
+		  std::copy( $filenames->begin(), $filenames->end(),
+		                  std::back_inserter(files) );
 		  exception_turns.add_exception(ec_type_t($ec), files);
 		}
 		;
@@ -454,14 +466,24 @@ except_check:	CHECKING on  { exception_turns.enabled = true; }
 		}
 		;
 
-filenames:      filename { $$ = new std::set<cbl_file_t*>; $$->insert($1); }
-        |       filenames filename { $1->insert($2); }
+filenames:      filename {
+		  $$ = new std::set<size_t>;
+		  $$->insert(symbol_index(symbol_elem_of($1)));
+		}
+        |       filenames filename {
+		  $$ = $1;
+		  auto inserted = $$->insert(symbol_index(symbol_elem_of($2)));
+		  if( ! inserted.second ) {
+		    error_msg(@2, "%s: No file-name shall be specified more than "
+			      " once for one exception condition", $filename->name);
+		  }
+		}
                 ;
 filename:       NAME
                 {
                   struct symbol_elem_t *e = symbol_file(PROGRAM, $1);
                   if( !(e && e->type == SymFile) ) {
-		    yyerror("invalid file name '%s'", $NAME);
+		    error_msg(@NAME, "invalid file name '%s'", $NAME);
 		    YYERROR;
                   }
                   $$ = cbl_file_of(e);
@@ -504,7 +526,7 @@ cdf_cond_expr:	BOOL
 			   $1, $$? "true" : "false");
 		  }
 		}
-	|	cdf_bool_expr { $$ = $1 == 0? false : true; }
+	|	cdf_bool_expr { $$ = $1(@1) == 0? false : true; }
 	|	FEATURE DEFINED {
 		  const auto& feature($1);
 		  $$ = (feature == int(feature & cbl_gcobol_features));
@@ -516,11 +538,11 @@ cdf_cond_expr:	BOOL
 		 * "Abbreviated combined relation conditions
 		 * shall not be specified."
 		 */
-cdf_bool_expr:	cdf_bool_expr OR cdf_and  { $$ = cdfval_t($1 || $3); }
+cdf_bool_expr:	cdf_bool_expr OR cdf_and { $$ = cdfval_t($1(@1) || $3(@3)); }
 	|	cdf_and
-	;
+		;
 
-cdf_and:	cdf_and AND cdf_reloper { $$ = cdfval_t($1 && $3); }
+cdf_and:	cdf_and AND cdf_reloper { $$ = cdfval_t($1(@1) && $3(@3)); }
 	|	cdf_reloper
 		;
 
@@ -528,22 +550,47 @@ cdf_reloper:	    cdf_relexpr
 	|	NOT cdf_relexpr { $$ = cdfval_t($2.number? 1 : 0); }
 		;
 
-cdf_relexpr:	cdf_relexpr '<' cdf_expr { $$ = $1 <  $3; }
-	|	cdf_relexpr LE  cdf_expr { $$ = $1 <= $3; }
-	|	cdf_relexpr '=' cdf_expr { $$ = $1 == $3; }
-	|	cdf_relexpr NE  cdf_expr { $$ = $1 != $3; }
-	|	cdf_relexpr GE  cdf_expr { $$ = $1 >= $3; }
-	|	cdf_relexpr '>' cdf_expr { $$ = $1 >  $3; }
+cdf_relexpr:	cdf_relexpr '<' cdf_expr { $$ = $1(@1) <  $3(@3); }
+	|	cdf_relexpr LE  cdf_expr { $$ = $1(@1) <= $3(@3); }
+	|	cdf_relexpr '=' cdf_expr {
+		  $$ = cdfval_t(false);
+		  if( ( $1.string &&  $3.string) ||
+		      (!$1.string && !$3.string) )
+		  {
+		      $$ = $1 == $3;
+		  } else {
+		    const char *msg = $1.string? 
+		      "incommensurate comparison is FALSE: '%s' = %ld" :
+		      "incommensurate comparison is FALSE: %ld = '%s'" ;
+		    error_msg(@1, msg);
+		  }
+		}
+	|	cdf_relexpr NE cdf_expr 
+		{
+		  $$ = cdfval_t(false);
+		  if( ( $1.string &&  $3.string) ||
+		      (!$1.string && !$3.string) )
+		  {
+		      $$ = $1 != $3;
+		  } else {
+		    const char *msg = $1.string? 
+		      "incommensurate comparison is FALSE: '%s' = %ld" :
+		      "incommensurate comparison is FALSE: %ld = '%s'" ;
+		    error_msg(@1, msg);
+		  }
+		}
+	|	cdf_relexpr GE  cdf_expr { $$ = $1(@1) >= $3(@3); }
+	|	cdf_relexpr '>' cdf_expr { $$ = $1(@1) >  $3(@3); }
 	|	cdf_expr
 		;
 
-cdf_expr:	cdf_expr '+' cdf_expr { $$ = $1 + $3; }
-        |       cdf_expr '-' cdf_expr { $$ = $1 - $3; }
-        |       cdf_expr '*' cdf_expr { $$ = $1 * $3; }
-        |       cdf_expr '/' cdf_expr { $$ = $1 / $3; }
-	|	         '+' cdf_expr %prec NEG { $$ = $2; }
-	|	         '-'cdf_expr %prec NEG { $$ = negate($2); }
-	|	         '(' cdf_bool_expr ')'  { $$ = $2; }
+cdf_expr:	cdf_expr '+' cdf_expr { $$ = $1(@1) + $3(@3); }
+        |       cdf_expr '-' cdf_expr { $$ = $1(@1) - $3(@3); }
+        |       cdf_expr '*' cdf_expr { $$ = $1(@1) * $3(@3); }
+        |       cdf_expr '/' cdf_expr { $$ = $1(@1) / $3(@3); }
+	|	         '+' cdf_expr %prec NEG { $$ = $2(@2); }
+	|	         '-' cdf_expr %prec NEG { $$ = negate($2(@2)); }
+	|	         '(' cdf_bool_expr ')'  { $$ = $2(@2); }
         |	cdf_factor
         ;
 
@@ -555,7 +602,7 @@ cdf_factor:     NAME {
 		    if( ! scanner_parsing() ) {
 		      yywarn("CDF skipping: no such variable '%s' (ignored)", $1);
 		    } else {
-		      yyerror("CDF syntax error: no such variable '%s'", $1);
+		      error_msg(@NAME, "CDF syntax error: no such variable '%s'", $1);
 		    }
 		    $$ = cdfval_t();
 		  }
@@ -565,7 +612,7 @@ cdf_factor:     NAME {
 	| 	NUMSTR {
 		  auto value = integer_literal($NUMSTR);
 		  if( !value.second ) {
-		    yyerror("CDF error: parsed %s as %ld",
+		    error_msg(@1, "CDF error: parsed %s as %ld",
 		             $NUMSTR, value.first);
 		    YYERROR;
 		  }
@@ -585,7 +632,7 @@ copy_impl:	copybook_name suppress REPLACING replace_bys
 copybook_name: 	COPY name_one
 		{
 		  if( -1 == copybook.open($2.string) ) {
-		    yyerror("could not open copybook file "
+		    error_msg(@2, "could not open copybook file "
 		             "for '%s'", $2.string);
 		    YYERROR;
 		  }
@@ -594,7 +641,7 @@ copybook_name: 	COPY name_one
 		{
 		  copybook.library($lib.string);
 		  if( -1 == copybook.open($src.string) ) {
-		    yyerror("could not open copybook file "
+		    error_msg(@src, "could not open copybook file "
 		             "for '%s' in '%'s'", $src.string, $lib.string);
 		    YYERROR;
 		  }
@@ -733,6 +780,13 @@ with:           %empty
 
 %%
 
+static YYLTYPE cdf_location;
+
+static YYLTYPE
+location_set( const YYLTYPE& loc ) {
+  return cdf_location = loc;
+}
+
 bool // used by cobol1.cc
 defined_cmd( const char arg[] )
 {
@@ -778,61 +832,27 @@ cdf_token_str( int token ) {
 }
 
 bool operator==( const cdfval_base_t& lhs, int rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-
+  gcc_assert( !lhs.string );
   return lhs.number == rhs;
 }
 
 bool operator||( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
-
+  gcc_assert( !lhs.string && !rhs.string );
   return lhs.number || rhs.number;
 }
 
 bool operator&&( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
-
+  gcc_assert( !lhs.string && !rhs.string );
   return lhs.number && rhs.number;
 }
 
 cdfval_t operator<( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
+  gcc_assert( !lhs.string && !rhs.string );
   return cdfval_t(lhs.number < rhs.number);
 }
 
 cdfval_t operator<=( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
+  gcc_assert( !lhs.string && !rhs.string );
   return cdfval_t(lhs.number <= rhs.number);
 }
 
@@ -843,17 +863,7 @@ cdfval_t operator==( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
   if( !lhs.string && !rhs.string ) {
     return cdfval_t(lhs.number == rhs.number);
   }
-  if( lhs.string ) {
-    yyerror("incommensurate comparison is FALSE: '%s' = %ld",
-	     lhs.string, rhs.number);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("incommensurate comparison is FALSE: %ld = '%s'",
-	     lhs.number, rhs.string);
-    return false;
-  }
-  cbl_internal_error("'%s' is not an integer", rhs.string);
+  cbl_internal_error("incommensurate operands");
   return false;
 }
 
@@ -864,96 +874,42 @@ cdfval_t operator!=( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
   if( !lhs.string && !rhs.string ) {
     return cdfval_t(lhs.number != rhs.number);
   }
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
-  cbl_internal_error("'%s' is not an integer", rhs.string);
+  cbl_internal_error("incommensurate operands");
   return false;
 }
 
 cdfval_t operator>=( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
+  gcc_assert( !lhs.string && !rhs.string );
   return cdfval_t(lhs.number >= rhs.number);
 }
 
 cdfval_t operator>( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
+  gcc_assert( !lhs.string && !rhs.string );
   return cdfval_t(lhs.number > rhs.number);
 }
 
 cdfval_t operator+( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
+  gcc_assert( !lhs.string && !rhs.string );
   return cdfval_t(lhs.number + rhs.number);
 }
 
 cdfval_t operator-( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
+  gcc_assert( !lhs.string && !rhs.string );
   return cdfval_t(lhs.number - rhs.number);
 }
 
 cdfval_t operator*( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
+  gcc_assert( !lhs.string && !rhs.string );
   return cdfval_t(lhs.number * rhs.number);
 }
 
 cdfval_t operator/( const cdfval_base_t& lhs, const cdfval_base_t& rhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-  if( rhs.string ) {
-    yyerror("'%s' is not an integer", rhs.string);
-    return false;
-  }
+  gcc_assert( !lhs.string && !rhs.string );
   return cdfval_t(lhs.number / rhs.number);
 }
 
 cdfval_t negate( cdfval_base_t lhs ) {
-  if( lhs.string ) {
-    yyerror("'%s' is not an integer", lhs.string);
-    return false;
-  }
-
+  gcc_assert( !lhs.string );
   lhs.number = -lhs.number;
   return lhs;
 }
@@ -965,9 +921,6 @@ static int ydflex(void) {
   return yylex();
 }
 
-#undef yyerror
-#include "cbldiag.h"
-
 bool
 cdf_value( const char name[], cdfval_t value ) {
   auto p = dictionary.find(name);
@@ -986,3 +939,19 @@ cdf_value( const char name[] ) {
 
   return &p->second;
 }
+
+static bool
+verify_integer( const YDFLTYPE& loc, const cdfval_base_t& val ) {
+  if( val.string ) {
+    error_msg(loc, "'%s' is not an integer", val.string);
+    return false;
+  }
+  return true;
+}
+
+cdfval_base_t&
+cdfval_base_t::operator()( const YDFLTYPE& loc ) {
+  static cdfval_t zero(0);
+  return verify_integer(loc, *this) ? *this : zero;
+}    
+      
diff --git a/gcc/cobol/cdfval.h b/gcc/cobol/cdfval.h
index 503e4507a15a..0b45820be117 100644
--- a/gcc/cobol/cdfval.h
+++ b/gcc/cobol/cdfval.h
@@ -38,10 +38,12 @@
 
 bool scanner_parsing();
 
+struct YDFLTYPE;
 struct cdfval_base_t {
   bool off;
   const char *string;
   int64_t number;
+  cdfval_base_t& operator()( const YDFLTYPE& loc );
 };
 
 struct cdf_arg_t {
diff --git a/gcc/cobol/lexio.h b/gcc/cobol/lexio.h
index d7354b79deb8..da1eaf6a710a 100644
--- a/gcc/cobol/lexio.h
+++ b/gcc/cobol/lexio.h
@@ -128,8 +128,8 @@ struct YYLTYPE
 # define YYLTYPE_IS_TRIVIAL 1
 #endif
 
-void location_dump( const char func[], int line,
-		    const char tag[], const YYLTYPE& loc);
+// void location_dump( const char func[], int line,
+// 		    const char tag[], const YYLTYPE& loc);
 
 struct filespan_t : public bytespan_t {
   char *cur, *eol, *quote;
diff --git a/gcc/cobol/scan_ante.h b/gcc/cobol/scan_ante.h
index c071d8f316e2..ab62fc6057be 100644
--- a/gcc/cobol/scan_ante.h
+++ b/gcc/cobol/scan_ante.h
@@ -364,6 +364,8 @@ static void level_found() {
     yyless(n);					\
   } while(0)
 
+void cdf_location_set(YYLTYPE loc);
+
 static void
 update_location() {
   YYLTYPE loc = {
@@ -379,6 +381,8 @@ update_location() {
 
   yylloc = loc;
 
+  cdf_location_set(loc);
+
   if( getenv(__func__) ) {
     location_dump(__func__, __LINE__, "yylloc", yylloc);
   }
diff --git a/gcc/cobol/scan_post.h b/gcc/cobol/scan_post.h
index 4069e2ac4492..00db3e278ccd 100644
--- a/gcc/cobol/scan_post.h
+++ b/gcc/cobol/scan_post.h
@@ -258,7 +258,7 @@ prelex() {
   while( is_cdf_token(token) ) {
 
     if( ! run_cdf(token) ) {
-      yyerror( ">>CDF parser failed" );
+      dbgmsg( ">>CDF parser failed" );
       return NO_CONDITION;
     }
     // Return the CDF's discarded lookahead token, if extant. 
diff --git a/gcc/cobol/symbols.h b/gcc/cobol/symbols.h
index a0c7f748c693..1421170adfd2 100644
--- a/gcc/cobol/symbols.h
+++ b/gcc/cobol/symbols.h
@@ -2196,12 +2196,17 @@ struct YYLTYPE
 # define YYLTYPE_IS_TRIVIAL 1
 
 const YYLTYPE& cobol_location();
-void gcc_location_set( const YYLTYPE& loc );
+////void gcc_location_set( const YYLTYPE& loc );
 
+#endif
+
+template <typename LOC>
+void gcc_location_set( const LOC& loc );
+
+template <typename LOC>
 void location_dump( const char func[], int line,
-		    const char tag[], const YYLTYPE& loc);
+		    const char tag[], const LOC& loc);
 
-#endif
 
 // This is slightly oddball.  This is an entry point in the charutf8.cc module.
 // It's the only entry point in the module, and so it seemed to me wasteful to
diff --git a/gcc/cobol/util.cc b/gcc/cobol/util.cc
index 695c3e18c0a8..eda924fbd68d 100644
--- a/gcc/cobol/util.cc
+++ b/gcc/cobol/util.cc
@@ -2071,8 +2071,9 @@ cobol_filename_restore( bool scanning ) {
 
 static location_t token_location;
 
+template <typename LOC>
 void
-gcc_location_set( const YYLTYPE& loc ) {
+gcc_location_set( const LOC& loc ) {
   token_location = linemap_line_start( line_table, loc.last_line, 80 );
   token_location = linemap_position_for_column( line_table, loc.first_column);
   if( getenv(__func__) ) {
@@ -2080,14 +2081,6 @@ gcc_location_set( const YYLTYPE& loc ) {
   }
 }
 
-void
-location_dump( const char func[], int line, const char tag[], const YYLTYPE& loc) {
-  if( yy_flex_debug || yydebug ) 
-    fprintf(stderr, "%s:%d: %s location (%d,%d) to (%d,%d)\n",
-	    func, line, tag, 
-	    loc.first_line, loc.first_column, loc.last_line, loc.last_column);
-}
-
 #ifdef NDEBUG
 # define verify_format(M)
 #else
@@ -2137,6 +2130,15 @@ ydferror( const char gmsgid[], ... ) {
 
 extern int yychar;
 extern YYLTYPE yylloc;
+
+struct YDFLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+};
+
 /*
  * temp_loc_t is a hack in lieu of "%define parse.error custom".  When
  * instantiated, if there is a lookahead token (or one is provided), it sets
@@ -2154,6 +2156,12 @@ class temp_loc_t : protected YYLTYPE {
   temp_loc_t( const YYLTYPE& loc) : orig(token_location) {
     gcc_location_set(loc);
   }
+  temp_loc_t( const YDFLTYPE& loc) : orig(token_location) {
+    YYLTYPE lloc = {
+      loc.first_line, loc.first_column, 
+      loc.last_line,  loc.last_column };      
+    gcc_location_set(lloc);
+  }
   ~temp_loc_t() {
     if( orig != token_location ) {
       token_location = orig;
@@ -2161,8 +2169,22 @@ class temp_loc_t : protected YYLTYPE {
   }
 };
 
+// These uses of the error_msg function are never invoked, but encourage the
+// compiler to instantiate the templates.
+namespace none {
+  void foo() {
+    error_msg(YYLTYPE(), "fake");
+    error_msg(YDFLTYPE(), "also fake");
+    gcc_location_set(YYLTYPE());
+    gcc_location_set(YDFLTYPE());
+    location_dump("also", 0, "also", YYLTYPE());
+    location_dump("fake", 0, "fake", YDFLTYPE());
+  }
+};
+
+template <typename LOC>
 void
-error_msg( const YYLTYPE& loc, const char gmsgid[], ... ) {
+error_msg( const LOC& loc, const char gmsgid[], ... ) {
   temp_loc_t looker(loc);
   verify_format(gmsgid);
   parse_error_inc();
@@ -2176,6 +2198,25 @@ error_msg( const YYLTYPE& loc, const char gmsgid[], ... ) {
   global_dc->end_group();
 }
 
+template <typename LOC>
+void
+location_dump( const char func[], int line, const char tag[], const LOC& loc) {
+  if( yy_flex_debug || yydebug ) 
+    fprintf(stderr, "%s:%d: %s location (%d,%d) to (%d,%d)\n",
+	    func, line, tag, 
+	    loc.first_line, loc.first_column, loc.last_line, loc.last_column);
+}
+
+void
+cdf_location_set(YYLTYPE loc) {
+  extern YDFLTYPE ydflloc;
+
+  ydflloc.first_line =   loc.first_line;
+  ydflloc.first_column = loc.first_column;
+  ydflloc.last_line =    loc.last_line;
+  ydflloc.last_column =  loc.last_column;
+}
+
 void
 yyerror( const char gmsgid[], ... ) {
   temp_loc_t looker;
-- 
GitLab