[flang-commits] [flang] df38f35 - [flang] Allow data transfer stmt control list errors to be caught

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Tue Mar 1 14:39:40 PST 2022


Author: Peter Klausler
Date: 2022-03-01T14:39:30-08:00
New Revision: df38f35acb56b63df23d8645e0f8d791cfe74c83

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

LOG: [flang] Allow data transfer stmt control list errors to be caught

The runtime crashes on several fundamental I/O data transfer statement
control list errors, like list I/O on a direct-access unit, or
input from a write-only unit, &c.  These errors should not be fatal
when ERR= or IOSTAT= are present.

This patch creates a new ErroneousIoStatementState class and
uses it for the state of an I/O statement that is doomed to fail
from these errors.  If there is no ERR= label or IOSTAT= variable,
the error will be raised at the end of the statement.  Data transfer
operations along the way will be no-op failures.

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

Added: 
    

Modified: 
    flang/include/flang/Runtime/iostat.h
    flang/runtime/io-api.cpp
    flang/runtime/io-stmt.cpp
    flang/runtime/io-stmt.h
    flang/runtime/iostat.cpp
    flang/runtime/unit.cpp
    flang/runtime/unit.h

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Runtime/iostat.h b/flang/include/flang/Runtime/iostat.h
index 07a25c420425b..06d6fe7ae3dd4 100644
--- a/flang/include/flang/Runtime/iostat.h
+++ b/flang/include/flang/Runtime/iostat.h
@@ -56,6 +56,13 @@ enum Iostat {
   IostatBackspaceAtFirstRecord,
   IostatRewindNonSequential,
   IostatWriteAfterEndfile,
+  IostatFormattedIoOnUnformattedUnit,
+  IostatUnformattedIoOnFormattedUnit,
+  IostatListIoOnDirectAccessUnit,
+  IostatUnformattedChildOnFormattedParent,
+  IostatFormattedChildOnUnformattedParent,
+  IostatChildInputFromOutputParent,
+  IostatChildOutputToInputParent,
 };
 
 const char *IostatErrorString(int);

diff  --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp
index 75695af5a1a12..e097867518c73 100644
--- a/flang/runtime/io-api.cpp
+++ b/flang/runtime/io-api.cpp
@@ -148,8 +148,8 @@ Cookie IONAME(BeginInternalFormattedInput)(const char *internal,
 }
 
 template <Direction DIR, template <Direction> class STATE, typename... A>
-Cookie BeginExternalListIO(const char *what, int unitNumber,
-    const char *sourceFile, int sourceLine, A &&...xs) {
+Cookie BeginExternalListIO(
+    int unitNumber, const char *sourceFile, int sourceLine, A &&...xs) {
   Terminator terminator{sourceFile, sourceLine};
   if (unitNumber == DefaultUnit) {
     unitNumber = DIR == Direction::Input ? 5 : 6;
@@ -159,38 +159,48 @@ Cookie BeginExternalListIO(const char *what, int unitNumber,
   if (!unit.isUnformatted.has_value()) {
     unit.isUnformatted = false;
   }
+  Iostat iostat{IostatOk};
   if (*unit.isUnformatted) {
-    terminator.Crash("%s attempted on unformatted file", what);
-    return nullptr;
+    iostat = IostatFormattedIoOnUnformattedUnit;
   }
   if (ChildIo * child{unit.GetChildIo()}) {
-    return child->CheckFormattingAndDirection(terminator, what, false, DIR)
-        ? &child->BeginIoStatement<ChildListIoStatementState<DIR>>(
-              *child, sourceFile, sourceLine)
-        : nullptr;
+    if (iostat == IostatOk) {
+      iostat = child->CheckFormattingAndDirection(false, DIR);
+    }
+    if (iostat == IostatOk) {
+      return &child->BeginIoStatement<ChildListIoStatementState<DIR>>(
+          *child, sourceFile, sourceLine);
+    } else {
+      return &child->BeginIoStatement<ErroneousIoStatementState>(
+          iostat, sourceFile, sourceLine);
+    }
   } else {
-    if (unit.access == Access::Direct) {
-      terminator.Crash("%s attempted on direct access file", what);
-      return nullptr;
+    if (iostat == IostatOk && unit.access == Access::Direct) {
+      iostat = IostatListIoOnDirectAccessUnit;
+    }
+    if (iostat == IostatOk) {
+      iostat = unit.SetDirection(DIR);
+    }
+    if (iostat == IostatOk) {
+      return &unit.BeginIoStatement<STATE<DIR>>(
+          std::forward<A>(xs)..., unit, sourceFile, sourceLine);
+    } else {
+      return &unit.BeginIoStatement<ErroneousIoStatementState>(
+          iostat, sourceFile, sourceLine);
     }
-    IoErrorHandler handler{terminator};
-    unit.SetDirection(DIR, handler);
-    IoStatementState &io{unit.BeginIoStatement<STATE<DIR>>(
-        std::forward<A>(xs)..., unit, sourceFile, sourceLine)};
-    return &io;
   }
 }
 
 Cookie IONAME(BeginExternalListOutput)(
     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
   return BeginExternalListIO<Direction::Output, ExternalListIoStatementState>(
-      "List-directed output", unitNumber, sourceFile, sourceLine);
+      unitNumber, sourceFile, sourceLine);
 }
 
 Cookie IONAME(BeginExternalListInput)(
     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
   return BeginExternalListIO<Direction::Input, ExternalListIoStatementState>(
-      "List-directed input", unitNumber, sourceFile, sourceLine);
+      unitNumber, sourceFile, sourceLine);
 }
 
 template <Direction DIR>
@@ -202,28 +212,35 @@ Cookie BeginExternalFormattedIO(const char *format, std::size_t formatLength,
   }
   ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
       unitNumber, DIR, false /*!unformatted*/, terminator)};
+  Iostat iostat{IostatOk};
   if (!unit.isUnformatted.has_value()) {
     unit.isUnformatted = false;
   }
   if (*unit.isUnformatted) {
-    terminator.Crash("Formatted I/O attempted on unformatted file");
-    return nullptr;
+    iostat = IostatFormattedIoOnUnformattedUnit;
   }
   if (ChildIo * child{unit.GetChildIo()}) {
-    return child->CheckFormattingAndDirection(terminator,
-               DIR == Direction::Output ? "formatted output"
-                                        : "formatted input",
-               false, DIR)
-        ? &child->BeginIoStatement<ChildFormattedIoStatementState<DIR>>(
-              *child, format, formatLength, sourceFile, sourceLine)
-        : nullptr;
+    if (iostat == IostatOk) {
+      iostat = child->CheckFormattingAndDirection(false, DIR);
+    }
+    if (iostat == IostatOk) {
+      return &child->BeginIoStatement<ChildFormattedIoStatementState<DIR>>(
+          *child, format, formatLength, sourceFile, sourceLine);
+    } else {
+      return &child->BeginIoStatement<ErroneousIoStatementState>(
+          iostat, sourceFile, sourceLine);
+    }
   } else {
-    IoErrorHandler handler{terminator};
-    unit.SetDirection(DIR, handler);
-    IoStatementState &io{
-        unit.BeginIoStatement<ExternalFormattedIoStatementState<DIR>>(
-            unit, format, formatLength, sourceFile, sourceLine)};
-    return &io;
+    if (iostat == IostatOk) {
+      iostat = unit.SetDirection(DIR);
+    }
+    if (iostat == IostatOk) {
+      return &unit.BeginIoStatement<ExternalFormattedIoStatementState<DIR>>(
+          unit, format, formatLength, sourceFile, sourceLine);
+    } else {
+      return &unit.BeginIoStatement<ErroneousIoStatementState>(
+          iostat, sourceFile, sourceLine);
+    }
   }
 }
 
@@ -247,35 +264,45 @@ Cookie BeginUnformattedIO(
   Terminator terminator{sourceFile, sourceLine};
   ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
       unitNumber, DIR, true /*unformatted*/, terminator)};
+  Iostat iostat{IostatOk};
   if (!unit.isUnformatted.has_value()) {
     unit.isUnformatted = true;
   }
   if (!*unit.isUnformatted) {
-    terminator.Crash("Unformatted I/O attempted on formatted file");
+    iostat = IostatUnformattedIoOnFormattedUnit;
   }
   if (ChildIo * child{unit.GetChildIo()}) {
-    return child->CheckFormattingAndDirection(terminator,
-               DIR == Direction::Output ? "unformatted output"
-                                        : "unformatted input",
-               true, DIR)
-        ? &child->BeginIoStatement<ChildUnformattedIoStatementState<DIR>>(
-              *child, sourceFile, sourceLine)
-        : nullptr;
+    if (iostat == IostatOk) {
+      iostat = child->CheckFormattingAndDirection(true, DIR);
+    }
+    if (iostat == IostatOk) {
+      return &child->BeginIoStatement<ChildUnformattedIoStatementState<DIR>>(
+          *child, sourceFile, sourceLine);
+    } else {
+      return &child->BeginIoStatement<ErroneousIoStatementState>(
+          iostat, sourceFile, sourceLine);
+    }
   } else {
-    IoStatementState &io{
-        unit.BeginIoStatement<ExternalUnformattedIoStatementState<DIR>>(
-            unit, sourceFile, sourceLine)};
-    IoErrorHandler handler{terminator};
-    unit.SetDirection(DIR, handler);
-    if constexpr (DIR == Direction::Output) {
-      if (unit.access == Access::Sequential) {
-        // Create space for (sub)record header to be completed by
-        // ExternalFileUnit::AdvanceRecord()
-        unit.recordLength.reset(); // in case of prior BACKSPACE
-        io.Emit("\0\0\0\0", 4); // placeholder for record length header
+    if (iostat == IostatOk) {
+      iostat = unit.SetDirection(DIR);
+    }
+    if (iostat == IostatOk) {
+      IoStatementState &io{
+          unit.BeginIoStatement<ExternalUnformattedIoStatementState<DIR>>(
+              unit, sourceFile, sourceLine)};
+      if constexpr (DIR == Direction::Output) {
+        if (unit.access == Access::Sequential) {
+          // Create space for (sub)record header to be completed by
+          // ExternalFileUnit::AdvanceRecord()
+          unit.recordLength.reset(); // in case of prior BACKSPACE
+          io.Emit("\0\0\0\0", 4); // placeholder for record length header
+        }
       }
+      return &io;
+    } else {
+      return &unit.BeginIoStatement<ErroneousIoStatementState>(
+          iostat, sourceFile, sourceLine);
     }
-    return &io;
   }
 }
 

diff  --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp
index 7d447bafa880e..4f47e36fe78f7 100644
--- a/flang/runtime/io-stmt.cpp
+++ b/flang/runtime/io-stmt.cpp
@@ -1130,7 +1130,7 @@ bool InquireUnitState::Inquire(
     } else if (unit().openRecl) {
       result = *unit().openRecl;
     } else {
-      result = std::numeric_limits<std::uint32_t>::max();
+      result = std::numeric_limits<std::int32_t>::max();
     }
     return true;
   case HashInquiryKeyword("SIZE"):
@@ -1360,4 +1360,9 @@ bool InquireIOLengthState::Emit(const char32_t *p, std::size_t n) {
   return true;
 }
 
+int ErroneousIoStatementState::EndIoStatement() {
+  SignalError(iostat_);
+  return IoStatementBase::EndIoStatement();
+}
+
 } // namespace Fortran::runtime::io

diff  --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h
index 59d4e50460fba..75cf327f05d7c 100644
--- a/flang/runtime/io-stmt.h
+++ b/flang/runtime/io-stmt.h
@@ -35,6 +35,7 @@ class InquireIOLengthState;
 class ExternalMiscIoStatementState;
 class CloseStatementState;
 class NoopStatementState; // CLOSE or FLUSH on unknown unit
+class ErroneousIoStatementState;
 
 template <Direction, typename CHAR = char>
 class InternalFormattedIoStatementState;
@@ -223,7 +224,8 @@ class IoStatementState {
       std::reference_wrapper<InquireNoUnitState>,
       std::reference_wrapper<InquireUnconnectedFileState>,
       std::reference_wrapper<InquireIOLengthState>,
-      std::reference_wrapper<ExternalMiscIoStatementState>>
+      std::reference_wrapper<ExternalMiscIoStatementState>,
+      std::reference_wrapper<ErroneousIoStatementState>>
       u_;
 };
 
@@ -674,5 +676,19 @@ class ExternalMiscIoStatementState : public ExternalIoStatementBase {
   Which which_;
 };
 
+class ErroneousIoStatementState : public IoStatementBase {
+public:
+  explicit ErroneousIoStatementState(
+      Iostat iostat, const char *sourceFile = nullptr, int sourceLine = 0)
+      : IoStatementBase{sourceFile, sourceLine}, iostat_{iostat} {}
+  int EndIoStatement();
+  ConnectionState &GetConnectionState() { return connection_; }
+  MutableModes &mutableModes() { return connection_.modes; }
+
+private:
+  Iostat iostat_;
+  ConnectionState connection_;
+};
+
 } // namespace Fortran::runtime::io
 #endif // FORTRAN_RUNTIME_IO_STMT_H_

diff  --git a/flang/runtime/iostat.cpp b/flang/runtime/iostat.cpp
index 58e3f1b387e14..f1d20c1a23b0b 100644
--- a/flang/runtime/iostat.cpp
+++ b/flang/runtime/iostat.cpp
@@ -55,6 +55,20 @@ const char *IostatErrorString(int iostat) {
     return "REWIND on non-sequential file";
   case IostatWriteAfterEndfile:
     return "WRITE after ENDFILE";
+  case IostatFormattedIoOnUnformattedUnit:
+    return "Formatted I/O on unformatted file";
+  case IostatUnformattedIoOnFormattedUnit:
+    return "Unformatted I/O on formatted file";
+  case IostatListIoOnDirectAccessUnit:
+    return "List-directed or NAMELIST I/O on direct-access file";
+  case IostatUnformattedChildOnFormattedParent:
+    return "Unformatted child I/O on formatted parent unit";
+  case IostatFormattedChildOnUnformattedParent:
+    return "Formatted child I/O on unformatted parent unit";
+  case IostatChildInputFromOutputParent:
+    return "Child input from output parent unit";
+  case IostatChildOutputToInputParent:
+    return "Child output to input parent unit";
   default:
     return nullptr;
   }

diff  --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp
index b74b3ebf4178d..971909b74e769 100644
--- a/flang/runtime/unit.cpp
+++ b/flang/runtime/unit.cpp
@@ -181,25 +181,20 @@ void ExternalFileUnit::DestroyClosed() {
   GetUnitMap().DestroyClosed(*this); // destroys *this
 }
 
-bool ExternalFileUnit::SetDirection(
-    Direction direction, IoErrorHandler &handler) {
+Iostat ExternalFileUnit::SetDirection(Direction direction) {
   if (direction == Direction::Input) {
     if (mayRead()) {
       direction_ = Direction::Input;
-      return true;
+      return IostatOk;
     } else {
-      handler.SignalError(IostatReadFromWriteOnly,
-          "READ(UNIT=%d) with ACTION='WRITE'", unitNumber());
-      return false;
+      return IostatReadFromWriteOnly;
     }
   } else {
     if (mayWrite()) {
       direction_ = Direction::Output;
-      return true;
+      return IostatOk;
     } else {
-      handler.SignalError(IostatWriteToReadOnly,
-          "WRITE(UNIT=%d) with ACTION='READ'", unitNumber());
-      return false;
+      return IostatWriteToReadOnly;
     }
   }
 }
@@ -220,21 +215,21 @@ UnitMap &ExternalFileUnit::GetUnitMap() {
   ExternalFileUnit &out{newUnitMap->LookUpOrCreate(6, terminator, wasExtant)};
   RUNTIME_CHECK(terminator, !wasExtant);
   out.Predefine(1);
-  out.SetDirection(Direction::Output, handler);
+  handler.SignalError(out.SetDirection(Direction::Output));
   out.isUnformatted = false;
   defaultOutput = &out;
 
   ExternalFileUnit &in{newUnitMap->LookUpOrCreate(5, terminator, wasExtant)};
   RUNTIME_CHECK(terminator, !wasExtant);
   in.Predefine(0);
-  in.SetDirection(Direction::Input, handler);
+  handler.SignalError(in.SetDirection(Direction::Input));
   in.isUnformatted = false;
   defaultInput = ∈
 
   ExternalFileUnit &error{newUnitMap->LookUpOrCreate(0, terminator, wasExtant)};
   RUNTIME_CHECK(terminator, !wasExtant);
   error.Predefine(2);
-  error.SetDirection(Direction::Output, handler);
+  handler.SignalError(error.SetDirection(Direction::Output));
   error.isUnformatted = false;
   errorOutput = &error;
 
@@ -872,8 +867,8 @@ void ChildIo::EndIoStatement() {
   u_.emplace<std::monostate>();
 }
 
-bool ChildIo::CheckFormattingAndDirection(Terminator &terminator,
-    const char *what, bool unformatted, Direction direction) {
+Iostat ChildIo::CheckFormattingAndDirection(
+    bool unformatted, Direction direction) {
   bool parentIsInput{!parent_.get_if<IoDirectionState<Direction::Output>>()};
   bool parentIsFormatted{parentIsInput
           ? parent_.get_if<FormattedIoStatementState<Direction::Input>>() !=
@@ -882,15 +877,13 @@ bool ChildIo::CheckFormattingAndDirection(Terminator &terminator,
               nullptr};
   bool parentIsUnformatted{!parentIsFormatted};
   if (unformatted != parentIsUnformatted) {
-    terminator.Crash("Child %s attempted on %s parent I/O unit", what,
-        parentIsUnformatted ? "unformatted" : "formatted");
-    return false;
+    return unformatted ? IostatUnformattedChildOnFormattedParent
+                       : IostatFormattedChildOnUnformattedParent;
   } else if (parentIsInput != (direction == Direction::Input)) {
-    terminator.Crash("Child %s attempted on %s parent I/O unit", what,
-        parentIsInput ? "input" : "output");
-    return false;
+    return parentIsInput ? IostatChildOutputToInputParent
+                         : IostatChildInputFromOutputParent;
   } else {
-    return true;
+    return IostatOk;
   }
 }
 

diff  --git a/flang/runtime/unit.h b/flang/runtime/unit.h
index da6bc4b23d710..c8e65c1f0c4c6 100644
--- a/flang/runtime/unit.h
+++ b/flang/runtime/unit.h
@@ -62,7 +62,7 @@ class ExternalFileUnit : public ConnectionState,
   void CloseUnit(CloseStatus, IoErrorHandler &);
   void DestroyClosed();
 
-  bool SetDirection(Direction, IoErrorHandler &);
+  Iostat SetDirection(Direction);
 
   template <typename A, typename... X>
   IoStatementState &BeginIoStatement(X &&...xs) {
@@ -128,7 +128,7 @@ class ExternalFileUnit : public ConnectionState,
       ExternalListIoStatementState<Direction::Input>,
       ExternalUnformattedIoStatementState<Direction::Output>,
       ExternalUnformattedIoStatementState<Direction::Input>, InquireUnitState,
-      ExternalMiscIoStatementState>
+      ExternalMiscIoStatementState, ErroneousIoStatementState>
       u_;
 
   // Points to the active alternative (if any) in u_ for use as a Cookie
@@ -171,8 +171,7 @@ class ChildIo {
 
   OwningPtr<ChildIo> AcquirePrevious() { return std::move(previous_); }
 
-  bool CheckFormattingAndDirection(
-      Terminator &, const char *what, bool unformatted, Direction);
+  Iostat CheckFormattingAndDirection(bool unformatted, Direction);
 
 private:
   IoStatementState &parent_;
@@ -183,7 +182,8 @@ class ChildIo {
       ChildListIoStatementState<Direction::Output>,
       ChildListIoStatementState<Direction::Input>,
       ChildUnformattedIoStatementState<Direction::Output>,
-      ChildUnformattedIoStatementState<Direction::Input>, InquireUnitState>
+      ChildUnformattedIoStatementState<Direction::Input>, InquireUnitState,
+      ErroneousIoStatementState>
       u_;
   std::optional<IoStatementState> io_;
 };


        


More information about the flang-commits mailing list