Skip to content

Commit 7cf198f

Browse files
committed
[flang][runtime] Don't do partial data transfer on short character read with ADVANCE='NO', PAD='NO'
When a non-advancing formatted READ can't completely fill a CHARACTER input item with data, and PAD='NO', don't modify the variable at all. This matters when the error is recoverable. Fixes llvm#63772 Differential Revision: https://reviews.llvm.org/D155000
1 parent 78be5ae commit 7cf198f

File tree

4 files changed

+56
-18
lines changed

4 files changed

+56
-18
lines changed

flang/runtime/edit-input.cpp

+12-5
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ bool EditCharacterInput(
719719
}
720720
// When the field is wider than the variable, we drop the leading
721721
// characters. When the variable is wider than the field, there can be
722-
// trailing padding.
722+
// trailing padding or an EOR condition.
723723
const char *input{nullptr};
724724
std::size_t ready{0};
725725
// Skip leading bytes.
@@ -729,11 +729,18 @@ bool EditCharacterInput(
729729
while (remaining > 0) {
730730
if (ready == 0) {
731731
ready = io.GetNextInputBytes(input);
732-
if (ready == 0) {
733-
if (io.CheckForEndOfRecord()) {
734-
std::fill_n(x, length, ' '); // PAD='YES'
732+
if (ready == 0 || (ready < remaining && edit.modes.nonAdvancing)) {
733+
if (io.CheckForEndOfRecord(ready)) {
734+
if (ready == 0) {
735+
// PAD='YES' and no more data
736+
std::fill_n(x, length, ' ');
737+
return !io.GetIoErrorHandler().InError();
738+
} else {
739+
// Do partial read(s) then pad on last iteration
740+
}
741+
} else {
742+
return !io.GetIoErrorHandler().InError();
735743
}
736-
return !io.GetIoErrorHandler().InError();
737744
}
738745
}
739746
std::size_t chunk;

flang/runtime/io-stmt.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -589,19 +589,21 @@ std::optional<char32_t> IoStatementState::NextInField(
589589
GotChar(byteCount);
590590
return next;
591591
}
592-
if (CheckForEndOfRecord()) { // do padding
592+
if (CheckForEndOfRecord(0)) { // do padding
593593
--*remaining;
594594
return std::optional<char32_t>{' '};
595595
}
596596
}
597597
return std::nullopt;
598598
}
599599

600-
bool IoStatementState::CheckForEndOfRecord() {
600+
bool IoStatementState::CheckForEndOfRecord(std::size_t afterReading) {
601601
const ConnectionState &connection{GetConnectionState()};
602602
if (!connection.IsAtEOF()) {
603603
if (auto length{connection.EffectiveRecordLength()}) {
604-
if (connection.positionInRecord >= *length) {
604+
if (connection.positionInRecord +
605+
static_cast<std::int64_t>(afterReading) >=
606+
*length) {
605607
IoErrorHandler &handler{GetIoErrorHandler()};
606608
const auto &modes{mutableModes()};
607609
if (modes.nonAdvancing) {

flang/runtime/io-stmt.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class IoStatementState {
169169

170170
// Detect and signal any end-of-record condition after input.
171171
// Returns true if at EOR and remaining input should be padded with blanks.
172-
bool CheckForEndOfRecord();
172+
bool CheckForEndOfRecord(std::size_t afterReading);
173173

174174
// Skips spaces, advances records, and ignores NAMELIST comments
175175
std::optional<char32_t> GetNextNonBlank(std::size_t &byteCount) {

flang/unittests/Runtime/ExternalIOTest.cpp

+38-9
Original file line numberDiff line numberDiff line change
@@ -525,35 +525,64 @@ TEST(ExternalIOTests, TestNonAvancingInput) {
525525
struct TestItems {
526526
std::string item;
527527
int expectedIoStat;
528-
std::string expectedItemValue;
528+
std::string expectedItemValue[2];
529529
};
530530
// Actual non advancing input IO test
531531
TestItems inputItems[]{
532-
{std::string(4, '+'), IostatOk, "ABCD"},
533-
{std::string(4, '+'), IostatOk, "EFGH"},
534-
{std::string(4, '+'), IostatEor, " "},
535-
{std::string(2, '+'), IostatOk, "IJ"},
536-
{std::string(8, '+'), IostatEor, "KLMNOP "},
537-
{std::string(10, '+'), IostatEor, "QRSTUVWX "},
532+
{std::string(4, '+'), IostatOk, "ABCD", "ABCD"},
533+
{std::string(4, '+'), IostatOk, "EFGH", "EFGH"},
534+
{std::string(4, '+'), IostatEor, "++++", " "},
535+
{std::string(2, '+'), IostatOk, "IJ", "IJ"},
536+
{std::string(8, '+'), IostatEor, "++++++++", "KLMNOP "},
537+
{std::string(10, '+'), IostatEor, "++++++++++", "QRSTUVWX "},
538538
};
539539

540+
// Test with PAD='NO'
540541
int j{0};
541542
for (auto &inputItem : inputItems) {
542-
// READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem
543+
// READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='NO', IOSTAT=iostat) inputItem
543544
io = IONAME(BeginExternalFormattedInput)(
544545
fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
545546
IONAME(EnableHandlers)(io, true, false, false, false, false);
546547
ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
548+
ASSERT_TRUE(IONAME(SetPad)(io, "NO", 2)) << "SetPad(NO)" << j;
547549
bool result{
548550
IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())};
549551
ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk)
550552
<< "InputAscii() " << j;
551553
ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
552554
<< "EndIoStatement() for Read " << j;
553-
ASSERT_EQ(inputItem.item, inputItem.expectedItemValue)
555+
ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[0])
556+
<< "Input-item value after non advancing read " << j;
557+
j++;
558+
}
559+
560+
// REWIND(UNIT=unit)
561+
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
562+
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
563+
<< "EndIoStatement() for Rewind";
564+
565+
// Test again with PAD='YES'
566+
j = 0;
567+
for (auto &inputItem : inputItems) {
568+
// READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='YES', IOSTAT=iostat)
569+
// inputItem
570+
io = IONAME(BeginExternalFormattedInput)(
571+
fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
572+
IONAME(EnableHandlers)(io, true, false, false, false, false);
573+
ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
574+
ASSERT_TRUE(IONAME(SetPad)(io, "YES", 3)) << "SetPad(YES)" << j;
575+
bool result{
576+
IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())};
577+
ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk)
578+
<< "InputAscii() " << j;
579+
ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
580+
<< "EndIoStatement() for Read " << j;
581+
ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[1])
554582
<< "Input-item value after non advancing read " << j;
555583
j++;
556584
}
585+
557586
// CLOSE(UNIT=unit)
558587
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
559588
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)

0 commit comments

Comments
 (0)