[flang-commits] [flang] ac4202f - [flang] Signal runtime error on WRITE after ENDFILE
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Fri Jan 14 14:46:06 PST 2022
Author: Peter Klausler
Date: 2022-01-14T14:46:00-08:00
New Revision: ac4202fe9daf3202f7d1e09e9a46cb962c072888
URL: https://github.com/llvm/llvm-project/commit/ac4202fe9daf3202f7d1e09e9a46cb962c072888
DIFF: https://github.com/llvm/llvm-project/commit/ac4202fe9daf3202f7d1e09e9a46cb962c072888.diff
LOG: [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
Added:
Modified:
flang/include/flang/Runtime/iostat.h
flang/runtime/connection.cpp
flang/runtime/connection.h
flang/runtime/edit-input.cpp
flang/runtime/iostat.cpp
flang/runtime/unit.cpp
flang/unittests/Runtime/ExternalIOTest.cpp
Removed:
################################################################################
diff --git a/flang/include/flang/Runtime/iostat.h b/flang/include/flang/Runtime/iostat.h
index ec1c2399819d4..ec1c6a2abdbeb 100644
--- a/flang/include/flang/Runtime/iostat.h
+++ b/flang/include/flang/Runtime/iostat.h
@@ -55,6 +55,7 @@ enum Iostat {
IostatBackspaceNonSequential,
IostatBackspaceAtFirstRecord,
IostatRewindNonSequential,
+ IostatWriteAfterEndfile,
};
const char *IostatErrorString(int);
diff --git a/flang/runtime/connection.cpp b/flang/runtime/connection.cpp
index 765ce90520c2a..4172a8361857c 100644
--- a/flang/runtime/connection.cpp
+++ b/flang/runtime/connection.cpp
@@ -27,6 +27,10 @@ bool ConnectionState::IsAtEOF() const {
return endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber;
}
+bool ConnectionState::IsAfterEndfile() const {
+ return endfileRecordNumber && currentRecordNumber > *endfileRecordNumber;
+}
+
void ConnectionState::HandleAbsolutePosition(std::int64_t n) {
positionInRecord = std::max(n, std::int64_t{0}) + leftTabLimit.value_or(0);
}
diff --git a/flang/runtime/connection.h b/flang/runtime/connection.h
index 0a97130288d31..c812312de91dc 100644
--- a/flang/runtime/connection.h
+++ b/flang/runtime/connection.h
@@ -35,6 +35,7 @@ struct ConnectionAttributes {
struct ConnectionState : public ConnectionAttributes {
bool IsAtEOF() const; // true when read has hit EOF or endfile record
+ bool IsAfterEndfile() const; // true after ENDFILE until repositioned
std::size_t RemainingSpaceInRecord() const;
bool NeedAdvance(std::size_t) const;
void HandleAbsolutePosition(std::int64_t);
diff --git a/flang/runtime/edit-input.cpp b/flang/runtime/edit-input.cpp
index 3251db1fed84f..7fe4bfaf9a89e 100644
--- a/flang/runtime/edit-input.cpp
+++ b/flang/runtime/edit-input.cpp
@@ -96,6 +96,7 @@ bool EditIntegerInput(
std::optional<char32_t> next;
bool negate{ScanNumericPrefix(io, edit, next, remaining)};
common::UnsignedInt128 value;
+ bool any{false};
for (; next; next = io.NextInField(remaining)) {
char32_t ch{*next};
if (ch == ' ' || ch == '\t') {
@@ -115,12 +116,15 @@ bool EditIntegerInput(
}
value *= 10;
value += digit;
+ any = true;
}
- if (negate) {
- value = -value;
+ if (any) {
+ if (negate) {
+ value = -value;
+ }
+ std::memcpy(n, &value, kind);
}
- std::memcpy(n, &value, kind);
- return true;
+ return any;
}
// Parses a REAL input number from the input source as a normalized
diff --git a/flang/runtime/iostat.cpp b/flang/runtime/iostat.cpp
index 649141fb8ea51..d58ad6261ac68 100644
--- a/flang/runtime/iostat.cpp
+++ b/flang/runtime/iostat.cpp
@@ -53,6 +53,8 @@ const char *IostatErrorString(int iostat) {
return "BACKSPACE at first record";
case IostatRewindNonSequential:
return "REWIND on non-sequential file";
+ case IostatWriteAfterEndfile:
+ return "WRITE after ENDFILE";
default:
return nullptr;
}
diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp
index 2cef9a014a182..e24e1b9ae64cd 100644
--- a/flang/runtime/unit.cpp
+++ b/flang/runtime/unit.cpp
@@ -299,6 +299,10 @@ bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
recordLength.reset();
beganReadingRecord_ = false;
}
+ if (IsAfterEndfile()) {
+ handler.SignalError(IostatWriteAfterEndfile);
+ return false;
+ }
WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler);
if (positionInRecord > furthestPositionInRecord) {
std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, ' ',
@@ -421,7 +425,7 @@ bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
}
} else if (access == Access::Sequential) {
recordLength.reset();
- if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
+ if (IsAtEOF()) {
handler.SignalEnd();
} else {
RUNTIME_CHECK(handler, isUnformatted.has_value());
@@ -514,10 +518,13 @@ bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
ok = ok && Emit("\n", 1, 1, handler); // TODO: Windows CR+LF
}
}
+ if (IsAfterEndfile()) {
+ return false;
+ }
CommitWrites();
impliedEndfile_ = true;
++currentRecordNumber;
- if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
+ if (IsAtEOF()) {
endfileRecordNumber.reset();
}
return ok;
@@ -529,7 +536,7 @@ void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
handler.SignalError(IostatBackspaceNonSequential,
"BACKSPACE(UNIT=%d) on non-sequential file", unitNumber());
} else {
- if (endfileRecordNumber && currentRecordNumber > *endfileRecordNumber) {
+ if (IsAfterEndfile()) {
// BACKSPACE after explicit ENDFILE
currentRecordNumber = *endfileRecordNumber;
} else {
@@ -580,8 +587,7 @@ void ExternalFileUnit::Endfile(IoErrorHandler &handler) {
} else if (!mayWrite()) {
handler.SignalError(IostatEndfileUnwritable,
"ENDFILE(UNIT=%d) on read-only file", unitNumber());
- } else if (endfileRecordNumber &&
- currentRecordNumber > *endfileRecordNumber) {
+ } else if (IsAfterEndfile()) {
// ENDFILE after ENDFILE
} else {
DoEndfile(handler);
diff --git a/flang/unittests/Runtime/ExternalIOTest.cpp b/flang/unittests/Runtime/ExternalIOTest.cpp
index 568a559fcd7f4..6a98b85c81204 100644
--- a/flang/unittests/Runtime/ExternalIOTest.cpp
+++ b/flang/unittests/Runtime/ExternalIOTest.cpp
@@ -649,3 +649,62 @@ TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) {
ASSERT_EQ(resultRecord, expectedRecord)
<< "Record after non advancing read followed by wrote";
}
+
+TEST(ExternalIOTests, TestWriteAfterEndfile) {
+ // 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(unit,"(I8)") 1234
+ static constexpr std::string_view format{"(I8)"};
+ io = IONAME(BeginExternalFormattedOutput)(
+ format.data(), format.length(), unit, __FILE__, __LINE__);
+ ASSERT_TRUE(IONAME(OutputInteger64)(io, 1234)) << "OutputInteger64()";
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement for WRITE before ENDFILE";
+ // ENDFILE(unit)
+ io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__);
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement for ENDFILE";
+ // WRITE(unit,"(I8)",iostat=iostat) 5678
+ io = IONAME(BeginExternalFormattedOutput)(
+ format.data(), format.length(), unit, __FILE__, __LINE__);
+ IONAME(EnableHandlers)(io, true /*IOSTAT=*/);
+ ASSERT_FALSE(IONAME(OutputInteger64)(io, 5678)) << "OutputInteger64()";
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile)
+ << "EndIoStatement for WRITE after ENDFILE";
+ // BACKSPACE(unit)
+ io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement for BACKSPACE";
+ // WRITE(unit,"(I8)") 3456
+ io = IONAME(BeginExternalFormattedOutput)(
+ format.data(), format.length(), unit, __FILE__, __LINE__);
+ ASSERT_TRUE(IONAME(OutputInteger64)(io, 3456)) << "OutputInteger64()";
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement for WRITE after BACKSPACE";
+ // REWIND(unit)
+ io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement for REWIND";
+ // READ(unit,"(I8)",END=) j, k
+ std::int64_t j{-1}, k{-1}, eof{-1};
+ io = IONAME(BeginExternalFormattedInput)(
+ format.data(), format.length(), unit, __FILE__, __LINE__);
+ IONAME(EnableHandlers)(io, false, false, true /*END=*/);
+ ASSERT_TRUE(IONAME(InputInteger)(io, j)) << "InputInteger(j)";
+ ASSERT_EQ(j, 1234) << "READ(j)";
+ ASSERT_TRUE(IONAME(InputInteger)(io, k)) << "InputInteger(k)";
+ ASSERT_EQ(k, 3456) << "READ(k)";
+ ASSERT_FALSE(IONAME(InputInteger)(io, eof)) << "InputInteger(eof)";
+ ASSERT_EQ(eof, -1) << "READ(eof)";
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatEnd) << "EndIoStatement for READ";
+}
More information about the flang-commits
mailing list