forked from OSchip/llvm-project
[flang] Allow write after non advancing read in IO runtime
1. To avoid overwriting the part of the record read in the non advancing read, the furtherPositionInRecord field must be set to the max of the furtherPositionInRecord and the positionInRecord at the beginning of the IO write. 2. To allow any further read to succeed after the write, the unit beganReadingRecord_ must be set to false when resetting the recordLength during the write, otherwise, recordLength will not be computed in further read and an assert is hit (at unit.cpp(398)). The added unit test exercises both of these scenarios. Differential Revision: https://reviews.llvm.org/D113740
This commit is contained in:
parent
422cf2b506
commit
2e65c8e8db
|
@ -264,7 +264,16 @@ template <Direction DIR>
|
|||
ExternalIoStatementState<DIR>::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 <Direction DIR> int ExternalIoStatementState<DIR>::EndIoStatement() {
|
||||
if constexpr (DIR == Direction::Input) {
|
||||
|
|
|
@ -261,9 +261,10 @@ bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
|
|||
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 "
|
||||
|
|
|
@ -537,3 +537,98 @@ TEST(ExternalIOTests, TestNonAvancingInput) {
|
|||
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";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue