[flang] Handle END= situations better in runtime input

Debug the input path for READ statements with END= labels;
don't emit errors when the program can handle them.
BeginReadingRecord() member functions have been made
"bool" for more convenient handling of error cases,
and some code in IoErrorHandler has been cleaned up.

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

GitOrigin-RevId: e81c96d6f8b1cc974f9dbdd47ed82be6be81ae0a
diff --git a/runtime/descriptor-io.h b/runtime/descriptor-io.h
index 514bcff..4947668 100644
--- a/runtime/descriptor-io.h
+++ b/runtime/descriptor-io.h
@@ -220,7 +220,9 @@
     return false;
   }
   if constexpr (DIR == Direction::Input) {
-    io.BeginReadingRecord();
+    if (!io.BeginReadingRecord()) {
+      return false;
+    }
   }
   if (auto *unf{io.get_if<UnformattedIoStatementState<DIR>>()}) {
     std::size_t elementBytes{descriptor.ElementBytes()};
diff --git a/runtime/io-api.cpp b/runtime/io-api.cpp
index 57a52bf..069e57f 100644
--- a/runtime/io-api.cpp
+++ b/runtime/io-api.cpp
@@ -894,6 +894,9 @@
     Cookie cookie, char *x, std::size_t length, std::size_t elementBytes) {
   IoStatementState &io{*cookie};
   io.BeginReadingRecord();
+  if (io.GetIoErrorHandler().InError()) {
+    return false;
+  }
   if (auto *unf{io.get_if<UnformattedIoStatementState<Direction::Input>>()}) {
     return unf->Receive(x, length, elementBytes);
   }
@@ -1037,7 +1040,7 @@
 
 void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) {
   IoErrorHandler &handler{cookie->GetIoErrorHandler()};
-  if (handler.GetIoStat()) { // leave "msg" alone when no error
+  if (handler.InError()) { // leave "msg" alone when no error
     handler.GetIoMsg(msg, length);
   }
 }
diff --git a/runtime/io-error.cpp b/runtime/io-error.cpp
index 5869672..f73272a 100644
--- a/runtime/io-error.cpp
+++ b/runtime/io-error.cpp
@@ -17,20 +17,13 @@
 
 namespace Fortran::runtime::io {
 
-void IoErrorHandler::Begin(const char *sourceFileName, int sourceLine) {
-  flags_ = 0;
-  ioStat_ = 0;
-  ioMsg_.reset();
-  SetLocation(sourceFileName, sourceLine);
-}
-
 void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) {
   if (iostatOrErrno == IostatEnd && (flags_ & hasEnd)) {
-    if (!ioStat_ || ioStat_ < IostatEnd) {
+    if (ioStat_ == IostatOk || ioStat_ < IostatEnd) {
       ioStat_ = IostatEnd;
     }
   } else if (iostatOrErrno == IostatEor && (flags_ & hasEor)) {
-    if (!ioStat_ || ioStat_ < IostatEor) {
+    if (!ioStat_ == IostatOk || ioStat_ < IostatEor) {
       ioStat_ = IostatEor; // least priority
     }
   } else if (iostatOrErrno != IostatOk) {
diff --git a/runtime/io-error.h b/runtime/io-error.h
index 5dd7f5e..e51df9b 100644
--- a/runtime/io-error.h
+++ b/runtime/io-error.h
@@ -27,14 +27,13 @@
 public:
   using Terminator::Terminator;
   explicit IoErrorHandler(const Terminator &that) : Terminator{that} {}
-  void Begin(const char *sourceFileName, int sourceLine);
   void HasIoStat() { flags_ |= hasIoStat; }
   void HasErrLabel() { flags_ |= hasErr; }
   void HasEndLabel() { flags_ |= hasEnd; }
   void HasEorLabel() { flags_ |= hasEor; }
   void HasIoMsg() { flags_ |= hasIoMsg; }
 
-  bool InError() const { return ioStat_ != 0; }
+  bool InError() const { return ioStat_ != IostatOk; }
 
   void SignalError(int iostatOrErrno, const char *msg, ...);
   void SignalError(int iostatOrErrno);
@@ -58,7 +57,7 @@
     hasIoMsg = 16, // IOMSG=
   };
   std::uint8_t flags_{0};
-  int ioStat_{0};
+  int ioStat_{IostatOk};
   OwningPtr<char> ioMsg_;
 };
 
diff --git a/runtime/io-stmt.cpp b/runtime/io-stmt.cpp
index c04013d..2fd6090 100644
--- a/runtime/io-stmt.cpp
+++ b/runtime/io-stmt.cpp
@@ -230,7 +230,7 @@
 
 template <Direction DIR> int ExternalIoStatementState<DIR>::EndIoStatement() {
   if constexpr (DIR == Direction::Input) {
-    BeginReadingRecord(); // in case of READ with no data items
+    BeginReadingRecord(); // in case there were no I/O items
     if (!unit().nonAdvancing) {
       FinishReadingRecord();
     }
@@ -310,12 +310,13 @@
 }
 
 template <Direction DIR>
-void ExternalIoStatementState<DIR>::BeginReadingRecord() {
+bool ExternalIoStatementState<DIR>::BeginReadingRecord() {
   if constexpr (DIR == Direction::Input) {
-    unit().BeginReadingRecord(*this);
+    return unit().BeginReadingRecord(*this);
   } else {
     Crash("ExternalIoStatementState<Direction::Output>::BeginReadingRecord() "
           "called");
+    return false;
   }
 }
 
@@ -384,8 +385,8 @@
       [](auto &x) -> MutableModes & { return x.get().mutableModes(); }, u_);
 }
 
-void IoStatementState::BeginReadingRecord() {
-  std::visit([](auto &x) { return x.get().BeginReadingRecord(); }, u_);
+bool IoStatementState::BeginReadingRecord() {
+  return std::visit([](auto &x) { return x.get().BeginReadingRecord(); }, u_);
 }
 
 IoErrorHandler &IoStatementState::GetIoErrorHandler() const {
diff --git a/runtime/io-stmt.h b/runtime/io-stmt.h
index 686cc0f..000b1ac 100644
--- a/runtime/io-stmt.h
+++ b/runtime/io-stmt.h
@@ -71,7 +71,7 @@
   IoErrorHandler &GetIoErrorHandler() const;
   ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit
   MutableModes &mutableModes();
-  void BeginReadingRecord();
+  bool BeginReadingRecord();
   void FinishReadingRecord();
   bool Inquire(InquiryKeywordHash, char *, std::size_t);
   bool Inquire(InquiryKeywordHash, bool &);
@@ -139,7 +139,7 @@
   int EndIoStatement();
   std::optional<DataEdit> GetNextDataEdit(IoStatementState &, int = 1);
   ExternalFileUnit *GetExternalFileUnit() const { return nullptr; }
-  void BeginReadingRecord() {}
+  bool BeginReadingRecord() { return true; }
   void FinishReadingRecord() {}
   bool Inquire(InquiryKeywordHash, char *, std::size_t);
   bool Inquire(InquiryKeywordHash, bool &);
@@ -282,7 +282,7 @@
   void BackspaceRecord();
   void HandleRelativePosition(std::int64_t);
   void HandleAbsolutePosition(std::int64_t);
-  void BeginReadingRecord();
+  bool BeginReadingRecord();
   void FinishReadingRecord();
 };
 
diff --git a/runtime/unit.cpp b/runtime/unit.cpp
index d5a9f8e..e84385d 100644
--- a/runtime/unit.cpp
+++ b/runtime/unit.cpp
@@ -352,34 +352,38 @@
   positionInRecord = furthestPositionInRecord;
 }
 
-void ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
+bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
   RUNTIME_CHECK(handler, direction_ == Direction::Input);
-  if (beganReadingRecord_) {
-    return;
-  }
-  beganReadingRecord_ = true;
-  if (access == Access::Sequential) {
-    if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
-      handler.SignalEnd();
-    } else if (isFixedRecordLength) {
-      RUNTIME_CHECK(handler, recordLength.has_value());
-      auto need{static_cast<std::size_t>(recordOffsetInFrame_ + *recordLength)};
-      auto got{ReadFrame(frameOffsetInFile_, need, handler)};
-      if (got < need) {
+  if (!beganReadingRecord_) {
+    beganReadingRecord_ = true;
+    if (access == Access::Sequential) {
+      if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
         handler.SignalEnd();
+      } else if (isFixedRecordLength) {
+        RUNTIME_CHECK(handler, recordLength.has_value());
+        auto need{
+            static_cast<std::size_t>(recordOffsetInFrame_ + *recordLength)};
+        auto got{ReadFrame(frameOffsetInFile_, need, handler)};
+        if (got < need) {
+          handler.SignalEnd();
+        }
+      } else if (isUnformatted) {
+        BeginSequentialVariableUnformattedInputRecord(handler);
+      } else { // formatted
+        BeginSequentialVariableFormattedInputRecord(handler);
       }
-    } else if (isUnformatted) {
-      BeginSequentialVariableUnformattedInputRecord(handler);
-    } else { // formatted
-      BeginSequentialVariableFormattedInputRecord(handler);
     }
   }
+  RUNTIME_CHECK(handler,
+      access != Access::Sequential || recordLength.has_value() ||
+          handler.InError());
+  return !handler.InError();
 }
 
 void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) {
   RUNTIME_CHECK(handler, direction_ == Direction::Input && beganReadingRecord_);
   beganReadingRecord_ = false;
-  if (handler.GetIoStat() != IostatOk) {
+  if (handler.InError()) {
     // avoid bogus crashes in END/ERR circumstances
   } else if (access == Access::Sequential) {
     RUNTIME_CHECK(handler, recordLength.has_value());
@@ -405,11 +409,11 @@
 }
 
 bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
-  bool ok{true};
   if (direction_ == Direction::Input) {
     FinishReadingRecord(handler);
-    BeginReadingRecord(handler);
+    return BeginReadingRecord(handler);
   } else { // Direction::Output
+    bool ok{true};
     if (isFixedRecordLength && recordLength) {
       // Pad remainder of fixed length record
       if (furthestPositionInRecord < *recordLength) {
@@ -445,8 +449,8 @@
     impliedEndfile_ = true;
     ++currentRecordNumber;
     BeginRecord();
+    return ok;
   }
-  return ok;
 }
 
 void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
diff --git a/runtime/unit.h b/runtime/unit.h
index ff1506c..c9db5fc 100644
--- a/runtime/unit.h
+++ b/runtime/unit.h
@@ -77,7 +77,7 @@
   bool Receive(char *, std::size_t, std::size_t elementBytes, IoErrorHandler &);
   std::optional<char32_t> GetCurrentChar(IoErrorHandler &);
   void SetLeftTabLimit();
-  void BeginReadingRecord(IoErrorHandler &);
+  bool BeginReadingRecord(IoErrorHandler &);
   void FinishReadingRecord(IoErrorHandler &);
   bool AdvanceRecord(IoErrorHandler &);
   void BackspaceRecord(IoErrorHandler &);