[gcc r17-2344] cobol: Accept FD name as CALL parameter.

"James K. Lowden via Gcc-cvs" <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:d556f558b9db9a136ac9ec4cc804dd899d2e63cd

commit r17-2344-gd556f558b9db9a136ac9ec4cc804dd899d2e63cd
Author: James K. Lowden <[email protected]>
Date:   Sat Jul 11 17:56:58 2026 -0400

    cobol: Accept FD name as CALL parameter.
    
    A nonstandard extension CALL may pass an FD.  On the mainframe the
    called cannot be a COBOL program and the passed parameter is the
    file's DCB.  With this patch the parser is accepts an FD name as a
    CALL parameter and interprets it, for now, as a buffer name, with a
    warning.  Without -dialect ibm it is an error [Wcall-fd]. Further
    development is needed to emulate mainframe semantics.  Begins to
    address RT 3619.
    
    Also introduce __funcsig__ as a debugging convenience to print the
    full name of __PRETTY_FUNCTION__ without type and parameters.
    
    gcc/cobol/ChangeLog:
    
            * cbldiag.h (enum cbl_diag_id_t): Add IbmCallFd.
            * cobol1.cc (cobol_langhook_handle_option): Handle Wcall-fd.
            * gcobol.1: Document Wcall-fd.
            * lang-specs.h: Add Wcall-fd.
            * lang.opt: Same
            * messages.cc: Add Wcall-fd as IBM dialect option.
            * parse.y: Use was_fd_name() to prevent misuse.
            * parse_ante.h (field_find): Add incompatible semantics warning.
            (parser_move_carefully): Same.
            * symbols.cc (symbols_update): Add FD name to symbol map.
            * symbols.h (update_symbol_map2): Declare overload for file.
            (was_fd_name): Declare function.
            * symfind.cc (struct funcname): Define to minimize __PRETTY_FUNCTION__.
            (__funcsig__): Define function-name macro.
            (class sym_name_t): Redefine public members as const.
            (class symbol_file_names_t) Define maps for FD names.
            (update_symbol_map2): Define overload for file.
            (was_fd_name): Define.
            (symbol_find): Use new symbol_file_names class.

Diff:
---
 gcc/cobol/cbldiag.h    |   1 +
 gcc/cobol/cobol1.cc    |   4 ++
 gcc/cobol/gcobol.1     |   4 ++
 gcc/cobol/lang-specs.h |   1 +
 gcc/cobol/lang.opt     |   5 +++
 gcc/cobol/messages.cc  |   1 +
 gcc/cobol/parse.y      |   8 +++-
 gcc/cobol/parse_ante.h |  12 +++++
 gcc/cobol/symbols.cc   |   5 ++-
 gcc/cobol/symbols.h    |   3 ++
 gcc/cobol/symfind.cc   | 118 +++++++++++++++++++++++++++++++++++++++++++++++--
 11 files changed, 156 insertions(+), 6 deletions(-)

diff --git a/gcc/cobol/cbldiag.h b/gcc/cobol/cbldiag.h
index 6bb998d02d7c..8916a191200b 100644
--- a/gcc/cobol/cbldiag.h
+++ b/gcc/cobol/cbldiag.h
@@ -172,6 +172,7 @@ enum cbl_diag_id_t : uint64_t {
   LexReplaceE,
   LexSeparatorE,
 
+  IbmCallFd,
   IbmCdf,
   IbmEjectE,
   IbmEqualAssignE,
diff --git a/gcc/cobol/cobol1.cc b/gcc/cobol/cobol1.cc
index f7d832d5d2e7..3e2f3d43994e 100644
--- a/gcc/cobol/cobol1.cc
+++ b/gcc/cobol/cobol1.cc
@@ -581,6 +581,10 @@ cobol_langhook_handle_option (size_t scode,
           cobol_warning(MfBinaryLongLong, binary_long_long, warning_as_error);
           return true;
 
+        case OPT_Wcall_fd:
+          cobol_warning(IbmCallFd, cobol_call_fd, warning_as_error);
+          return true;
+
         case OPT_Wcall_giving:
           cobol_warning(MfCallGiving, call_giving, warning_as_error);
           return true;
diff --git a/gcc/cobol/gcobol.1 b/gcc/cobol/gcobol.1
index 8d39a1b3612c..bd11694ffd55 100644
--- a/gcc/cobol/gcobol.1
+++ b/gcc/cobol/gcobol.1
@@ -44,6 +44,7 @@
 .Op Fl Wno-bad-line-directive
 .Op Fl Wno-bad-numeric
 .Op Fl Wno-binary-long-long
+.Op Fl Wno-call-fd
 .Op Fl Wno-call-giving
 .Op Fl Wno-call-literal
 .Op Fl Wno-cdf-dollar
@@ -603,6 +604,9 @@ Warn if malformed
 directive is encountered.
 .It Fl Wno-binary-long-long
 Warn if BINARY-LONG-LONG is used.
+.It Fl Wno-call_fd
+Warn if CALL with FD parameter is used.
+This feature is currently unimplemented. 
 .It Fl Wno-call_giving
 Warn if CALL ... GIVING is used.
 .It Fl Wno-call_literal
diff --git a/gcc/cobol/lang-specs.h b/gcc/cobol/lang-specs.h
index 65f49b662883..a7a3092f0494 100644
--- a/gcc/cobol/lang-specs.h
+++ b/gcc/cobol/lang-specs.h
@@ -54,6 +54,7 @@
 	"%{Wno-bad-line-directive} "
 	"%{Wno-bad-numeric} "
 	"%{Wno-binary-long-long} "
+	"%{Wno-call-fd} "
 	"%{Wno-call-giving} "
 	"%{Wno-call-literal} "
 	"%{Wno-cdf-dollar} "
diff --git a/gcc/cobol/lang.opt b/gcc/cobol/lang.opt
index bd4cb22b0b28..61b1f583c83a 100644
--- a/gcc/cobol/lang.opt
+++ b/gcc/cobol/lang.opt
@@ -228,6 +228,11 @@ Wnllanginfo-error
 Cobol Warning Var(nllanginfo_error, 1) Init(1)
 Warn if nlanglanginfo(3) fails.
 
+; IbmCallFd
+Wcall-fd
+Cobol Warning Var(cobol_call_fd, 1) Init(1)
+Warn if fd name is used as a CALL parameter.
+
 ; IbmCdf
 Wibm-cdf
 Cobol Warning Var(cobol_ibmcdf, 1) Init(1)
diff --git a/gcc/cobol/messages.cc b/gcc/cobol/messages.cc
index aac9ff0a622e..6b33e91fc342 100644
--- a/gcc/cobol/messages.cc
+++ b/gcc/cobol/messages.cc
@@ -124,6 +124,7 @@ std::set<cbl_diag_t> cbl_diagnostics {
 
   { EcUnknownW, "-Wec-unknown", diagnostics::kind::warning },
 
+  { IbmCallFd, "-Wcall-fd", diagnostics::kind::error, dialect_ibm_e },
   { IbmCdf, "-Wibm-cdf", diagnostics::kind::error, dialect_ibm_e },
   { IbmEjectE, "-Wcobol-eject", diagnostics::kind::error, dialect_ibm_e },
   { IbmLengthOf, "-Wlength-of", diagnostics::kind::error, dialect_ibm_mf_gnu },
diff --git a/gcc/cobol/parse.y b/gcc/cobol/parse.y
index dec2f4e7dc2b..8213520980a0 100644
--- a/gcc/cobol/parse.y
+++ b/gcc/cobol/parse.y
@@ -1642,7 +1642,7 @@ cobol_words1:	COBOL_WORDS EQUATE NAME[keyword] WITH NAME[name] {
 		  if( ! cdf_tokens.reserve(@name, $name) ) { YYERROR; }
 		}
         |       PROCESS {
-                  cbl_message(@1, IbmCdf, "%qs", $1);
+                  cbl_message(@1, IbmCdf, "CDF directive ignored: %qs", $1);
                 }
 		;
 
@@ -13678,6 +13678,12 @@ ast_op( const cbl_loc_t& loc, cbl_refer_t *lhs, char op, cbl_refer_t *rhs ) {
 static void
 ast_relop( const cbl_loc_t& loc, cbl_field_t *tgt,
            cbl_refer_t lhs, relop_t op, cbl_refer_t rhs ) {
+  if( was_fd_name(lhs.field) || was_fd_name(rhs.field) ) {
+    const char *name = was_fd_name(lhs.field) ? lhs.field->name : rhs.field->name;
+    error_msg(loc, "cannot compare anything to FD %qs", name);
+    return;
+  }
+
   if( ! (valid_move(lhs.field, rhs.field) && valid_move(rhs.field, lhs.field)) ) {
     if( is_numeric(lhs.field) != is_numeric(rhs.field) ) {
       if( (lhs.field->type == FldFloat) || (rhs.field->type == FldFloat) ) {
diff --git a/gcc/cobol/parse_ante.h b/gcc/cobol/parse_ante.h
index bfc15a9918f1..dbab65f42a4d 100644
--- a/gcc/cobol/parse_ante.h
+++ b/gcc/cobol/parse_ante.h
@@ -2896,6 +2896,13 @@ field_find( cbl_loc_t loc, const std::list<const char *>& names ) {
     }
   }
   symbol_elem_t *e = symbol_find(names);
+  if( e && SymField == e->type && was_fd_name(cbl_field_of(e)) ) {
+    if( dialect_ok(loc, IbmCallFd, "CALL USING FD unimplemented") ) {
+      // No other COBOL compiler interprets the FD as a buffer.  This feature
+      // requires further development.
+      warn_msg(loc, "CALL USING FD passes file buffer, not handle");
+    }
+  }
   return e? cbl_field_of(e) : NULL;
 }
 
@@ -3426,6 +3433,11 @@ parser_move_carefully( const char */*F*/, int /*L*/,
   for( const auto& num_result : tgt_list->targets ) {
     const cbl_refer_t& tgt = num_result.refer;
 
+    if( was_fd_name(tgt.field) ) { 
+      error_msg(src.loc, "cannot compare anything to FD %qs", tgt.field->name);
+      return false;
+    }
+
     if( is_index ) {
       if( tgt.field->type != FldIndex && src.field->type != FldIndex) {
         error_msg(src.loc, "invalid SET %qs (%s) TO %qs (%s): not a field index",
diff --git a/gcc/cobol/symbols.cc b/gcc/cobol/symbols.cc
index 45437d334185..56dbf066145c 100644
--- a/gcc/cobol/symbols.cc
+++ b/gcc/cobol/symbols.cc
@@ -2059,7 +2059,10 @@ symbols_update( size_t first, bool parsed_ok ) {
         ninvalid++;
         continue;
       }
-    if( parsed_ok ) parser_file_add(&file);
+      if( parsed_ok ) {
+        parser_file_add(&file);
+        update_symbol_map2(file); // Add FD name as a name for the default record. 
+      }
     } else {
       if( p->type == SymField ) {
         auto f = cbl_field_of(p);
diff --git a/gcc/cobol/symbols.h b/gcc/cobol/symbols.h
index 3744b3a7f616..408a8a5f1043 100644
--- a/gcc/cobol/symbols.h
+++ b/gcc/cobol/symbols.h
@@ -2369,9 +2369,12 @@ void build_symbol_map();
 bool update_symbol_map( symbol_elem_t *e );
 
 void update_symbol_map2( const symbol_elem_t *elem );
+void update_symbol_map2( const cbl_file_t& file );
 void finalize_symbol_map2();
 void dump_symbol_map2();
 
+bool was_fd_name( const cbl_field_t * field ); // uses symbol_map2, sort of
+
 symbol_elem_t * symbol_register( const char name[] );
 
 std::pair<symbol_elem_t *, bool>
diff --git a/gcc/cobol/symfind.cc b/gcc/cobol/symfind.cc
index a3abd4049d8a..6c3d06cc1a6e 100644
--- a/gcc/cobol/symfind.cc
+++ b/gcc/cobol/symfind.cc
@@ -45,6 +45,34 @@
 
 extern int yydebug;
 
+/*
+ * This silly function accepts __PRETTY_FUNCTION__ and trims the return type
+ * and parameter list from the name.  It's used only for debug messages.  It
+ * will not DTRT for conversion operators e.g. operator bool() or others with
+ * spaces in their names.
+ */
+struct funcname {
+  std::string output;
+  funcname( const char name[] ) {
+    auto ename = name + strlen(name);
+    auto p = std::find(name, ename, ' ');
+    p = p == ename? name : p + 1;
+    auto pend = std::find(p, ename, '(');
+    std::reverse_iterator<const char *> rbeg(pend), rend(p);
+    // Find the last character that could not by part of the function name.
+    const std::string stop(" *&");
+    auto rp = std::find_if( rbeg, rend, 
+                            [stop]( char ch ) {
+                              return stop.find(ch) != std::string::npos;
+                            } );
+    if( rp != rend ) p = rp.base();
+
+    assert(p < pend && pend < ename);
+    output.assign(p, pend);
+  }
+};
+#define __funcsig__ funcname(__PRETTY_FUNCTION__).output.c_str()
+
 static bool
 is_data_field( symbol_elem_t& e ) {
   if( e.type != SymField ) return false;
@@ -56,10 +84,10 @@ is_data_field( symbol_elem_t& e ) {
 }
 
 class sym_name_t {
-public: // TEMPORARY
-  const char *name;
-  size_t program, parent;
 public:
+  const char *name;
+  const size_t program, parent;
+
   explicit sym_name_t( const char name[] )
     : name(name), program(0), parent(0) { assert(name[0] == '\0'); }
   sym_name_t( size_t program, const char name[], size_t parent )
@@ -86,12 +114,60 @@ public:
 
 typedef std::map< sym_name_t, std::vector<size_t> > symbol_map_t;
 
-
 static symbol_map_t symbol_map;
 
 typedef std::map <field_key_t, std::list<size_t> > field_keymap_t;
 static field_keymap_t symbol_map2;
 
+/*
+ * symbol_file_names_t is a small "lookaside" map of FD name to the file's
+ * default buffer. It's a little bit too generous.  It works correctly for
+ * CALL, but because it's used by the parser's name: nonterminal, everything
+ * that wants a cbl_field_t gets it.  So MOVE would also work.
+ *
+ * To make it convenient for the parser to verify it's not relying on an FD
+ * name, the was_fd_name() function calls symbol_file_names_t::exists.
+ */
+static class symbol_file_names_t : protected symbol_map_t {
+  std::set<size_t> named_defaults;
+ public:
+  void add( const cbl_file_t& file ) {
+    auto program = symbol_elem_of(&file)->program;
+    std::vector<size_t> ids( 1, file.default_record );
+    sym_name_t key(program, file.name, 0);
+    (*this)[key] = ids;
+    // Keep track of symbols we added, so the parser can ask. 
+    auto f = cbl_field_of(symbol_at(ids.front()));
+    assert('_' == f->name[0]); // not a COBOL name
+    named_defaults.insert(ids.front());
+  }
+  symbol_map_t find( size_t program, const char name[] ) const {
+    symbol_map_t output;
+    std::copy_if( cbegin(), cend(),
+                  std::inserter(output, output.begin()), 
+                  [program, name]( const auto& elem ) {
+                    return match(program, name, elem);
+                  } );
+    
+    dbgmsg("%s:%d: found %lu #%lu for %s", __funcsig__, __LINE__,
+           (unsigned long)output.size(),
+           output.empty()? 0ul : (unsigned long)output.begin()->second.front(),
+           name);
+           
+    return output;
+  }
+  bool exists( const cbl_field_t * field ) const {
+    auto isym = symbol_index(symbol_elem_of(field));
+    return 1 == named_defaults.count(isym);
+  }
+ protected:
+  static bool match( size_t program, const char *name, const_reference elem ) {
+    const sym_name_t& key = elem.first;
+    return key.program == program
+      &&   0 == strcasecmp(key.name, name);
+  }
+} symbol_file_names;
+
 /*
  * As each field is added to the symbol table, add its name and index to the
  * name map.  Initially the type is FldInvalid.  Those are removed by
@@ -118,6 +194,36 @@ update_symbol_map2( const symbol_elem_t *e ) {
   symbol_map2[fk].push_back(symbol_index(e));
 }
 
+void
+update_symbol_map2( const cbl_file_t& file ) {
+  assert(file.default_record);
+  auto e = symbol_elem_of(&file);
+
+  symbol_file_names.add(file);
+
+  sym_name_t key( e->program, file.name, 0 );
+  auto m = symbol_file_names.find(e->program, file.name);
+  dbgmsg("%s:%d: %s => %lu (last of %lu)", __func__, __LINE__,
+         key.name, m[key].back(), m[key].size() );
+}
+
+/*
+ * The field may have been accepted as an FD NAME, representing the file's
+ * buffer.  That is valid for a CALL parameter, for example, but not otherwise,
+ * as in MOVE.
+ *
+ * When parsing a name nonterminal, we smuggle in the file's default buffer if
+ * an FD name is referenced (and unique).  The function below exists to check
+ * against that possibility in cases where it's not allowed.
+ */
+bool
+was_fd_name( const cbl_field_t * field ) {
+  if( current_program_index() < field->our_index ) {
+    return symbol_file_names.exists(field);
+  }
+  return false;
+}
+
 /*
  * Purge any field whose type is FldInvalid.  Remove any names that do
  * not map to any field.
@@ -529,6 +635,10 @@ symbol_find( size_t program, std::list<const char *> names ) {
     items = qualified;
   }
 
+  if( items.empty() && names.size() == 1 ) {
+    items = symbol_file_names.find(program, names.front());
+  }
+
   auto unique = items.size() == 1;
 
   if( ! unique ) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.