[flang-commits] [flang] 06ca9f2 - [flang] OPEN(RECL=) handling for sequential formatted I/O
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Sat Dec 4 16:03:08 PST 2021
Author: Peter Klausler
Date: 2021-12-04T16:02:48-08:00
New Revision: 06ca9f24e73fb1a9302830b4464493abd38425d0
URL: https://github.com/llvm/llvm-project/commit/06ca9f24e73fb1a9302830b4464493abd38425d0
DIFF: https://github.com/llvm/llvm-project/commit/06ca9f24e73fb1a9302830b4464493abd38425d0.diff
LOG: [flang] OPEN(RECL=) handling for sequential formatted I/O
RECL= is required for direct access I/O, but is permitted
as well for sequential I/O, where it is defined by the
standard to specify a maximum record (line) length.
The standard does not say what should happen when an
sequential formatted input record appears whose length is
unequal to RECL= when it is specified.
Precedents from other compilers are unclear: one raises an error,
some honor RECL= as an effective truncation, and a few ignore the
situation. On output, all other compilers tested raised an
error when an attempt is made to emit a record longer than RECL=.
This patch treats RECL= as effective truncation on input and
as a hard limit with error on output, and also ensures that
RECL= can be set *longer* than the actual input record lengths.
Differential Revision: https://reviews.llvm.org/D115102
Added:
Modified:
flang/docs/Extensions.md
flang/runtime/connection.h
flang/runtime/internal-unit.cpp
flang/runtime/io-api.cpp
flang/runtime/io-stmt.cpp
flang/runtime/io-stmt.h
flang/runtime/unit.cpp
Removed:
################################################################################
diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 3c63d83aacc4f..7bcea54b0f520 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -351,3 +351,12 @@ end
the parent, allocatable or not;
all finalization takes place before any deallocation;
and no object or subobject will be finalized more than once.
+
+* When `RECL=` is set via the `OPEN` statement for a sequential formatted input
+ file, it functions as an effective maximum record length.
+ Longer records, if any, will appear as if they had been truncated to
+ the value of `RECL=`.
+ (Other compilers ignore `RECL=`, signal an error, or apply effective truncation
+ to some forms of input in this situation.)
+ For sequential formatted output, RECL= serves as a limit on record lengths
+ that raises an error when it is exceeded.
diff --git a/flang/runtime/connection.h b/flang/runtime/connection.h
index 6e7e0a6b17dc0..0a97130288d31 100644
--- a/flang/runtime/connection.h
+++ b/flang/runtime/connection.h
@@ -25,14 +25,12 @@ enum class Access { Sequential, Direct, Stream };
inline bool IsRecordFile(Access a) { return a != Access::Stream; }
// These characteristics of a connection are immutable after being
-// established in an OPEN statement, except for recordLength,
-// which is immutable only when isFixedRecordLength is true.
+// established in an OPEN statement.
struct ConnectionAttributes {
Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM'
std::optional<bool> isUnformatted; // FORM='UNFORMATTED' if true
bool isUTF8{false}; // ENCODING='UTF-8'
- bool isFixedRecordLength{false}; // RECL= on OPEN
- std::optional<std::int64_t> recordLength; // RECL= or current record
+ std::optional<std::int64_t> openRecl; // RECL= on OPEN
};
struct ConnectionState : public ConnectionAttributes {
@@ -48,6 +46,15 @@ struct ConnectionState : public ConnectionAttributes {
leftTabLimit.reset();
}
+ std::optional<std::int64_t> EffectiveRecordLength() const {
+ // When an input record is longer than an explicit RECL= from OPEN
+ // it is effectively truncated on input.
+ return openRecl && recordLength && *openRecl < *recordLength ? openRecl
+ : recordLength;
+ }
+
+ std::optional<std::int64_t> recordLength;
+
// Positions in a record file (sequential or direct, not stream)
std::int64_t currentRecordNumber{1}; // 1 is first
std::int64_t positionInRecord{0}; // offset in current record
diff --git a/flang/runtime/internal-unit.cpp b/flang/runtime/internal-unit.cpp
index ba274b6b5ace5..e63b2149793f1 100644
--- a/flang/runtime/internal-unit.cpp
+++ b/flang/runtime/internal-unit.cpp
@@ -17,7 +17,6 @@ namespace Fortran::runtime::io {
template <Direction DIR>
InternalDescriptorUnit<DIR>::InternalDescriptorUnit(
Scalar scalar, std::size_t length) {
- isFixedRecordLength = true;
recordLength = length;
endfileRecordNumber = 2;
void *pointer{reinterpret_cast<void *>(const_cast<char *>(scalar))};
@@ -34,7 +33,6 @@ InternalDescriptorUnit<DIR>::InternalDescriptorUnit(
terminator, that.SizeInBytes() <= d.SizeInBytes(maxRank, true, 0));
new (&d) Descriptor{that};
d.Check();
- isFixedRecordLength = true;
recordLength = d.ElementBytes();
endfileRecordNumber = d.Elements() + 1;
}
diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp
index 977e192e6c833..213fd5ddeca77 100644
--- a/flang/runtime/io-api.cpp
+++ b/flang/runtime/io-api.cpp
@@ -543,7 +543,7 @@ bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) {
"REC= may not appear unless ACCESS='DIRECT'");
return false;
}
- if (!connection.isFixedRecordLength || !connection.recordLength) {
+ if (!connection.openRecl) {
io.GetIoErrorHandler().SignalError("RECL= was not specified");
return false;
}
@@ -554,7 +554,7 @@ bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) {
}
connection.currentRecordNumber = rec;
if (auto *unit{io.GetExternalFileUnit()}) {
- unit->SetPosition((rec - 1) * *connection.recordLength);
+ unit->SetPosition((rec - 1) * *connection.openRecl);
}
return true;
}
@@ -827,12 +827,11 @@ bool IONAME(SetRecl)(Cookie cookie, std::size_t n) {
if (n <= 0) {
io.GetIoErrorHandler().SignalError("RECL= must be greater than zero");
}
- if (open->wasExtant() && open->unit().isFixedRecordLength &&
- open->unit().recordLength.value_or(n) != static_cast<std::int64_t>(n)) {
+ if (open->wasExtant() &&
+ open->unit().openRecl.value_or(n) != static_cast<std::int64_t>(n)) {
open->SignalError("RECL= may not be changed for an open unit");
}
- open->unit().isFixedRecordLength = true;
- open->unit().recordLength = n;
+ open->unit().openRecl = n;
return true;
}
diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp
index d003390bf1070..f92b4bb02e8b0 100644
--- a/flang/runtime/io-stmt.cpp
+++ b/flang/runtime/io-stmt.cpp
@@ -888,7 +888,7 @@ bool InquireUnitState::Inquire(
case HashInquiryKeyword("DIRECT"):
str = !unit().IsConnected() ? "UNKNOWN"
: unit().access == Access::Direct ||
- (unit().mayPosition() && unit().isFixedRecordLength)
+ (unit().mayPosition() && unit().openRecl)
? "YES"
: "NO";
break;
@@ -1056,8 +1056,8 @@ bool InquireUnitState::Inquire(
result = -1;
} else if (unit().access == Access::Stream) {
result = -2;
- } else if (unit().isFixedRecordLength && unit().recordLength) {
- result = *unit().recordLength;
+ } else if (unit().openRecl) {
+ result = *unit().openRecl;
} else {
result = std::numeric_limits<std::uint32_t>::max();
}
diff --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h
index ca3a6db264570..065fc2f096051 100644
--- a/flang/runtime/io-stmt.h
+++ b/flang/runtime/io-stmt.h
@@ -192,17 +192,20 @@ class IoStatementState {
return next;
}
const ConnectionState &connection{GetConnectionState()};
- if (!connection.IsAtEOF() && connection.recordLength &&
- connection.positionInRecord >= *connection.recordLength) {
- IoErrorHandler &handler{GetIoErrorHandler()};
- if (mutableModes().nonAdvancing) {
- handler.SignalEor();
- } else if (connection.isFixedRecordLength && !connection.modes.pad) {
- handler.SignalError(IostatRecordReadOverrun);
- }
- if (connection.modes.pad) { // PAD='YES'
- --*remaining;
- return std::optional<char32_t>{' '};
+ if (!connection.IsAtEOF()) {
+ if (auto length{connection.EffectiveRecordLength()}) {
+ if (connection.positionInRecord >= *length) {
+ IoErrorHandler &handler{GetIoErrorHandler()};
+ if (mutableModes().nonAdvancing) {
+ handler.SignalEor();
+ } else if (connection.openRecl && !connection.modes.pad) {
+ handler.SignalError(IostatRecordReadOverrun);
+ }
+ if (connection.modes.pad) { // PAD='YES'
+ --*remaining;
+ return std::optional<char32_t>{' '};
+ }
+ }
}
}
}
diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp
index f1d2b2ca95fe6..2cef9a014a182 100644
--- a/flang/runtime/unit.cpp
+++ b/flang/runtime/unit.cpp
@@ -129,26 +129,27 @@ void ExternalFileUnit::OpenUnit(std::optional<OpenStatus> status,
Open(status.value_or(OpenStatus::Unknown), action, position, handler);
auto totalBytes{knownSize()};
if (access == Access::Direct) {
- if (!isFixedRecordLength || !recordLength) {
+ if (!openRecl) {
handler.SignalError(IostatOpenBadRecl,
"OPEN(UNIT=%d,ACCESS='DIRECT'): record length is not known",
unitNumber());
- } else if (*recordLength <= 0) {
+ } else if (*openRecl <= 0) {
handler.SignalError(IostatOpenBadRecl,
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is invalid",
- unitNumber(), static_cast<std::intmax_t>(*recordLength));
- } else if (totalBytes && (*totalBytes % *recordLength != 0)) {
+ unitNumber(), static_cast<std::intmax_t>(*openRecl));
+ } else if (totalBytes && (*totalBytes % *openRecl != 0)) {
handler.SignalError(IostatOpenBadAppend,
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is not an "
"even divisor of the file size %jd",
- unitNumber(), static_cast<std::intmax_t>(*recordLength),
+ unitNumber(), static_cast<std::intmax_t>(*openRecl),
static_cast<std::intmax_t>(*totalBytes));
}
+ recordLength = openRecl;
}
endfileRecordNumber.reset();
currentRecordNumber = 1;
- if (totalBytes && recordLength && *recordLength) {
- endfileRecordNumber = 1 + (*totalBytes / *recordLength);
+ if (totalBytes && access == Access::Direct && openRecl.value_or(0) > 0) {
+ endfileRecordNumber = 1 + (*totalBytes / *openRecl);
}
if (position == Position::Append) {
if (!endfileRecordNumber) {
@@ -276,21 +277,27 @@ bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
std::size_t elementBytes, IoErrorHandler &handler) {
auto furthestAfter{std::max(furthestPositionInRecord,
positionInRecord + static_cast<std::int64_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 or non advancing input statement.
- if (!isFixedRecordLength) {
- recordLength.reset();
- beganReadingRecord_ = false;
- } else if (furthestAfter > *recordLength) {
+ if (openRecl) {
+ // Check for fixed-length record overrun, but allow for
+ // the unformatted sequential record header & footer, if any.
+ int extra{access == Access::Sequential && isUnformatted && *isUnformatted
+ ? static_cast<int>(sizeof(std::uint32_t))
+ : 0};
+ if (furthestAfter > 2 * extra + *openRecl) {
handler.SignalError(IostatRecordWriteOverrun,
"Attempt to write %zd bytes to position %jd in a fixed-size record "
"of %jd bytes",
- bytes, static_cast<std::intmax_t>(positionInRecord),
- static_cast<std::intmax_t>(*recordLength));
+ bytes,
+ static_cast<std::intmax_t>(positionInRecord - extra /*header*/),
+ static_cast<std::intmax_t>(*openRecl));
return false;
}
+ } else 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 or non advancing input statement.
+ recordLength.reset();
+ beganReadingRecord_ = false;
}
WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler);
if (positionInRecord > furthestPositionInRecord) {
@@ -340,7 +347,9 @@ std::size_t ExternalFileUnit::GetNextInputBytes(
const char *&p, IoErrorHandler &handler) {
RUNTIME_CHECK(handler, direction_ == Direction::Input);
p = FrameNextInput(handler, 1);
- return p ? recordLength.value_or(positionInRecord + 1) - positionInRecord : 0;
+ return p ? EffectiveRecordLength().value_or(positionInRecord + 1) -
+ positionInRecord
+ : 0;
}
std::optional<char32_t> ExternalFileUnit::GetCurrentChar(
@@ -400,17 +409,20 @@ bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
RUNTIME_CHECK(handler, direction_ == Direction::Input);
if (!beganReadingRecord_) {
beganReadingRecord_ = true;
- if (access == Access::Sequential) {
+ if (access == Access::Direct) {
+ RUNTIME_CHECK(handler, openRecl);
+ auto need{static_cast<std::size_t>(recordOffsetInFrame_ + *openRecl)};
+ auto got{ReadFrame(frameOffsetInFile_, need, handler)};
+ if (got >= need) {
+ recordLength = openRecl;
+ } else {
+ recordLength.reset();
+ handler.SignalEnd();
+ }
+ } else if (access == Access::Sequential) {
+ recordLength.reset();
if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
handler.SignalEnd();
- } else if (isFixedRecordLength && access == Access::Direct) {
- RUNTIME_CHECK(handler, recordLength.has_value());
- auto need{
- static_cast<std::size_t>(recordOffsetInFrame_ + *recordLength)};
- auto got{ReadFrame(frameOffsetInFile_, need, handler)};
- if (got < need) {
- handler.SignalEnd();
- }
} else {
RUNTIME_CHECK(handler, isUnformatted.has_value());
if (isUnformatted.value_or(false)) {
@@ -422,8 +434,7 @@ bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
}
}
RUNTIME_CHECK(handler,
- access != Access::Sequential || recordLength.has_value() ||
- handler.InError());
+ recordLength.has_value() || !IsRecordFile(access) || handler.InError());
return !handler.InError();
}
@@ -435,7 +446,7 @@ void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) {
} else if (access == Access::Sequential) {
RUNTIME_CHECK(handler, recordLength.has_value());
recordOffsetInFrame_ += *recordLength;
- if (isFixedRecordLength && access == Access::Direct) {
+ if (openRecl && access == Access::Direct) {
frameOffsetInFile_ += recordOffsetInFrame_;
recordOffsetInFrame_ = 0;
} else {
@@ -472,17 +483,15 @@ bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
} else { // Direction::Output
bool ok{true};
RUNTIME_CHECK(handler, isUnformatted.has_value());
- if (isFixedRecordLength && recordLength &&
- furthestPositionInRecord < *recordLength) {
+ if (openRecl && furthestPositionInRecord < *openRecl) {
// Pad remainder of fixed length record
- WriteFrame(
- frameOffsetInFile_, recordOffsetInFrame_ + *recordLength, handler);
+ WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + *openRecl, handler);
std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord,
isUnformatted.value_or(false) ? 0 : ' ',
- *recordLength - furthestPositionInRecord);
- furthestPositionInRecord = *recordLength;
+ *openRecl - furthestPositionInRecord);
+ furthestPositionInRecord = *openRecl;
}
- if (!(isFixedRecordLength && access == Access::Direct)) {
+ if (!(openRecl && access == Access::Direct)) {
positionInRecord = furthestPositionInRecord;
if (isUnformatted.value_or(false)) {
// Append the length of a sequential unformatted variable-length record
@@ -527,7 +536,7 @@ void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
DoImpliedEndfile(handler);
if (frameOffsetInFile_ + recordOffsetInFrame_ > 0) {
--currentRecordNumber;
- if (isFixedRecordLength && access == Access::Direct) {
+ if (openRecl && access == Access::Direct) {
BackspaceFixedRecord(handler);
} else {
RUNTIME_CHECK(handler, isUnformatted.has_value());
@@ -670,11 +679,11 @@ void ExternalFileUnit::BeginSequentialVariableFormattedInputRecord(
}
void ExternalFileUnit::BackspaceFixedRecord(IoErrorHandler &handler) {
- RUNTIME_CHECK(handler, recordLength.has_value());
- if (frameOffsetInFile_ < *recordLength) {
+ RUNTIME_CHECK(handler, openRecl.has_value());
+ if (frameOffsetInFile_ < *openRecl) {
handler.SignalError(IostatBackspaceAtFirstRecord);
} else {
- frameOffsetInFile_ -= *recordLength;
+ frameOffsetInFile_ -= *openRecl;
}
}
More information about the flang-commits
mailing list