Skip to content

Commit 199a623

Browse files
committed
[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
1 parent 642df18 commit 199a623

File tree

5 files changed

+78
-49
lines changed

5 files changed

+78
-49
lines changed

flang/runtime/connection.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ inline bool IsRecordFile(Access a) { return a != Access::Stream; }
2626
// established in an OPEN statement.
2727
struct ConnectionAttributes {
2828
Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM'
29-
bool isUnformatted{false}; // FORM='UNFORMATTED'
29+
std::optional<bool> isUnformatted; // FORM='UNFORMATTED' if true
3030
bool isUTF8{false}; // ENCODING='UTF-8'
3131
bool isFixedRecordLength{false}; // RECL= on OPEN
3232
std::optional<std::int64_t> recordLength; // RECL= or current record

flang/runtime/io-api.cpp

+17-8
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,15 @@ Cookie BeginExternalListIO(
155155
unitNumber = DIR == Direction::Input ? 5 : 6;
156156
}
157157
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
158-
unitNumber, DIR, false /*formatted*/, terminator)};
158+
unitNumber, DIR, false /*!unformatted*/, terminator)};
159159
if (unit.access == Access::Direct) {
160160
terminator.Crash("List-directed I/O attempted on direct access file");
161161
return nullptr;
162162
}
163-
if (unit.isUnformatted) {
163+
if (!unit.isUnformatted.has_value()) {
164+
unit.isUnformatted = false;
165+
}
166+
if (*unit.isUnformatted) {
164167
terminator.Crash("List-directed I/O attempted on unformatted file");
165168
return nullptr;
166169
}
@@ -191,8 +194,11 @@ Cookie BeginExternalFormattedIO(const char *format, std::size_t formatLength,
191194
unitNumber = DIR == Direction::Input ? 5 : 6;
192195
}
193196
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
194-
unitNumber, DIR, false /*formatted*/, terminator)};
195-
if (unit.isUnformatted) {
197+
unitNumber, DIR, false /*!unformatted*/, terminator)};
198+
if (!unit.isUnformatted.has_value()) {
199+
unit.isUnformatted = false;
200+
}
201+
if (*unit.isUnformatted) {
196202
terminator.Crash("Formatted I/O attempted on unformatted file");
197203
return nullptr;
198204
}
@@ -224,8 +230,11 @@ Cookie BeginUnformattedIO(
224230
Terminator terminator{sourceFile, sourceLine};
225231
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
226232
unitNumber, DIR, true /*unformatted*/, terminator)};
227-
if (!unit.isUnformatted) {
228-
terminator.Crash("Unformatted output attempted on formatted file");
233+
if (!unit.isUnformatted.has_value()) {
234+
unit.isUnformatted = true;
235+
}
236+
if (!*unit.isUnformatted) {
237+
terminator.Crash("Unformatted I/O attempted on formatted file");
229238
}
230239
IoStatementState &io{unit.BeginIoStatement<UnformattedIoStatementState<DIR>>(
231240
unit, sourceFile, sourceLine)};
@@ -310,7 +319,7 @@ Cookie IONAME(BeginEndfile)(
310319
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
311320
Terminator terminator{sourceFile, sourceLine};
312321
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
313-
unitNumber, Direction::Output, false /*formatted*/, terminator)};
322+
unitNumber, Direction::Output, std::nullopt, terminator)};
314323
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
315324
unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
316325
}
@@ -319,7 +328,7 @@ Cookie IONAME(BeginRewind)(
319328
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
320329
Terminator terminator{sourceFile, sourceLine};
321330
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
322-
unitNumber, Direction::Input, false /*formatted*/, terminator)};
331+
unitNumber, Direction::Input, std::nullopt, terminator)};
323332
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
324333
unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
325334
}

flang/runtime/io-stmt.cpp

+23-15
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,10 @@ int OpenStatementState::EndIoStatement() {
203203
}
204204
unit().access = *access_;
205205
}
206-
if (!isUnformatted_) {
207-
isUnformatted_ = unit().access != Access::Sequential;
206+
if (!unit().isUnformatted) {
207+
unit().isUnformatted = isUnformatted_;
208208
}
209-
if (*isUnformatted_ != unit().isUnformatted) {
209+
if (isUnformatted_ && *isUnformatted_ != *unit().isUnformatted) {
210210
if (wasExtant_) {
211211
SignalError("FORM= may not be changed on an open unit");
212212
}
@@ -757,7 +757,7 @@ bool InquireUnitState::Inquire(
757757
str = unit().mayAsynchronous() ? "YES" : "NO";
758758
break;
759759
case HashInquiryKeyword("BLANK"):
760-
str = unit().isUnformatted ? "UNDEFINED"
760+
str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
761761
: unit().modes.editingFlags & blankZero ? "ZERO"
762762
: "NULL";
763763
break;
@@ -768,12 +768,12 @@ bool InquireUnitState::Inquire(
768768
str = unit().swapEndianness() ? "SWAP" : "NATIVE";
769769
break;
770770
case HashInquiryKeyword("DECIMAL"):
771-
str = unit().isUnformatted ? "UNDEFINED"
771+
str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
772772
: unit().modes.editingFlags & decimalComma ? "COMMA"
773773
: "POINT";
774774
break;
775775
case HashInquiryKeyword("DELIM"):
776-
if (unit().isUnformatted) {
776+
if (unit().isUnformatted.value_or(true)) {
777777
str = "UNDEFINED";
778778
} else {
779779
switch (unit().modes.delim) {
@@ -796,15 +796,19 @@ bool InquireUnitState::Inquire(
796796
: "NO";
797797
break;
798798
case HashInquiryKeyword("ENCODING"):
799-
str = unit().isUnformatted ? "UNDEFINED"
800-
: unit().isUTF8 ? "UTF-8"
801-
: "ASCII";
799+
str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
800+
: unit().isUTF8 ? "UTF-8"
801+
: "ASCII";
802802
break;
803803
case HashInquiryKeyword("FORM"):
804-
str = unit().isUnformatted ? "UNFORMATTED" : "FORMATTED";
804+
str = !unit().isUnformatted ? "UNKNOWN"
805+
: *unit().isUnformatted ? "UNFORMATTED"
806+
: "FORMATTED";
805807
break;
806808
case HashInquiryKeyword("FORMATTED"):
807-
str = !unit().isUnformatted ? "YES" : "NO";
809+
str = !unit().isUnformatted ? "UNKNOWN"
810+
: *unit().isUnformatted ? "NO"
811+
: "YES";
808812
break;
809813
case HashInquiryKeyword("NAME"):
810814
str = unit().path();
@@ -813,7 +817,9 @@ bool InquireUnitState::Inquire(
813817
}
814818
break;
815819
case HashInquiryKeyword("PAD"):
816-
str = unit().isUnformatted ? "UNDEFINED" : unit().modes.pad ? "YES" : "NO";
820+
str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
821+
: unit().modes.pad ? "YES"
822+
: "NO";
817823
break;
818824
case HashInquiryKeyword("POSITION"):
819825
if (unit().access == Access::Direct) {
@@ -837,7 +843,7 @@ bool InquireUnitState::Inquire(
837843
str = unit().mayRead() && unit().mayWrite() ? "YES" : "NO";
838844
break;
839845
case HashInquiryKeyword("ROUND"):
840-
if (unit().isUnformatted) {
846+
if (unit().isUnformatted.value_or(true)) {
841847
str = "UNDEFINED";
842848
} else {
843849
switch (unit().modes.round) {
@@ -865,7 +871,7 @@ bool InquireUnitState::Inquire(
865871
str = unit().access == Access::Sequential ? "YES" : "NO";
866872
break;
867873
case HashInquiryKeyword("SIGN"):
868-
str = unit().isUnformatted ? "UNDEFINED"
874+
str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
869875
: unit().modes.editingFlags & signPlus ? "PLUS"
870876
: "SUPPRESS";
871877
break;
@@ -876,7 +882,9 @@ bool InquireUnitState::Inquire(
876882
str = unit().mayWrite() ? "YES" : "NO";
877883
break;
878884
case HashInquiryKeyword("UNFORMATTED"):
879-
str = unit().isUnformatted ? "YES" : "NO";
885+
str = !unit().isUnformatted ? "UNKNOWN"
886+
: *unit().isUnformatted ? "YES"
887+
: "NO";
880888
break;
881889
}
882890
if (str) {

flang/runtime/unit.cpp

+35-23
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ ExternalFileUnit &ExternalFileUnit::LookUpOrCreate(
5353
return GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant);
5454
}
5555

56-
ExternalFileUnit &ExternalFileUnit::LookUpOrCreateAnonymous(
57-
int unit, Direction dir, bool isUnformatted, const Terminator &terminator) {
56+
ExternalFileUnit &ExternalFileUnit::LookUpOrCreateAnonymous(int unit,
57+
Direction dir, std::optional<bool> isUnformatted,
58+
const Terminator &terminator) {
5859
bool exists{false};
5960
ExternalFileUnit &result{
6061
GetUnitMap().LookUpOrCreate(unit, terminator, exists)};
@@ -313,7 +314,7 @@ std::optional<char32_t> ExternalFileUnit::GetCurrentChar(
313314

314315
const char *ExternalFileUnit::FrameNextInput(
315316
IoErrorHandler &handler, std::size_t bytes) {
316-
RUNTIME_CHECK(handler, !isUnformatted);
317+
RUNTIME_CHECK(handler, isUnformatted.has_value() && !*isUnformatted);
317318
if (static_cast<std::int64_t>(positionInRecord + bytes) <=
318319
recordLength.value_or(positionInRecord + bytes)) {
319320
auto at{recordOffsetInFrame_ + positionInRecord};
@@ -367,10 +368,13 @@ bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
367368
if (got < need) {
368369
handler.SignalEnd();
369370
}
370-
} else if (isUnformatted) {
371-
BeginSequentialVariableUnformattedInputRecord(handler);
372-
} else { // formatted
373-
BeginSequentialVariableFormattedInputRecord(handler);
371+
} else {
372+
RUNTIME_CHECK(handler, isUnformatted.has_value());
373+
if (isUnformatted.value_or(false)) {
374+
BeginSequentialVariableUnformattedInputRecord(handler);
375+
} else { // formatted
376+
BeginSequentialVariableFormattedInputRecord(handler);
377+
}
374378
}
375379
}
376380
}
@@ -390,18 +394,21 @@ void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) {
390394
if (isFixedRecordLength) {
391395
frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength;
392396
recordOffsetInFrame_ = 0;
393-
} else if (isUnformatted) {
394-
// Retain footer in frame for more efficient BACKSPACE
395-
frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength;
396-
recordOffsetInFrame_ = sizeof(std::uint32_t);
397-
recordLength.reset();
398-
} else { // formatted
399-
if (Frame()[recordOffsetInFrame_ + *recordLength] == '\r') {
400-
++recordOffsetInFrame_;
397+
} else {
398+
RUNTIME_CHECK(handler, isUnformatted.has_value());
399+
if (isUnformatted.value_or(false)) {
400+
// Retain footer in frame for more efficient BACKSPACE
401+
frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength;
402+
recordOffsetInFrame_ = sizeof(std::uint32_t);
403+
recordLength.reset();
404+
} else { // formatted
405+
if (Frame()[recordOffsetInFrame_ + *recordLength] == '\r') {
406+
++recordOffsetInFrame_;
407+
}
408+
recordOffsetInFrame_ += *recordLength + 1;
409+
RUNTIME_CHECK(handler, Frame()[recordOffsetInFrame_ - 1] == '\n');
410+
recordLength.reset();
401411
}
402-
recordOffsetInFrame_ += *recordLength + 1;
403-
RUNTIME_CHECK(handler, Frame()[recordOffsetInFrame_ - 1] == '\n');
404-
recordLength.reset();
405412
}
406413
}
407414
++currentRecordNumber;
@@ -414,17 +421,19 @@ bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
414421
return BeginReadingRecord(handler);
415422
} else { // Direction::Output
416423
bool ok{true};
424+
RUNTIME_CHECK(handler, isUnformatted.has_value());
417425
if (isFixedRecordLength && recordLength) {
418426
// Pad remainder of fixed length record
419427
if (furthestPositionInRecord < *recordLength) {
420428
WriteFrame(
421429
frameOffsetInFile_, recordOffsetInFrame_ + *recordLength, handler);
422430
std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord,
423-
isUnformatted ? 0 : ' ', *recordLength - furthestPositionInRecord);
431+
isUnformatted.value_or(false) ? 0 : ' ',
432+
*recordLength - furthestPositionInRecord);
424433
}
425434
} else {
426435
positionInRecord = furthestPositionInRecord;
427-
if (isUnformatted) {
436+
if (isUnformatted.value_or(false)) {
428437
// Append the length of a sequential unformatted variable-length record
429438
// as its footer, then overwrite the reserved first four bytes of the
430439
// record with its length as its header. These four bytes were skipped
@@ -466,10 +475,13 @@ void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
466475
--currentRecordNumber;
467476
if (isFixedRecordLength) {
468477
BackspaceFixedRecord(handler);
469-
} else if (isUnformatted) {
470-
BackspaceVariableUnformattedRecord(handler);
471478
} else {
472-
BackspaceVariableFormattedRecord(handler);
479+
RUNTIME_CHECK(handler, isUnformatted.has_value());
480+
if (isUnformatted.value_or(false)) {
481+
BackspaceVariableUnformattedRecord(handler);
482+
} else {
483+
BackspaceVariableFormattedRecord(handler);
484+
}
473485
}
474486
}
475487
}

flang/runtime/unit.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ class ExternalFileUnit : public ConnectionState,
4141
static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &);
4242
static ExternalFileUnit &LookUpOrCreate(
4343
int unit, const Terminator &, bool &wasExtant);
44-
static ExternalFileUnit &LookUpOrCreateAnonymous(
45-
int unit, Direction, bool isUnformatted, const Terminator &);
44+
static ExternalFileUnit &LookUpOrCreateAnonymous(int unit, Direction,
45+
std::optional<bool> isUnformatted, const Terminator &);
4646
static ExternalFileUnit *LookUp(const char *path);
4747
static ExternalFileUnit &CreateNew(int unit, const Terminator &);
4848
static ExternalFileUnit *LookUpForClose(int unit);

0 commit comments

Comments
 (0)