[flang-commits] [flang] ac4202f - [flang] Signal runtime error on WRITE after ENDFILE

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Fri Jan 14 14:46:06 PST 2022


Author: Peter Klausler
Date: 2022-01-14T14:46:00-08:00
New Revision: ac4202fe9daf3202f7d1e09e9a46cb962c072888

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

LOG: [flang] Signal runtime error on WRITE after ENDFILE

After an ENDFILE statement, a WRITE is an error without
a prior BACKSPACE.  Also fix the return value for the case
of formatted integer input with no input digits to be false
(exposed by new test).

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

Added: 
    

Modified: 
    flang/include/flang/Runtime/iostat.h
    flang/runtime/connection.cpp
    flang/runtime/connection.h
    flang/runtime/edit-input.cpp
    flang/runtime/iostat.cpp
    flang/runtime/unit.cpp
    flang/unittests/Runtime/ExternalIOTest.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Runtime/iostat.h b/flang/include/flang/Runtime/iostat.h
index ec1c2399819d4..ec1c6a2abdbeb 100644
--- a/flang/include/flang/Runtime/iostat.h
+++ b/flang/include/flang/Runtime/iostat.h
@@ -55,6 +55,7 @@ enum Iostat {
   IostatBackspaceNonSequential,
   IostatBackspaceAtFirstRecord,
   IostatRewindNonSequential,
+  IostatWriteAfterEndfile,
 };
 
 const char *IostatErrorString(int);

diff  --git a/flang/runtime/connection.cpp b/flang/runtime/connection.cpp
index 765ce90520c2a..4172a8361857c 100644
--- a/flang/runtime/connection.cpp
+++ b/flang/runtime/connection.cpp
@@ -27,6 +27,10 @@ bool ConnectionState::IsAtEOF() const {
   return endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber;
 }
 
+bool ConnectionState::IsAfterEndfile() const {
+  return endfileRecordNumber && currentRecordNumber > *endfileRecordNumber;
+}
+
 void ConnectionState::HandleAbsolutePosition(std::int64_t n) {
   positionInRecord = std::max(n, std::int64_t{0}) + leftTabLimit.value_or(0);
 }

diff  --git a/flang/runtime/connection.h b/flang/runtime/connection.h
index 0a97130288d31..c812312de91dc 100644
--- a/flang/runtime/connection.h
+++ b/flang/runtime/connection.h
@@ -35,6 +35,7 @@ struct ConnectionAttributes {
 
 struct ConnectionState : public ConnectionAttributes {
   bool IsAtEOF() const; // true when read has hit EOF or endfile record
+  bool IsAfterEndfile() const; // true after ENDFILE until repositioned
   std::size_t RemainingSpaceInRecord() const;
   bool NeedAdvance(std::size_t) const;
   void HandleAbsolutePosition(std::int64_t);

diff  --git a/flang/runtime/edit-input.cpp b/flang/runtime/edit-input.cpp
index 3251db1fed84f..7fe4bfaf9a89e 100644
--- a/flang/runtime/edit-input.cpp
+++ b/flang/runtime/edit-input.cpp
@@ -96,6 +96,7 @@ bool EditIntegerInput(
   std::optional<char32_t> next;
   bool negate{ScanNumericPrefix(io, edit, next, remaining)};
   common::UnsignedInt128 value;
+  bool any{false};
   for (; next; next = io.NextInField(remaining)) {
     char32_t ch{*next};
     if (ch == ' ' || ch == '\t') {
@@ -115,12 +116,15 @@ bool EditIntegerInput(
     }
     value *= 10;
     value += digit;
+    any = true;
   }
-  if (negate) {
-    value = -value;
+  if (any) {
+    if (negate) {
+      value = -value;
+    }
+    std::memcpy(n, &value, kind);
   }
-  std::memcpy(n, &value, kind);
-  return true;
+  return any;
 }
 
 // Parses a REAL input number from the input source as a normalized

diff  --git a/flang/runtime/iostat.cpp b/flang/runtime/iostat.cpp
index 649141fb8ea51..d58ad6261ac68 100644
--- a/flang/runtime/iostat.cpp
+++ b/flang/runtime/iostat.cpp
@@ -53,6 +53,8 @@ const char *IostatErrorString(int iostat) {
     return "BACKSPACE at first record";
   case IostatRewindNonSequential:
     return "REWIND on non-sequential file";
+  case IostatWriteAfterEndfile:
+    return "WRITE after ENDFILE";
   default:
     return nullptr;
   }

diff  --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp
index 2cef9a014a182..e24e1b9ae64cd 100644
--- a/flang/runtime/unit.cpp
+++ b/flang/runtime/unit.cpp
@@ -299,6 +299,10 @@ bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
     recordLength.reset();
     beganReadingRecord_ = false;
   }
+  if (IsAfterEndfile()) {
+    handler.SignalError(IostatWriteAfterEndfile);
+    return false;
+  }
   WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler);
   if (positionInRecord > furthestPositionInRecord) {
     std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, ' ',
@@ -421,7 +425,7 @@ bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
       }
     } else if (access == Access::Sequential) {
       recordLength.reset();
-      if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
+      if (IsAtEOF()) {
         handler.SignalEnd();
       } else {
         RUNTIME_CHECK(handler, isUnformatted.has_value());
@@ -514,10 +518,13 @@ bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
         ok = ok && Emit("\n", 1, 1, handler); // TODO: Windows CR+LF
       }
     }
+    if (IsAfterEndfile()) {
+      return false;
+    }
     CommitWrites();
     impliedEndfile_ = true;
     ++currentRecordNumber;
-    if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
+    if (IsAtEOF()) {
       endfileRecordNumber.reset();
     }
     return ok;
@@ -529,7 +536,7 @@ void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
     handler.SignalError(IostatBackspaceNonSequential,
         "BACKSPACE(UNIT=%d) on non-sequential file", unitNumber());
   } else {
-    if (endfileRecordNumber && currentRecordNumber > *endfileRecordNumber) {
+    if (IsAfterEndfile()) {
       // BACKSPACE after explicit ENDFILE
       currentRecordNumber = *endfileRecordNumber;
     } else {
@@ -580,8 +587,7 @@ void ExternalFileUnit::Endfile(IoErrorHandler &handler) {
   } else if (!mayWrite()) {
     handler.SignalError(IostatEndfileUnwritable,
         "ENDFILE(UNIT=%d) on read-only file", unitNumber());
-  } else if (endfileRecordNumber &&
-      currentRecordNumber > *endfileRecordNumber) {
+  } else if (IsAfterEndfile()) {
     // ENDFILE after ENDFILE
   } else {
     DoEndfile(handler);

diff  --git a/flang/unittests/Runtime/ExternalIOTest.cpp b/flang/unittests/Runtime/ExternalIOTest.cpp
index 568a559fcd7f4..6a98b85c81204 100644
--- a/flang/unittests/Runtime/ExternalIOTest.cpp
+++ b/flang/unittests/Runtime/ExternalIOTest.cpp
@@ -649,3 +649,62 @@ TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) {
   ASSERT_EQ(resultRecord, expectedRecord)
       << "Record after non advancing read followed by wrote";
 }
+
+TEST(ExternalIOTests, TestWriteAfterEndfile) {
+  // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
+  //   FORM='FORMATTED',STATUS='SCRATCH')
+  auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
+  ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
+      << "SetAccess(SEQUENTIAL)";
+  ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
+  ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
+  ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
+  int unit{-1};
+  ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement() for OpenNewUnit";
+  // WRITE(unit,"(I8)") 1234
+  static constexpr std::string_view format{"(I8)"};
+  io = IONAME(BeginExternalFormattedOutput)(
+      format.data(), format.length(), unit, __FILE__, __LINE__);
+  ASSERT_TRUE(IONAME(OutputInteger64)(io, 1234)) << "OutputInteger64()";
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement for WRITE before ENDFILE";
+  // ENDFILE(unit)
+  io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__);
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement for ENDFILE";
+  // WRITE(unit,"(I8)",iostat=iostat) 5678
+  io = IONAME(BeginExternalFormattedOutput)(
+      format.data(), format.length(), unit, __FILE__, __LINE__);
+  IONAME(EnableHandlers)(io, true /*IOSTAT=*/);
+  ASSERT_FALSE(IONAME(OutputInteger64)(io, 5678)) << "OutputInteger64()";
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile)
+      << "EndIoStatement for WRITE after ENDFILE";
+  // BACKSPACE(unit)
+  io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement for BACKSPACE";
+  // WRITE(unit,"(I8)") 3456
+  io = IONAME(BeginExternalFormattedOutput)(
+      format.data(), format.length(), unit, __FILE__, __LINE__);
+  ASSERT_TRUE(IONAME(OutputInteger64)(io, 3456)) << "OutputInteger64()";
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement for WRITE after BACKSPACE";
+  // REWIND(unit)
+  io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement for REWIND";
+  // READ(unit,"(I8)",END=) j, k
+  std::int64_t j{-1}, k{-1}, eof{-1};
+  io = IONAME(BeginExternalFormattedInput)(
+      format.data(), format.length(), unit, __FILE__, __LINE__);
+  IONAME(EnableHandlers)(io, false, false, true /*END=*/);
+  ASSERT_TRUE(IONAME(InputInteger)(io, j)) << "InputInteger(j)";
+  ASSERT_EQ(j, 1234) << "READ(j)";
+  ASSERT_TRUE(IONAME(InputInteger)(io, k)) << "InputInteger(k)";
+  ASSERT_EQ(k, 3456) << "READ(k)";
+  ASSERT_FALSE(IONAME(InputInteger)(io, eof)) << "InputInteger(eof)";
+  ASSERT_EQ(eof, -1) << "READ(eof)";
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatEnd) << "EndIoStatement for READ";
+}


        


More information about the flang-commits mailing list