Skip to content

Commit ac4202f

Browse files
committed
[flang] Signal runtime error on WRITE after ENDFILE
After an ENDFILE statement, a WRITE is an error without a prior BACKSPACE. Also fix the return value for the case of formatted integer input with no input digits to be false (exposed by new test). Differential Revision: https://reviews.llvm.org/D117346
1 parent dd13744 commit ac4202f

File tree

7 files changed

+86
-9
lines changed

7 files changed

+86
-9
lines changed

flang/include/flang/Runtime/iostat.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ enum Iostat {
5555
IostatBackspaceNonSequential,
5656
IostatBackspaceAtFirstRecord,
5757
IostatRewindNonSequential,
58+
IostatWriteAfterEndfile,
5859
};
5960

6061
const char *IostatErrorString(int);

flang/runtime/connection.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ bool ConnectionState::IsAtEOF() const {
2727
return endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber;
2828
}
2929

30+
bool ConnectionState::IsAfterEndfile() const {
31+
return endfileRecordNumber && currentRecordNumber > *endfileRecordNumber;
32+
}
33+
3034
void ConnectionState::HandleAbsolutePosition(std::int64_t n) {
3135
positionInRecord = std::max(n, std::int64_t{0}) + leftTabLimit.value_or(0);
3236
}

flang/runtime/connection.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct ConnectionAttributes {
3535

3636
struct ConnectionState : public ConnectionAttributes {
3737
bool IsAtEOF() const; // true when read has hit EOF or endfile record
38+
bool IsAfterEndfile() const; // true after ENDFILE until repositioned
3839
std::size_t RemainingSpaceInRecord() const;
3940
bool NeedAdvance(std::size_t) const;
4041
void HandleAbsolutePosition(std::int64_t);

flang/runtime/edit-input.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ bool EditIntegerInput(
9696
std::optional<char32_t> next;
9797
bool negate{ScanNumericPrefix(io, edit, next, remaining)};
9898
common::UnsignedInt128 value;
99+
bool any{false};
99100
for (; next; next = io.NextInField(remaining)) {
100101
char32_t ch{*next};
101102
if (ch == ' ' || ch == '\t') {
@@ -115,12 +116,15 @@ bool EditIntegerInput(
115116
}
116117
value *= 10;
117118
value += digit;
119+
any = true;
118120
}
119-
if (negate) {
120-
value = -value;
121+
if (any) {
122+
if (negate) {
123+
value = -value;
124+
}
125+
std::memcpy(n, &value, kind);
121126
}
122-
std::memcpy(n, &value, kind);
123-
return true;
127+
return any;
124128
}
125129

126130
// Parses a REAL input number from the input source as a normalized

flang/runtime/iostat.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const char *IostatErrorString(int iostat) {
5353
return "BACKSPACE at first record";
5454
case IostatRewindNonSequential:
5555
return "REWIND on non-sequential file";
56+
case IostatWriteAfterEndfile:
57+
return "WRITE after ENDFILE";
5658
default:
5759
return nullptr;
5860
}

flang/runtime/unit.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
299299
recordLength.reset();
300300
beganReadingRecord_ = false;
301301
}
302+
if (IsAfterEndfile()) {
303+
handler.SignalError(IostatWriteAfterEndfile);
304+
return false;
305+
}
302306
WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler);
303307
if (positionInRecord > furthestPositionInRecord) {
304308
std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, ' ',
@@ -421,7 +425,7 @@ bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
421425
}
422426
} else if (access == Access::Sequential) {
423427
recordLength.reset();
424-
if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
428+
if (IsAtEOF()) {
425429
handler.SignalEnd();
426430
} else {
427431
RUNTIME_CHECK(handler, isUnformatted.has_value());
@@ -514,10 +518,13 @@ bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
514518
ok = ok && Emit("\n", 1, 1, handler); // TODO: Windows CR+LF
515519
}
516520
}
521+
if (IsAfterEndfile()) {
522+
return false;
523+
}
517524
CommitWrites();
518525
impliedEndfile_ = true;
519526
++currentRecordNumber;
520-
if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
527+
if (IsAtEOF()) {
521528
endfileRecordNumber.reset();
522529
}
523530
return ok;
@@ -529,7 +536,7 @@ void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
529536
handler.SignalError(IostatBackspaceNonSequential,
530537
"BACKSPACE(UNIT=%d) on non-sequential file", unitNumber());
531538
} else {
532-
if (endfileRecordNumber && currentRecordNumber > *endfileRecordNumber) {
539+
if (IsAfterEndfile()) {
533540
// BACKSPACE after explicit ENDFILE
534541
currentRecordNumber = *endfileRecordNumber;
535542
} else {
@@ -580,8 +587,7 @@ void ExternalFileUnit::Endfile(IoErrorHandler &handler) {
580587
} else if (!mayWrite()) {
581588
handler.SignalError(IostatEndfileUnwritable,
582589
"ENDFILE(UNIT=%d) on read-only file", unitNumber());
583-
} else if (endfileRecordNumber &&
584-
currentRecordNumber > *endfileRecordNumber) {
590+
} else if (IsAfterEndfile()) {
585591
// ENDFILE after ENDFILE
586592
} else {
587593
DoEndfile(handler);

flang/unittests/Runtime/ExternalIOTest.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,3 +649,62 @@ TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) {
649649
ASSERT_EQ(resultRecord, expectedRecord)
650650
<< "Record after non advancing read followed by wrote";
651651
}
652+
653+
TEST(ExternalIOTests, TestWriteAfterEndfile) {
654+
// OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
655+
// FORM='FORMATTED',STATUS='SCRATCH')
656+
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
657+
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
658+
<< "SetAccess(SEQUENTIAL)";
659+
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
660+
ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
661+
ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
662+
int unit{-1};
663+
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
664+
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
665+
<< "EndIoStatement() for OpenNewUnit";
666+
// WRITE(unit,"(I8)") 1234
667+
static constexpr std::string_view format{"(I8)"};
668+
io = IONAME(BeginExternalFormattedOutput)(
669+
format.data(), format.length(), unit, __FILE__, __LINE__);
670+
ASSERT_TRUE(IONAME(OutputInteger64)(io, 1234)) << "OutputInteger64()";
671+
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
672+
<< "EndIoStatement for WRITE before ENDFILE";
673+
// ENDFILE(unit)
674+
io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__);
675+
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
676+
<< "EndIoStatement for ENDFILE";
677+
// WRITE(unit,"(I8)",iostat=iostat) 5678
678+
io = IONAME(BeginExternalFormattedOutput)(
679+
format.data(), format.length(), unit, __FILE__, __LINE__);
680+
IONAME(EnableHandlers)(io, true /*IOSTAT=*/);
681+
ASSERT_FALSE(IONAME(OutputInteger64)(io, 5678)) << "OutputInteger64()";
682+
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile)
683+
<< "EndIoStatement for WRITE after ENDFILE";
684+
// BACKSPACE(unit)
685+
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
686+
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
687+
<< "EndIoStatement for BACKSPACE";
688+
// WRITE(unit,"(I8)") 3456
689+
io = IONAME(BeginExternalFormattedOutput)(
690+
format.data(), format.length(), unit, __FILE__, __LINE__);
691+
ASSERT_TRUE(IONAME(OutputInteger64)(io, 3456)) << "OutputInteger64()";
692+
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
693+
<< "EndIoStatement for WRITE after BACKSPACE";
694+
// REWIND(unit)
695+
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
696+
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
697+
<< "EndIoStatement for REWIND";
698+
// READ(unit,"(I8)",END=) j, k
699+
std::int64_t j{-1}, k{-1}, eof{-1};
700+
io = IONAME(BeginExternalFormattedInput)(
701+
format.data(), format.length(), unit, __FILE__, __LINE__);
702+
IONAME(EnableHandlers)(io, false, false, true /*END=*/);
703+
ASSERT_TRUE(IONAME(InputInteger)(io, j)) << "InputInteger(j)";
704+
ASSERT_EQ(j, 1234) << "READ(j)";
705+
ASSERT_TRUE(IONAME(InputInteger)(io, k)) << "InputInteger(k)";
706+
ASSERT_EQ(k, 3456) << "READ(k)";
707+
ASSERT_FALSE(IONAME(InputInteger)(io, eof)) << "InputInteger(eof)";
708+
ASSERT_EQ(eof, -1) << "READ(eof)";
709+
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatEnd) << "EndIoStatement for READ";
710+
}

0 commit comments

Comments
 (0)