forked from OSchip/llvm-project
[flang] Runtime must defer formatted/unformatted determination
What the Fortran standard calls "preconnected" external I/O units might not be known to be connected to unformatted or formatted files until the first I/O data transfer statement is executed. Support this deferred determination by representing the flag as a tri-state Boolean and adapting its points of use. Differential Revision: https://reviews.llvm.org/D101929
This commit is contained in:
parent
642df18f14
commit
199a623ebf
|
@ -26,7 +26,7 @@ inline bool IsRecordFile(Access a) { return a != Access::Stream; }
|
|||
// established in an OPEN statement.
|
||||
struct ConnectionAttributes {
|
||||
Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM'
|
||||
bool isUnformatted{false}; // FORM='UNFORMATTED'
|
||||
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
|
||||
|
|
|
@ -155,12 +155,15 @@ Cookie BeginExternalListIO(
|
|||
unitNumber = DIR == Direction::Input ? 5 : 6;
|
||||
}
|
||||
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||
unitNumber, DIR, false /*formatted*/, terminator)};
|
||||
unitNumber, DIR, false /*!unformatted*/, terminator)};
|
||||
if (unit.access == Access::Direct) {
|
||||
terminator.Crash("List-directed I/O attempted on direct access file");
|
||||
return nullptr;
|
||||
}
|
||||
if (unit.isUnformatted) {
|
||||
if (!unit.isUnformatted.has_value()) {
|
||||
unit.isUnformatted = false;
|
||||
}
|
||||
if (*unit.isUnformatted) {
|
||||
terminator.Crash("List-directed I/O attempted on unformatted file");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -191,8 +194,11 @@ Cookie BeginExternalFormattedIO(const char *format, std::size_t formatLength,
|
|||
unitNumber = DIR == Direction::Input ? 5 : 6;
|
||||
}
|
||||
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||
unitNumber, DIR, false /*formatted*/, terminator)};
|
||||
if (unit.isUnformatted) {
|
||||
unitNumber, DIR, false /*!unformatted*/, terminator)};
|
||||
if (!unit.isUnformatted.has_value()) {
|
||||
unit.isUnformatted = false;
|
||||
}
|
||||
if (*unit.isUnformatted) {
|
||||
terminator.Crash("Formatted I/O attempted on unformatted file");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -224,8 +230,11 @@ Cookie BeginUnformattedIO(
|
|||
Terminator terminator{sourceFile, sourceLine};
|
||||
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||
unitNumber, DIR, true /*unformatted*/, terminator)};
|
||||
if (!unit.isUnformatted) {
|
||||
terminator.Crash("Unformatted output attempted on formatted file");
|
||||
if (!unit.isUnformatted.has_value()) {
|
||||
unit.isUnformatted = true;
|
||||
}
|
||||
if (!*unit.isUnformatted) {
|
||||
terminator.Crash("Unformatted I/O attempted on formatted file");
|
||||
}
|
||||
IoStatementState &io{unit.BeginIoStatement<UnformattedIoStatementState<DIR>>(
|
||||
unit, sourceFile, sourceLine)};
|
||||
|
@ -310,7 +319,7 @@ Cookie IONAME(BeginEndfile)(
|
|||
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
|
||||
Terminator terminator{sourceFile, sourceLine};
|
||||
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||
unitNumber, Direction::Output, false /*formatted*/, terminator)};
|
||||
unitNumber, Direction::Output, std::nullopt, terminator)};
|
||||
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
|
||||
unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
|
||||
}
|
||||
|
@ -319,7 +328,7 @@ Cookie IONAME(BeginRewind)(
|
|||
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
|
||||
Terminator terminator{sourceFile, sourceLine};
|
||||
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||
unitNumber, Direction::Input, false /*formatted*/, terminator)};
|
||||
unitNumber, Direction::Input, std::nullopt, terminator)};
|
||||
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
|
||||
unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
|
||||
}
|
||||
|
|
|
@ -203,10 +203,10 @@ int OpenStatementState::EndIoStatement() {
|
|||
}
|
||||
unit().access = *access_;
|
||||
}
|
||||
if (!isUnformatted_) {
|
||||
isUnformatted_ = unit().access != Access::Sequential;
|
||||
if (!unit().isUnformatted) {
|
||||
unit().isUnformatted = isUnformatted_;
|
||||
}
|
||||
if (*isUnformatted_ != unit().isUnformatted) {
|
||||
if (isUnformatted_ && *isUnformatted_ != *unit().isUnformatted) {
|
||||
if (wasExtant_) {
|
||||
SignalError("FORM= may not be changed on an open unit");
|
||||
}
|
||||
|
@ -757,7 +757,7 @@ bool InquireUnitState::Inquire(
|
|||
str = unit().mayAsynchronous() ? "YES" : "NO";
|
||||
break;
|
||||
case HashInquiryKeyword("BLANK"):
|
||||
str = unit().isUnformatted ? "UNDEFINED"
|
||||
str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
|
||||
: unit().modes.editingFlags & blankZero ? "ZERO"
|
||||
: "NULL";
|
||||
break;
|
||||
|
@ -768,12 +768,12 @@ bool InquireUnitState::Inquire(
|
|||
str = unit().swapEndianness() ? "SWAP" : "NATIVE";
|
||||
break;
|
||||
case HashInquiryKeyword("DECIMAL"):
|
||||
str = unit().isUnformatted ? "UNDEFINED"
|
||||
str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
|
||||
: unit().modes.editingFlags & decimalComma ? "COMMA"
|
||||
: "POINT";
|
||||
break;
|
||||
case HashInquiryKeyword("DELIM"):
|
||||
if (unit().isUnformatted) {
|
||||
if (unit().isUnformatted.value_or(true)) {
|
||||
str = "UNDEFINED";
|
||||
} else {
|
||||
switch (unit().modes.delim) {
|
||||
|
@ -796,15 +796,19 @@ bool InquireUnitState::Inquire(
|
|||
: "NO";
|
||||
break;
|
||||
case HashInquiryKeyword("ENCODING"):
|
||||
str = unit().isUnformatted ? "UNDEFINED"
|
||||
: unit().isUTF8 ? "UTF-8"
|
||||
: "ASCII";
|
||||
str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
|
||||
: unit().isUTF8 ? "UTF-8"
|
||||
: "ASCII";
|
||||
break;
|
||||
case HashInquiryKeyword("FORM"):
|
||||
str = unit().isUnformatted ? "UNFORMATTED" : "FORMATTED";
|
||||
str = !unit().isUnformatted ? "UNKNOWN"
|
||||
: *unit().isUnformatted ? "UNFORMATTED"
|
||||
: "FORMATTED";
|
||||
break;
|
||||
case HashInquiryKeyword("FORMATTED"):
|
||||
str = !unit().isUnformatted ? "YES" : "NO";
|
||||
str = !unit().isUnformatted ? "UNKNOWN"
|
||||
: *unit().isUnformatted ? "NO"
|
||||
: "YES";
|
||||
break;
|
||||
case HashInquiryKeyword("NAME"):
|
||||
str = unit().path();
|
||||
|
@ -813,7 +817,9 @@ bool InquireUnitState::Inquire(
|
|||
}
|
||||
break;
|
||||
case HashInquiryKeyword("PAD"):
|
||||
str = unit().isUnformatted ? "UNDEFINED" : unit().modes.pad ? "YES" : "NO";
|
||||
str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
|
||||
: unit().modes.pad ? "YES"
|
||||
: "NO";
|
||||
break;
|
||||
case HashInquiryKeyword("POSITION"):
|
||||
if (unit().access == Access::Direct) {
|
||||
|
@ -837,7 +843,7 @@ bool InquireUnitState::Inquire(
|
|||
str = unit().mayRead() && unit().mayWrite() ? "YES" : "NO";
|
||||
break;
|
||||
case HashInquiryKeyword("ROUND"):
|
||||
if (unit().isUnformatted) {
|
||||
if (unit().isUnformatted.value_or(true)) {
|
||||
str = "UNDEFINED";
|
||||
} else {
|
||||
switch (unit().modes.round) {
|
||||
|
@ -865,7 +871,7 @@ bool InquireUnitState::Inquire(
|
|||
str = unit().access == Access::Sequential ? "YES" : "NO";
|
||||
break;
|
||||
case HashInquiryKeyword("SIGN"):
|
||||
str = unit().isUnformatted ? "UNDEFINED"
|
||||
str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
|
||||
: unit().modes.editingFlags & signPlus ? "PLUS"
|
||||
: "SUPPRESS";
|
||||
break;
|
||||
|
@ -876,7 +882,9 @@ bool InquireUnitState::Inquire(
|
|||
str = unit().mayWrite() ? "YES" : "NO";
|
||||
break;
|
||||
case HashInquiryKeyword("UNFORMATTED"):
|
||||
str = unit().isUnformatted ? "YES" : "NO";
|
||||
str = !unit().isUnformatted ? "UNKNOWN"
|
||||
: *unit().isUnformatted ? "YES"
|
||||
: "NO";
|
||||
break;
|
||||
}
|
||||
if (str) {
|
||||
|
|
|
@ -53,8 +53,9 @@ ExternalFileUnit &ExternalFileUnit::LookUpOrCreate(
|
|||
return GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant);
|
||||
}
|
||||
|
||||
ExternalFileUnit &ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||
int unit, Direction dir, bool isUnformatted, const Terminator &terminator) {
|
||||
ExternalFileUnit &ExternalFileUnit::LookUpOrCreateAnonymous(int unit,
|
||||
Direction dir, std::optional<bool> isUnformatted,
|
||||
const Terminator &terminator) {
|
||||
bool exists{false};
|
||||
ExternalFileUnit &result{
|
||||
GetUnitMap().LookUpOrCreate(unit, terminator, exists)};
|
||||
|
@ -313,7 +314,7 @@ std::optional<char32_t> ExternalFileUnit::GetCurrentChar(
|
|||
|
||||
const char *ExternalFileUnit::FrameNextInput(
|
||||
IoErrorHandler &handler, std::size_t bytes) {
|
||||
RUNTIME_CHECK(handler, !isUnformatted);
|
||||
RUNTIME_CHECK(handler, isUnformatted.has_value() && !*isUnformatted);
|
||||
if (static_cast<std::int64_t>(positionInRecord + bytes) <=
|
||||
recordLength.value_or(positionInRecord + bytes)) {
|
||||
auto at{recordOffsetInFrame_ + positionInRecord};
|
||||
|
@ -367,10 +368,13 @@ bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
|
|||
if (got < need) {
|
||||
handler.SignalEnd();
|
||||
}
|
||||
} else if (isUnformatted) {
|
||||
BeginSequentialVariableUnformattedInputRecord(handler);
|
||||
} else { // formatted
|
||||
BeginSequentialVariableFormattedInputRecord(handler);
|
||||
} else {
|
||||
RUNTIME_CHECK(handler, isUnformatted.has_value());
|
||||
if (isUnformatted.value_or(false)) {
|
||||
BeginSequentialVariableUnformattedInputRecord(handler);
|
||||
} else { // formatted
|
||||
BeginSequentialVariableFormattedInputRecord(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -390,18 +394,21 @@ void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) {
|
|||
if (isFixedRecordLength) {
|
||||
frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength;
|
||||
recordOffsetInFrame_ = 0;
|
||||
} else if (isUnformatted) {
|
||||
// Retain footer in frame for more efficient BACKSPACE
|
||||
frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength;
|
||||
recordOffsetInFrame_ = sizeof(std::uint32_t);
|
||||
recordLength.reset();
|
||||
} else { // formatted
|
||||
if (Frame()[recordOffsetInFrame_ + *recordLength] == '\r') {
|
||||
++recordOffsetInFrame_;
|
||||
} else {
|
||||
RUNTIME_CHECK(handler, isUnformatted.has_value());
|
||||
if (isUnformatted.value_or(false)) {
|
||||
// Retain footer in frame for more efficient BACKSPACE
|
||||
frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength;
|
||||
recordOffsetInFrame_ = sizeof(std::uint32_t);
|
||||
recordLength.reset();
|
||||
} else { // formatted
|
||||
if (Frame()[recordOffsetInFrame_ + *recordLength] == '\r') {
|
||||
++recordOffsetInFrame_;
|
||||
}
|
||||
recordOffsetInFrame_ += *recordLength + 1;
|
||||
RUNTIME_CHECK(handler, Frame()[recordOffsetInFrame_ - 1] == '\n');
|
||||
recordLength.reset();
|
||||
}
|
||||
recordOffsetInFrame_ += *recordLength + 1;
|
||||
RUNTIME_CHECK(handler, Frame()[recordOffsetInFrame_ - 1] == '\n');
|
||||
recordLength.reset();
|
||||
}
|
||||
}
|
||||
++currentRecordNumber;
|
||||
|
@ -414,17 +421,19 @@ bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
|
|||
return BeginReadingRecord(handler);
|
||||
} else { // Direction::Output
|
||||
bool ok{true};
|
||||
RUNTIME_CHECK(handler, isUnformatted.has_value());
|
||||
if (isFixedRecordLength && recordLength) {
|
||||
// Pad remainder of fixed length record
|
||||
if (furthestPositionInRecord < *recordLength) {
|
||||
WriteFrame(
|
||||
frameOffsetInFile_, recordOffsetInFrame_ + *recordLength, handler);
|
||||
std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord,
|
||||
isUnformatted ? 0 : ' ', *recordLength - furthestPositionInRecord);
|
||||
isUnformatted.value_or(false) ? 0 : ' ',
|
||||
*recordLength - furthestPositionInRecord);
|
||||
}
|
||||
} else {
|
||||
positionInRecord = furthestPositionInRecord;
|
||||
if (isUnformatted) {
|
||||
if (isUnformatted.value_or(false)) {
|
||||
// Append the length of a sequential unformatted variable-length record
|
||||
// as its footer, then overwrite the reserved first four bytes of the
|
||||
// record with its length as its header. These four bytes were skipped
|
||||
|
@ -466,10 +475,13 @@ void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
|
|||
--currentRecordNumber;
|
||||
if (isFixedRecordLength) {
|
||||
BackspaceFixedRecord(handler);
|
||||
} else if (isUnformatted) {
|
||||
BackspaceVariableUnformattedRecord(handler);
|
||||
} else {
|
||||
BackspaceVariableFormattedRecord(handler);
|
||||
RUNTIME_CHECK(handler, isUnformatted.has_value());
|
||||
if (isUnformatted.value_or(false)) {
|
||||
BackspaceVariableUnformattedRecord(handler);
|
||||
} else {
|
||||
BackspaceVariableFormattedRecord(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,8 +41,8 @@ public:
|
|||
static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &);
|
||||
static ExternalFileUnit &LookUpOrCreate(
|
||||
int unit, const Terminator &, bool &wasExtant);
|
||||
static ExternalFileUnit &LookUpOrCreateAnonymous(
|
||||
int unit, Direction, bool isUnformatted, const Terminator &);
|
||||
static ExternalFileUnit &LookUpOrCreateAnonymous(int unit, Direction,
|
||||
std::optional<bool> isUnformatted, const Terminator &);
|
||||
static ExternalFileUnit *LookUp(const char *path);
|
||||
static ExternalFileUnit &CreateNew(int unit, const Terminator &);
|
||||
static ExternalFileUnit *LookUpForClose(int unit);
|
||||
|
|
Loading…
Reference in New Issue