diff --git a/flang/include/flang/Runtime/iostat.h b/flang/include/flang/Runtime/iostat.h --- a/flang/include/flang/Runtime/iostat.h +++ b/flang/include/flang/Runtime/iostat.h @@ -55,6 +55,7 @@ IostatBackspaceNonSequential, IostatBackspaceAtFirstRecord, IostatRewindNonSequential, + IostatWriteAfterEndfile, }; const char *IostatErrorString(int); diff --git a/flang/runtime/connection.h b/flang/runtime/connection.h --- a/flang/runtime/connection.h +++ b/flang/runtime/connection.h @@ -35,6 +35,7 @@ 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/connection.cpp b/flang/runtime/connection.cpp --- a/flang/runtime/connection.cpp +++ b/flang/runtime/connection.cpp @@ -27,6 +27,10 @@ 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/edit-input.cpp b/flang/runtime/edit-input.cpp --- a/flang/runtime/edit-input.cpp +++ b/flang/runtime/edit-input.cpp @@ -96,6 +96,7 @@ std::optional 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 @@ } 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 --- a/flang/runtime/iostat.cpp +++ b/flang/runtime/iostat.cpp @@ -53,6 +53,8 @@ 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 --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -299,6 +299,10 @@ 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 @@ } } 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 @@ 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 @@ 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 @@ } 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 --- a/flang/unittests/Runtime/ExternalIOTest.cpp +++ b/flang/unittests/Runtime/ExternalIOTest.cpp @@ -649,3 +649,62 @@ 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"; +}