diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp --- a/flang/runtime/io-stmt.cpp +++ b/flang/runtime/io-stmt.cpp @@ -272,7 +272,7 @@ template int ExternalIoStatementState::EndIoStatement() { if constexpr (DIR == Direction::Input) { BeginReadingRecord(); // in case there were no I/O items - if (!mutableModes().nonAdvancing) { + if (!mutableModes().nonAdvancing || GetIoStat() == IostatEor) { FinishReadingRecord(); } } else { @@ -559,19 +559,18 @@ return next; } const ConnectionState &connection{GetConnectionState()}; - if (!connection.IsAtEOF() && connection.isFixedRecordLength && - connection.recordLength && + if (!connection.IsAtEOF() && connection.recordLength && connection.positionInRecord >= *connection.recordLength) { - if (connection.modes.pad) { // PAD='YES' - --*remaining; - return std::optional{' '}; - } IoErrorHandler &handler{GetIoErrorHandler()}; if (mutableModes().nonAdvancing) { handler.SignalEor(); - } else { + } else if (connection.isFixedRecordLength && !connection.modes.pad) { handler.SignalError(IostatRecordReadOverrun); } + if (connection.modes.pad) { // PAD='YES' + --*remaining; + return std::optional{' '}; + } } } return std::nullopt; diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -402,7 +402,7 @@ void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) { RUNTIME_CHECK(handler, direction_ == Direction::Input && beganReadingRecord_); beganReadingRecord_ = false; - if (handler.InError()) { + if (handler.InError() && handler.GetIoStat() != IostatEor) { // avoid bogus crashes in END/ERR circumstances } else if (access == Access::Sequential) { RUNTIME_CHECK(handler, recordLength.has_value()); 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 @@ -17,6 +17,7 @@ #include "flang/Runtime/stop.h" #include "llvm/Support/raw_ostream.h" #include +#include using namespace Fortran::runtime::io; @@ -446,3 +447,70 @@ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) << "EndIoStatement() for Close"; } + +TEST(ExternalIOTests, TestNonAvancingInput) { + // 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 the file to be used for the input test. + static constexpr std::string_view records[] = { + "ABCDEFGH", "IJKLMNOP", "QRSTUVWX"}; + static constexpr std::string_view fmt{"(A)"}; + for (const auto &record : records) { + // WRITE(UNIT=unit,FMT=fmt) record + io = IONAME(BeginExternalFormattedOutput)( + fmt.data(), fmt.length(), unit, __FILE__, __LINE__); + ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length())) + << "OutputAscii()"; + ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) + << "EndIoStatement() for OutputAscii"; + } + + // REWIND(UNIT=unit) + io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); + ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) + << "EndIoStatement() for Rewind"; + + struct TestItems { + std::string item; + int expectedIoStat; + std::string expectedItemValue; + }; + // Actual non advancing input IO test + TestItems inputItems[]{ + {std::string(4, '+'), IostatOk, "ABCD"}, + {std::string(4, '+'), IostatOk, "EFGH"}, + {std::string(4, '+'), IostatEor, " "}, + {std::string(2, '+'), IostatOk, "IJ"}, + {std::string(8, '+'), IostatEor, "KLMNOP "}, + {std::string(10, '+'), IostatEor, "QRSTUVWX "}, + }; + + int j{0}; + for (auto &inputItem : inputItems) { + // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem + io = IONAME(BeginExternalFormattedInput)( + fmt.data(), fmt.length(), unit, __FILE__, __LINE__); + IONAME(EnableHandlers)(io, true, false, false, false, false); + ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j; + ASSERT_TRUE( + IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())) + << "InputAscii() " << j; + ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat) + << "EndIoStatement() for Read " << j; + ASSERT_EQ(inputItem.item, inputItem.expectedItemValue) + << "Input-item value after non advancing read " << j; + j++; + } +}