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 @@ -264,7 +264,16 @@ ExternalIoStatementState::ExternalIoStatementState( ExternalFileUnit &unit, const char *sourceFile, int sourceLine) : ExternalIoStatementBase{unit, sourceFile, sourceLine}, mutableModes_{ - unit.modes} {} + unit.modes} { + if constexpr (DIR == Direction::Output) { + // If the last statement was a non advancing IO input statement, the unit + // furthestPositionInRecord was not advanced, but the positionInRecord may + // have been advanced. Advance furthestPositionInRecord here to avoid + // overwriting the part of the record that has been read with blanks. + unit.furthestPositionInRecord = + std::max(unit.furthestPositionInRecord, unit.positionInRecord); + } +} template int ExternalIoStatementState::EndIoStatement() { if constexpr (DIR == Direction::Input) { diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -261,9 +261,10 @@ if (recordLength) { // It is possible for recordLength to have a value now for a // variable-length output record if the previous operation - // was a BACKSPACE. + // was a BACKSPACE or non advancing input statement. if (!isFixedRecordLength) { recordLength.reset(); + beganReadingRecord_ = false; } else if (furthestAfter > *recordLength) { handler.SignalError(IostatRecordWriteOverrun, "Attempt to write %zd bytes to position %jd in a fixed-size record " 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 @@ -537,3 +537,98 @@ j++; } } + +TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) { + // 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[] = {"ABCDEFGHIJKLMNOPQRST"}; + 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"}, + }; + + 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++; + } + + // WRITE(UNIT=unit, FMT=fmt, IOSTAT=iostat) outputItem. + static constexpr std::string_view outputItem{"XYZ"}; + // WRITE(UNIT=unit,FMT=fmt) record + io = IONAME(BeginExternalFormattedOutput)( + fmt.data(), fmt.length(), unit, __FILE__, __LINE__); + ASSERT_TRUE(IONAME(OutputAscii)(io, outputItem.data(), outputItem.length())) + << "OutputAscii()"; + ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) + << "EndIoStatement() for OutputAscii"; + + // Verify that the output was written in the record read in non avdancing + // mode, after the read part, and that the end was truncated. + + // REWIND(UNIT=unit) + io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); + ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) + << "EndIoStatement() for Rewind"; + + std::string resultRecord(20, '+'); + std::string expectedRecord{"ABCDEFGHXYZ "}; + // READ(UNIT=unit, FMT=fmt, IOSTAT=iostat) result + io = IONAME(BeginExternalFormattedInput)( + fmt.data(), fmt.length(), unit, __FILE__, __LINE__); + IONAME(EnableHandlers)(io, true, false, false, false, false); + ASSERT_TRUE( + IONAME(InputAscii)(io, resultRecord.data(), resultRecord.length())) + << "InputAscii() "; + ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) + << "EndIoStatement() for Read "; + + ASSERT_EQ(resultRecord, expectedRecord) + << "Record after non advancing read followed by wrote"; +}