[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:
peter klausler 2021-05-05 11:33:00 -07:00
parent 642df18f14
commit 199a623ebf
5 changed files with 78 additions and 49 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);
}
}
}
}

View File

@ -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);