[flang-commits] [flang] 79f6b81 - [flang][runtime] Corrections to formatted child I/O

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Fri Mar 10 10:21:01 PST 2023


Author: Peter Klausler
Date: 2023-03-10T10:09:44-08:00
New Revision: 79f6b812356880f64975998fda72d06d46735ad6

URL: https://github.com/llvm/llvm-project/commit/79f6b812356880f64975998fda72d06d46735ad6
DIFF: https://github.com/llvm/llvm-project/commit/79f6b812356880f64975998fda72d06d46735ad6.diff

LOG: [flang][runtime] Corrections to formatted child I/O

A handful of I/O statements (OPEN, CLOSE, positioning) are not allowed
on units during child I/O; catch violations and report errors.
Also finesse error handling during FORMAT runtime parsing of DT
derived type edit descriptors, and ensure that formatted child
I/O is nonadvancing.

Differential Revision: https://reviews.llvm.org/D145751

Added: 
    

Modified: 
    flang/include/flang/Runtime/iostat.h
    flang/runtime/descriptor-io.cpp
    flang/runtime/format-implementation.h
    flang/runtime/format.h
    flang/runtime/io-api.cpp
    flang/runtime/io-error.cpp
    flang/runtime/iostat.cpp
    flang/runtime/unit.h
    flang/unittests/Runtime/Format.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Runtime/iostat.h b/flang/include/flang/Runtime/iostat.h
index 6d6be7c7b8d57..ddb82ce0ca238 100644
--- a/flang/include/flang/Runtime/iostat.h
+++ b/flang/include/flang/Runtime/iostat.h
@@ -82,6 +82,7 @@ enum Iostat {
   IostatBadBackspaceUnit,
   IostatBadUnitNumber,
   IostatBadFlushUnit,
+  IostatBadOpOnChildUnit,
 };
 
 const char *IostatErrorString(int);

diff  --git a/flang/runtime/descriptor-io.cpp b/flang/runtime/descriptor-io.cpp
index c6b57bf5f088d..4a17a53b495a5 100644
--- a/flang/runtime/descriptor-io.cpp
+++ b/flang/runtime/descriptor-io.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "descriptor-io.h"
+#include "flang/Common/restorer.h"
 
 namespace Fortran::runtime::io::descr {
 
@@ -47,6 +48,8 @@ std::optional<bool> DefinedFormattedIo(IoStatementState &io,
       external = &ExternalFileUnit::NewUnit(handler, true);
     }
     ChildIo &child{external->PushChildIo(io)};
+    // Child formatted I/O is nonadvancing by definition (F'2018 12.6.2.4).
+    auto restorer{common::ScopedSet(io.mutableModes().nonAdvancing, true)};
     int unit{external->unitNumber()};
     int ioStat{IostatOk};
     char ioMsg[100];

diff  --git a/flang/runtime/format-implementation.h b/flang/runtime/format-implementation.h
index 630fbf68082e4..89c700eaa4b2d 100644
--- a/flang/runtime/format-implementation.h
+++ b/flang/runtime/format-implementation.h
@@ -64,11 +64,14 @@ FormatControl<CONTEXT>::FormatControl(const Terminator &terminator,
 
 template <typename CONTEXT>
 int FormatControl<CONTEXT>::GetIntField(
-    IoErrorHandler &handler, CharType firstCh) {
+    IoErrorHandler &handler, CharType firstCh, bool *hadError) {
   CharType ch{firstCh ? firstCh : PeekNext()};
   if (ch != '-' && ch != '+' && (ch < '0' || ch > '9')) {
     handler.SignalError(IostatErrorInFormat,
         "Invalid FORMAT: integer expected at '%c'", static_cast<char>(ch));
+    if (hadError) {
+      *hadError = true;
+    }
     return 0;
   }
   int result{0};
@@ -86,6 +89,9 @@ int FormatControl<CONTEXT>::GetIntField(
         std::numeric_limits<int>::max() / 10 - (static_cast<int>(ch) - '0')) {
       handler.SignalError(
           IostatErrorInFormat, "FORMAT integer field out of range");
+      if (hadError) {
+        *hadError = true;
+      }
       return result;
     }
     result = 10 * result + ch - '0';
@@ -99,6 +105,9 @@ int FormatControl<CONTEXT>::GetIntField(
   if (negate && (result *= -1) > 0) {
     handler.SignalError(
         IostatErrorInFormat, "FORMAT integer field out of range");
+    if (hadError) {
+      *hadError = true;
+    }
   }
   return result;
 }
@@ -401,7 +410,7 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
 
 // Returns the next data edit descriptor
 template <typename CONTEXT>
-DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
+std::optional<DataEdit> FormatControl<CONTEXT>::GetNextDataEdit(
     Context &context, int maxRepeat) {
   int repeat{CueUpNextDataEdit(context)};
   auto start{offset_};
@@ -420,7 +429,7 @@ DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
     if (auto quote{static_cast<char>(PeekNext())};
         quote == '\'' || quote == '"') {
       // Capture the quoted 'iotype'
-      bool ok{false}, tooLong{false};
+      bool ok{false};
       for (++offset_; offset_ < formatLength_;) {
         auto ch{static_cast<char>(format_[offset_++])};
         if (ch == quote &&
@@ -428,31 +437,36 @@ DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
                 static_cast<char>(format_[offset_]) != quote)) {
           ok = true;
           break; // that was terminating quote
-        } else if (edit.ioTypeChars >= edit.maxIoTypeChars) {
-          tooLong = true;
-        } else {
-          edit.ioType[edit.ioTypeChars++] = ch;
-          if (ch == quote) {
-            ++offset_;
-          }
+        }
+        if (edit.ioTypeChars >= edit.maxIoTypeChars) {
+          ReportBadFormat(context, "Excessive DT'iotype' in FORMAT", start);
+          return std::nullopt;
+        }
+        edit.ioType[edit.ioTypeChars++] = ch;
+        if (ch == quote) {
+          ++offset_;
         }
       }
       if (!ok) {
         ReportBadFormat(context, "Unclosed DT'iotype' in FORMAT", start);
-      } else if (tooLong) {
-        ReportBadFormat(context, "Excessive DT'iotype' in FORMAT", start);
+        return std::nullopt;
       }
     }
     if (PeekNext() == '(') {
       // Capture the v_list arguments
-      bool ok{false}, tooLong{false};
+      bool ok{false};
       for (++offset_; offset_ < formatLength_;) {
-        int n{GetIntField(context)};
+        bool hadError{false};
+        int n{GetIntField(context, '\0', &hadError)};
+        if (hadError) {
+          ok = false;
+          break;
+        }
         if (edit.vListEntries >= edit.maxVListEntries) {
-          tooLong = true;
-        } else {
-          edit.vList[edit.vListEntries++] = n;
+          ReportBadFormat(context, "Excessive DT(v_list) in FORMAT", start);
+          return std::nullopt;
         }
+        edit.vList[edit.vListEntries++] = n;
         auto ch{static_cast<char>(GetNextChar(context))};
         if (ch != ',') {
           ok = ch == ')';
@@ -461,8 +475,7 @@ DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
       }
       if (!ok) {
         ReportBadFormat(context, "Unclosed DT(v_list) in FORMAT", start);
-      } else if (tooLong) {
-        ReportBadFormat(context, "Excessive DT(v_list) in FORMAT", start);
+        return std::nullopt;
       }
     }
   }

diff  --git a/flang/runtime/format.h b/flang/runtime/format.h
index 718a2677c14c9..9077a849eaec6 100644
--- a/flang/runtime/format.h
+++ b/flang/runtime/format.h
@@ -102,7 +102,7 @@ template <typename CONTEXT> class FormatControl {
   // Extracts the next data edit descriptor, handling control edit descriptors
   // along the way.  If maxRepeat==0, this is a peek at the next data edit
   // descriptor.
-  DataEdit GetNextDataEdit(Context &, int maxRepeat = 1);
+  std::optional<DataEdit> GetNextDataEdit(Context &, int maxRepeat = 1);
 
   // Emit any remaining character literals after the last data item (on output)
   // and perform remaining record positioning actions.
@@ -142,7 +142,8 @@ template <typename CONTEXT> class FormatControl {
     }
     return format_[offset_++];
   }
-  int GetIntField(IoErrorHandler &, CharType firstCh = '\0');
+  int GetIntField(
+      IoErrorHandler &, CharType firstCh = '\0', bool *hadError = nullptr);
 
   // Advances through the FORMAT until the next data edit
   // descriptor has been found; handles control edit descriptors

diff  --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp
index 197cfcc2f7f3d..faf062cd8f596 100644
--- a/flang/runtime/io-api.cpp
+++ b/flang/runtime/io-api.cpp
@@ -374,8 +374,14 @@ Cookie IONAME(BeginOpenUnit)( // OPEN(without NEWUNIT=)
   if (ExternalFileUnit *
       unit{ExternalFileUnit::LookUpOrCreate(
           unitNumber, terminator, wasExtant)}) {
-    return &unit->BeginIoStatement<OpenStatementState>(
-        terminator, *unit, wasExtant, sourceFile, sourceLine);
+    if (ChildIo * child{unit->GetChildIo()}) {
+      return &child->BeginIoStatement<ErroneousIoStatementState>(
+          IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
+          sourceLine);
+    } else {
+      return &unit->BeginIoStatement<OpenStatementState>(
+          terminator, *unit, wasExtant, sourceFile, sourceLine);
+    }
   } else {
     return NoopUnit(terminator, unitNumber, IostatBadUnitNumber);
   }
@@ -414,6 +420,13 @@ Cookie IONAME(BeginWaitAll)(
 Cookie IONAME(BeginClose)(
     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
   Terminator terminator{sourceFile, sourceLine};
+  if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
+    if (ChildIo * child{unit->GetChildIo()}) {
+      return &child->BeginIoStatement<ErroneousIoStatementState>(
+          IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
+          sourceLine);
+    }
+  }
   if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) {
     return &unit->BeginIoStatement<CloseStatementState>(
         terminator, *unit, sourceFile, sourceLine);
@@ -427,8 +440,13 @@ Cookie IONAME(BeginFlush)(
     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
   Terminator terminator{sourceFile, sourceLine};
   if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
-    return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
-        *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
+    if (ChildIo * child{unit->GetChildIo()}) {
+      return &child->BeginIoStatement<ExternalMiscIoStatementState>(
+          *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
+    } else {
+      return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
+          *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
+    }
   } else {
     // FLUSH(UNIT=bad unit) is an error; an unconnected unit is a no-op
     return NoopUnit(terminator, unitNumber,
@@ -440,8 +458,15 @@ Cookie IONAME(BeginBackspace)(
     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
   Terminator terminator{sourceFile, sourceLine};
   if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
-    return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
-        *unit, ExternalMiscIoStatementState::Backspace, sourceFile, sourceLine);
+    if (ChildIo * child{unit->GetChildIo()}) {
+      return &child->BeginIoStatement<ErroneousIoStatementState>(
+          IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
+          sourceLine);
+    } else {
+      return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
+          *unit, ExternalMiscIoStatementState::Backspace, sourceFile,
+          sourceLine);
+    }
   } else {
     return NoopUnit(terminator, unitNumber, IostatBadBackspaceUnit);
   }
@@ -454,8 +479,14 @@ Cookie IONAME(BeginEndfile)(
   if (ExternalFileUnit *
       unit{GetOrCreateUnit(unitNumber, Direction::Output, std::nullopt,
           terminator, errorCookie)}) {
-    return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
-        *unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
+    if (ChildIo * child{unit->GetChildIo()}) {
+      return &child->BeginIoStatement<ErroneousIoStatementState>(
+          IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
+          sourceLine);
+    } else {
+      return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
+          *unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
+    }
   } else {
     return errorCookie;
   }
@@ -468,8 +499,14 @@ Cookie IONAME(BeginRewind)(
   if (ExternalFileUnit *
       unit{GetOrCreateUnit(unitNumber, Direction::Input, std::nullopt,
           terminator, errorCookie)}) {
-    return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
-        *unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
+    if (ChildIo * child{unit->GetChildIo()}) {
+      return &child->BeginIoStatement<ErroneousIoStatementState>(
+          IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
+          sourceLine);
+    } else {
+      return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
+          *unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
+    }
   } else {
     return errorCookie;
   }
@@ -504,8 +541,13 @@ Cookie IONAME(BeginInquireFile)(const char *path, std::size_t pathLength,
       unit{ExternalFileUnit::LookUp(
           trimmed.get(), std::strlen(trimmed.get()))}) {
     // INQUIRE(FILE=) to a connected unit
-    return &unit->BeginIoStatement<InquireUnitState>(
-        terminator, *unit, sourceFile, sourceLine);
+    if (ChildIo * child{unit->GetChildIo()}) {
+      return &child->BeginIoStatement<InquireUnitState>(
+          *unit, sourceFile, sourceLine);
+    } else {
+      return &unit->BeginIoStatement<InquireUnitState>(
+          terminator, *unit, sourceFile, sourceLine);
+    }
   } else {
     return &New<InquireUnconnectedFileState>{terminator}(
         std::move(trimmed), sourceFile, sourceLine)
@@ -566,7 +608,12 @@ bool IONAME(SetAdvance)(
   if (nonAdvancing && io.GetConnectionState().access == Access::Direct) {
     handler.SignalError("Non-advancing I/O attempted on direct access file");
   } else {
-    io.mutableModes().nonAdvancing = nonAdvancing;
+    auto *unit{io.GetExternalFileUnit()};
+    if (unit && unit->GetChildIo()) {
+      // ADVANCE= is ignored for child I/O (12.6.4.8.3 p3)
+    } else {
+      io.mutableModes().nonAdvancing = nonAdvancing;
+    }
   }
   return !handler.InError();
 }
@@ -648,7 +695,12 @@ bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) {
   IoStatementState &io{*cookie};
   IoErrorHandler &handler{io.GetIoErrorHandler()};
   if (auto *unit{io.GetExternalFileUnit()}) {
-    unit->SetDirectRec(rec, handler);
+    if (unit->GetChildIo()) {
+      handler.SignalError(
+          IostatBadOpOnChildUnit, "REC= specifier on child I/O");
+    } else {
+      unit->SetDirectRec(rec, handler);
+    }
   } else if (!io.get_if<ErroneousIoStatementState>()) {
     handler.Crash("SetRec() called on internal unit");
   }

diff  --git a/flang/runtime/io-error.cpp b/flang/runtime/io-error.cpp
index 790c579e1c43c..e333f6d75bc4d 100644
--- a/flang/runtime/io-error.cpp
+++ b/flang/runtime/io-error.cpp
@@ -59,13 +59,12 @@ void IoErrorHandler::SignalError(int iostatOrErrno) {
 
 void IoErrorHandler::Forward(
     int ioStatOrErrno, const char *msg, std::size_t length) {
-  if (ioStat_ != IostatOk && msg && (flags_ & hasIoMsg)) {
-    ioMsg_ = SaveDefaultCharacter(msg, length, *this);
-  }
-  if (ioStatOrErrno != IostatOk && msg) {
-    SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg);
-  } else {
-    SignalError(ioStatOrErrno);
+  if (ioStatOrErrno != IostatOk) {
+    if (msg) {
+      SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg);
+    } else {
+      SignalError(ioStatOrErrno);
+    }
   }
 }
 

diff  --git a/flang/runtime/iostat.cpp b/flang/runtime/iostat.cpp
index 747a776aae7cc..f34bde28be9d0 100644
--- a/flang/runtime/iostat.cpp
+++ b/flang/runtime/iostat.cpp
@@ -109,6 +109,8 @@ const char *IostatErrorString(int iostat) {
     return "Negative unit number is not allowed";
   case IostatBadFlushUnit:
     return "FLUSH attempted on a bad or unconnected unit number";
+  case IostatBadOpOnChildUnit:
+    return "Impermissible I/O statement on child I/O unit";
   default:
     return nullptr;
   }

diff  --git a/flang/runtime/unit.h b/flang/runtime/unit.h
index c49f479830338..aad896afce513 100644
--- a/flang/runtime/unit.h
+++ b/flang/runtime/unit.h
@@ -203,7 +203,7 @@ class ChildIo {
       ChildListIoStatementState<Direction::Input>,
       ChildUnformattedIoStatementState<Direction::Output>,
       ChildUnformattedIoStatementState<Direction::Input>, InquireUnitState,
-      ErroneousIoStatementState>
+      ErroneousIoStatementState, ExternalMiscIoStatementState>
       u_;
   std::optional<IoStatementState> io_;
 };

diff  --git a/flang/unittests/Runtime/Format.cpp b/flang/unittests/Runtime/Format.cpp
index 970373043131a..1f55b39d59053 100644
--- a/flang/unittests/Runtime/Format.cpp
+++ b/flang/unittests/Runtime/Format.cpp
@@ -10,6 +10,7 @@
 #include "../runtime/connection.h"
 #include "../runtime/format-implementation.h"
 #include "../runtime/io-error.h"
+#include <optional>
 #include <string>
 #include <tuple>
 #include <vector>
@@ -29,7 +30,7 @@ class TestFormatContext : public IoErrorHandler {
   bool AdvanceRecord(int = 1);
   void HandleRelativePosition(std::int64_t);
   void HandleAbsolutePosition(std::int64_t);
-  void Report(const DataEdit &);
+  void Report(const std::optional<DataEdit> &);
   ResultsTy results;
   MutableModes &mutableModes() { return mutableModes_; }
   ConnectionState &GetConnectionState() { return connectionState_; }
@@ -64,25 +65,29 @@ void TestFormatContext::HandleRelativePosition(std::int64_t n) {
   }
 }
 
-void TestFormatContext::Report(const DataEdit &edit) {
-  std::string str{edit.descriptor};
-  if (edit.repeat != 1) {
-    str = std::to_string(edit.repeat) + '*' + str;
-  }
-  if (edit.variation) {
-    str += edit.variation;
-  }
-  if (edit.width) {
-    str += std::to_string(*edit.width);
-  }
-  if (edit.digits) {
-    str += "."s + std::to_string(*edit.digits);
-  }
-  if (edit.expoDigits) {
-    str += "E"s + std::to_string(*edit.expoDigits);
+void TestFormatContext::Report(const std::optional<DataEdit> &edit) {
+  if (edit) {
+    std::string str{edit->descriptor};
+    if (edit->repeat != 1) {
+      str = std::to_string(edit->repeat) + '*' + str;
+    }
+    if (edit->variation) {
+      str += edit->variation;
+    }
+    if (edit->width) {
+      str += std::to_string(*edit->width);
+    }
+    if (edit->digits) {
+      str += "."s + std::to_string(*edit->digits);
+    }
+    if (edit->expoDigits) {
+      str += "E"s + std::to_string(*edit->expoDigits);
+    }
+    // modes?
+    results.push_back(str);
+  } else {
+    results.push_back("(nullopt)"s);
   }
-  // modes?
-  results.push_back(str);
 }
 
 struct FormatTests : public CrashHandlerFixture {};


        


More information about the flang-commits mailing list