[flang-commits] [flang] 675ad1b - [flang] Implement runtime support for INQUIRE statements
peter klausler via flang-commits
flang-commits at lists.llvm.org
Mon Aug 3 17:15:29 PDT 2020
Author: peter klausler
Date: 2020-08-03T17:15:08-07:00
New Revision: 675ad1bc6a96d3c7ef1909c91695189cd818a143
URL: https://github.com/llvm/llvm-project/commit/675ad1bc6a96d3c7ef1909c91695189cd818a143
DIFF: https://github.com/llvm/llvm-project/commit/675ad1bc6a96d3c7ef1909c91695189cd818a143.diff
LOG: [flang] Implement runtime support for INQUIRE statements
Differential Revision: https://reviews.llvm.org/D85166
Added:
Modified:
flang/runtime/io-api.cpp
flang/runtime/io-api.h
flang/runtime/io-error.h
flang/runtime/io-stmt.cpp
flang/runtime/io-stmt.h
flang/runtime/memory.h
flang/runtime/tools.cpp
flang/runtime/tools.h
flang/runtime/unit-map.cpp
flang/runtime/unit-map.h
flang/runtime/unit.cpp
flang/runtime/unit.h
Removed:
################################################################################
diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp
index f36144d0c3c4..f64fe97b2d23 100644
--- a/flang/runtime/io-api.cpp
+++ b/flang/runtime/io-api.cpp
@@ -23,6 +23,23 @@
namespace Fortran::runtime::io {
+const char *InquiryKeywordHashDecode(
+ char *buffer, std::size_t n, InquiryKeywordHash hash) {
+ if (n < 1) {
+ return nullptr;
+ }
+ char *p{buffer + n};
+ *--p = '\0';
+ while (hash > 1) {
+ if (p < buffer) {
+ return nullptr;
+ }
+ *--p = 'A' + (hash % 26);
+ hash /= 26;
+ }
+ return hash == 1 ? p : nullptr;
+}
+
template <Direction DIR>
Cookie BeginInternalArrayListIO(const Descriptor &descriptor,
void ** /*scratchArea*/, std::size_t /*scratchBytes*/,
@@ -289,8 +306,8 @@ Cookie IONAME(BeginBackspace)(
Cookie IONAME(BeginEndfile)(
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
- ExternalFileUnit &unit{
- ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)};
+ ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
+ unitNumber, Direction::Output, true /*formatted*/, terminator)};
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
}
@@ -298,12 +315,50 @@ Cookie IONAME(BeginEndfile)(
Cookie IONAME(BeginRewind)(
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
- ExternalFileUnit &unit{
- ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)};
+ ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
+ unitNumber, Direction::Input, true /*formatted*/, terminator)};
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
}
+Cookie IONAME(BeginInquireUnit)(
+ ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
+ if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
+ return &unit->BeginIoStatement<InquireUnitState>(
+ *unit, sourceFile, sourceLine);
+ } else {
+ // INQUIRE(UNIT=unrecognized unit)
+ Terminator oom{sourceFile, sourceLine};
+ return &New<InquireNoUnitState>{oom}(sourceFile, sourceLine)
+ .release()
+ ->ioStatementState();
+ }
+}
+
+Cookie IONAME(BeginInquireFile)(const char *path, std::size_t pathLength,
+ const char *sourceFile, int sourceLine) {
+ Terminator oom{sourceFile, sourceLine};
+ auto trimmed{
+ SaveDefaultCharacter(path, TrimTrailingSpaces(path, pathLength), oom)};
+ if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(trimmed.get())}) {
+ // INQUIRE(FILE=) to a connected unit
+ return &unit->BeginIoStatement<InquireUnitState>(
+ *unit, sourceFile, sourceLine);
+ } else {
+ return &New<InquireUnconnectedFileState>{oom}(
+ std::move(trimmed), sourceFile, sourceLine)
+ .release()
+ ->ioStatementState();
+ }
+}
+
+Cookie IONAME(BeginInquireIoLength)(const char *sourceFile, int sourceLine) {
+ Terminator oom{sourceFile, sourceLine};
+ return &New<InquireIOLengthState>{oom}(sourceFile, sourceLine)
+ .release()
+ ->ioStatementState();
+}
+
// Control list items
void IONAME(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr,
@@ -522,29 +577,21 @@ bool IONAME(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) {
io.GetIoErrorHandler().Crash(
"SetAccess() called when not in an OPEN statement");
}
- ConnectionState &connection{open->GetConnectionState()};
- Access access{connection.access};
static const char *keywords[]{"SEQUENTIAL", "DIRECT", "STREAM", nullptr};
switch (IdentifyValue(keyword, length, keywords)) {
case 0:
- access = Access::Sequential;
+ open->set_access(Access::Sequential);
break;
case 1:
- access = Access::Direct;
+ open->set_access(Access::Direct);
break;
case 2:
- access = Access::Stream;
+ open->set_access(Access::Stream);
break;
default:
open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'",
static_cast<int>(length), keyword);
}
- if (access != connection.access) {
- if (open->wasExtant()) {
- open->SignalError("ACCESS= may not be changed on an open unit");
- }
- connection.access = access;
- }
return true;
}
@@ -661,25 +708,18 @@ bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) {
io.GetIoErrorHandler().Crash(
"SetEncoding() called when not in an OPEN statement");
}
- bool isUnformatted{false};
static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr};
switch (IdentifyValue(keyword, length, keywords)) {
case 0:
- isUnformatted = false;
+ open->set_isUnformatted(false);
break;
case 1:
- isUnformatted = true;
+ open->set_isUnformatted(true);
break;
default:
open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'",
static_cast<int>(length), keyword);
}
- if (isUnformatted != open->unit().isUnformatted) {
- if (open->wasExtant()) {
- open->SignalError("FORM= may not be changed on an open unit");
- }
- open->unit().isUnformatted = isUnformatted;
- }
return true;
}
@@ -777,11 +817,10 @@ bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) {
"SetStatus() called when not in an OPEN or CLOSE statement");
}
-bool IONAME(SetFile)(
- Cookie cookie, const char *path, std::size_t chars, int kind) {
+bool IONAME(SetFile)(Cookie cookie, const char *path, std::size_t chars) {
IoStatementState &io{*cookie};
if (auto *open{io.get_if<OpenStatementState>()}) {
- open->set_path(path, chars, kind);
+ open->set_path(path, chars);
return true;
}
io.GetIoErrorHandler().Crash(
@@ -789,7 +828,8 @@ bool IONAME(SetFile)(
return false;
}
-static bool SetInteger(int &x, int kind, int value) {
+template <typename INT>
+static bool SetInteger(INT &x, int kind, std::int64_t value) {
switch (kind) {
case 1:
reinterpret_cast<std::int8_t &>(x) = value;
@@ -798,7 +838,7 @@ static bool SetInteger(int &x, int kind, int value) {
reinterpret_cast<std::int16_t &>(x) = value;
return true;
case 4:
- x = value;
+ reinterpret_cast<std::int32_t &>(x) = value;
return true;
case 8:
reinterpret_cast<std::int64_t &>(x) = value;
@@ -1059,6 +1099,34 @@ void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) {
}
}
+bool IONAME(InquireCharacter)(Cookie cookie, InquiryKeywordHash inquiry,
+ char *result, std::size_t length) {
+ IoStatementState &io{*cookie};
+ return io.Inquire(inquiry, result, length);
+}
+
+bool IONAME(InquireLogical)(
+ Cookie cookie, InquiryKeywordHash inquiry, bool &result) {
+ IoStatementState &io{*cookie};
+ return io.Inquire(inquiry, result);
+}
+
+bool IONAME(InquirePendingId)(Cookie cookie, std::int64_t id, bool &result) {
+ IoStatementState &io{*cookie};
+ return io.Inquire(HashInquiryKeyword("PENDING"), id, result);
+}
+
+bool IONAME(InquireInteger64)(
+ Cookie cookie, InquiryKeywordHash inquiry, std::int64_t &result, int kind) {
+ IoStatementState &io{*cookie};
+ std::int64_t n;
+ if (io.Inquire(inquiry, n)) {
+ SetInteger(result, kind, n);
+ return true;
+ }
+ return false;
+}
+
enum Iostat IONAME(EndIoStatement)(Cookie cookie) {
IoStatementState &io{*cookie};
return static_cast<enum Iostat>(io.EndIoStatement());
diff --git a/flang/runtime/io-api.h b/flang/runtime/io-api.h
index f6ebc63e3f3d..a38152d6ec1c 100644
--- a/flang/runtime/io-api.h
+++ b/flang/runtime/io-api.h
@@ -29,6 +29,26 @@ using ExternalUnit = int;
using AsynchronousId = int;
static constexpr ExternalUnit DefaultUnit{-1}; // READ(*), WRITE(*), PRINT
+// INQUIRE specifiers are encoded as simple base-26 packings of
+// the spellings of their keywords.
+using InquiryKeywordHash = std::uint64_t;
+constexpr InquiryKeywordHash HashInquiryKeyword(const char *p) {
+ InquiryKeywordHash hash{1};
+ while (char ch{*p++}) {
+ std::uint64_t letter{0};
+ if (ch >= 'a' && ch <= 'z') {
+ letter = ch - 'a';
+ } else {
+ letter = ch - 'A';
+ }
+ hash = 26 * hash + letter;
+ }
+ return hash;
+}
+
+const char *InquiryKeywordHashDecode(
+ char *buffer, std::size_t, InquiryKeywordHash);
+
extern "C" {
#define IONAME(name) RTNAME(io##name)
@@ -150,7 +170,7 @@ Cookie IONAME(BeginOpenNewUnit)(
// BeginInquireIoLength() is basically a no-op output statement.
Cookie IONAME(BeginInquireUnit)(
ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
-Cookie IONAME(BeginInquireFile)(const char *, std::size_t, int kind = 1,
+Cookie IONAME(BeginInquireFile)(const char *, std::size_t,
const char *sourceFile = nullptr, int sourceLine = 0);
Cookie IONAME(BeginInquireIoLength)(
const char *sourceFile = nullptr, int sourceLine = 0);
@@ -255,10 +275,7 @@ bool IONAME(SetRecl)(Cookie, std::size_t); // RECL=
// For CLOSE: STATUS=KEEP, DELETE
bool IONAME(SetStatus)(Cookie, const char *, std::size_t);
-// SetFile() may pass a CHARACTER argument of non-default kind,
-// and such filenames are converted to UTF-8 before being
-// presented to the filesystem.
-bool IONAME(SetFile)(Cookie, const char *, std::size_t chars, int kind = 1);
+bool IONAME(SetFile)(Cookie, const char *, std::size_t chars);
// Acquires the runtime-created unit number for OPEN(NEWUNIT=)
bool IONAME(GetNewUnit)(Cookie, int &, int kind = 4);
@@ -275,18 +292,17 @@ void IONAME(GetIoMsg)(Cookie, char *, std::size_t); // IOMSG=
// INQUIRE() specifiers are mostly identified by their NUL-terminated
// case-insensitive names.
-// ACCESS, ACTION, ASYNCHRONOUS, BLANK, DECIMAL, DELIM, DIRECT, ENCODING,
-// FORM, FORMATTED, NAME, PAD, POSITION, READ, READWRITE, ROUND,
+// ACCESS, ACTION, ASYNCHRONOUS, BLANK, CONVERT, DECIMAL, DELIM, DIRECT,
+// ENCODING, FORM, FORMATTED, NAME, PAD, POSITION, READ, READWRITE, ROUND,
// SEQUENTIAL, SIGN, STREAM, UNFORMATTED, WRITE:
-bool IONAME(InquireCharacter)(
- Cookie, const char *specifier, char *, std::size_t);
+bool IONAME(InquireCharacter)(Cookie, InquiryKeywordHash, char *, std::size_t);
// EXIST, NAMED, OPENED, and PENDING (without ID):
-bool IONAME(InquireLogical)(Cookie, const char *specifier, bool &);
+bool IONAME(InquireLogical)(Cookie, InquiryKeywordHash, bool &);
// PENDING with ID
bool IONAME(InquirePendingId)(Cookie, std::int64_t, bool &);
// NEXTREC, NUMBER, POS, RECL, SIZE
bool IONAME(InquireInteger64)(
- Cookie, const char *specifier, std::int64_t &, int kind = 8);
+ Cookie, InquiryKeywordHash, std::int64_t &, int kind = 8);
// This function must be called to end an I/O statement, and its
// cookie value may not be used afterwards unless it is recycled
diff --git a/flang/runtime/io-error.h b/flang/runtime/io-error.h
index 8d43c40ef103..5dd7f5e03d08 100644
--- a/flang/runtime/io-error.h
+++ b/flang/runtime/io-error.h
@@ -38,7 +38,7 @@ class IoErrorHandler : public Terminator {
void SignalError(int iostatOrErrno, const char *msg, ...);
void SignalError(int iostatOrErrno);
- template <typename... X> void SignalError(const char *msg, X &&... xs) {
+ template <typename... X> void SignalError(const char *msg, X &&...xs) {
SignalError(IostatGenericError, msg, std::forward<X>(xs)...);
}
diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp
index a903f708bc62..8300b1ea3c27 100644
--- a/flang/runtime/io-stmt.cpp
+++ b/flang/runtime/io-stmt.cpp
@@ -26,6 +26,37 @@ std::optional<DataEdit> IoStatementBase::GetNextDataEdit(
return std::nullopt;
}
+bool IoStatementBase::Inquire(InquiryKeywordHash, char *, std::size_t) {
+ Crash(
+ "IoStatementBase::Inquire() called for I/O statement other than INQUIRE");
+ return false;
+}
+
+bool IoStatementBase::Inquire(InquiryKeywordHash, bool &) {
+ Crash(
+ "IoStatementBase::Inquire() called for I/O statement other than INQUIRE");
+ return false;
+}
+
+bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t, bool &) {
+ Crash(
+ "IoStatementBase::Inquire() called for I/O statement other than INQUIRE");
+ return false;
+}
+
+bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t &) {
+ Crash(
+ "IoStatementBase::Inquire() called for I/O statement other than INQUIRE");
+ return false;
+}
+
+void IoStatementBase::BadInquiryKeywordHashCrash(InquiryKeywordHash inquiry) {
+ char buffer[16];
+ const char *decode{InquiryKeywordHashDecode(buffer, sizeof buffer, inquiry)};
+ Crash("bad InquiryKeywordHash 0x%x (%s)", inquiry,
+ decode ? decode : "(cannot decode)");
+}
+
template <Direction DIR, typename CHAR>
InternalIoStatementState<DIR, CHAR>::InternalIoStatementState(
Buffer scalar, std::size_t length, const char *sourceFile, int sourceLine)
@@ -151,14 +182,9 @@ int ExternalIoStatementBase::EndIoStatement() {
return result;
}
-void OpenStatementState::set_path(
- const char *path, std::size_t length, int kind) {
- if (kind != 1) { // TODO
- Crash("OPEN: FILE= with unimplemented: CHARACTER(KIND=%d)", kind);
- }
- std::size_t bytes{length * kind}; // TODO: UTF-8 encoding of Unicode path
- path_ = SaveDefaultCharacter(path, bytes, *this);
- pathLength_ = length;
+void OpenStatementState::set_path(const char *path, std::size_t length) {
+ pathLength_ = TrimTrailingSpaces(path, length);
+ path_ = SaveDefaultCharacter(path, pathLength_, *this);
}
int OpenStatementState::EndIoStatement() {
@@ -166,8 +192,31 @@ int OpenStatementState::EndIoStatement() {
SignalError("OPEN statement for connected unit may not have STATUS= other "
"than 'OLD'");
}
- unit().OpenUnit(status_.value_or(OpenStatus::Unknown), action_, position_,
- std::move(path_), pathLength_, convert_, *this);
+ if (path_.get() || wasExtant_ ||
+ (status_ && *status_ == OpenStatus::Scratch)) {
+ unit().OpenUnit(status_.value_or(OpenStatus::Unknown), action_, position_,
+ std::move(path_), pathLength_, convert_, *this);
+ } else {
+ unit().OpenAnonymousUnit(status_.value_or(OpenStatus::Unknown), action_,
+ position_, convert_, *this);
+ }
+ if (access_) {
+ if (*access_ != unit().access) {
+ if (wasExtant_) {
+ SignalError("ACCESS= may not be changed on an open unit");
+ }
+ }
+ unit().access = *access_;
+ }
+ if (!isUnformatted_) {
+ isUnformatted_ = unit().access != Access::Sequential;
+ }
+ if (*isUnformatted_ != unit().isUnformatted) {
+ if (wasExtant_) {
+ SignalError("FORM= may not be changed on an open unit");
+ }
+ unit().isUnformatted = *isUnformatted_;
+ }
return ExternalIoStatementBase::EndIoStatement();
}
@@ -178,7 +227,7 @@ int CloseStatementState::EndIoStatement() {
return result;
}
-int NoopCloseStatementState::EndIoStatement() {
+int NoUnitIoStatementState::EndIoStatement() {
auto result{IoStatementBase::EndIoStatement()};
FreeMemory(this);
return result;
@@ -454,6 +503,26 @@ bool ListDirectedStatementState<Direction::Output>::NeedAdvance(
width > connection.RemainingSpaceInRecord();
}
+bool IoStatementState::Inquire(
+ InquiryKeywordHash inquiry, char *out, std::size_t chars) {
+ return std::visit(
+ [&](auto &x) { return x.get().Inquire(inquiry, out, chars); }, u_);
+}
+
+bool IoStatementState::Inquire(InquiryKeywordHash inquiry, bool &out) {
+ return std::visit([&](auto &x) { return x.get().Inquire(inquiry, out); }, u_);
+}
+
+bool IoStatementState::Inquire(
+ InquiryKeywordHash inquiry, std::int64_t id, bool &out) {
+ return std::visit(
+ [&](auto &x) { return x.get().Inquire(inquiry, id, out); }, u_);
+}
+
+bool IoStatementState::Inquire(InquiryKeywordHash inquiry, std::int64_t &n) {
+ return std::visit([&](auto &x) { return x.get().Inquire(inquiry, n); }, u_);
+}
+
bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance(
IoStatementState &io, std::size_t length, bool isCharacter) {
if (length == 0) {
@@ -678,4 +747,419 @@ int ExternalMiscIoStatementState::EndIoStatement() {
return ExternalIoStatementBase::EndIoStatement();
}
+InquireUnitState::InquireUnitState(
+ ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
+ : ExternalIoStatementBase{unit, sourceFile, sourceLine} {}
+
+bool InquireUnitState::Inquire(
+ InquiryKeywordHash inquiry, char *result, std::size_t length) {
+ const char *str{nullptr};
+ switch (inquiry) {
+ case HashInquiryKeyword("ACCESS"):
+ switch (unit().access) {
+ case Access::Sequential:
+ str = "SEQUENTIAL";
+ break;
+ case Access::Direct:
+ str = "DIRECT";
+ break;
+ case Access::Stream:
+ str = "STREAM";
+ break;
+ }
+ break;
+ case HashInquiryKeyword("ACTION"):
+ str = unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE" : "READ";
+ break;
+ case HashInquiryKeyword("ASYNCHRONOUS"):
+ str = unit().mayAsynchronous() ? "YES" : "NO";
+ break;
+ case HashInquiryKeyword("BLANK"):
+ str = unit().isUnformatted ? "UNDEFINED"
+ : unit().modes.editingFlags & blankZero ? "ZERO"
+ : "NULL";
+ break;
+ case HashInquiryKeyword("CONVERT"):
+ str = unit().swapEndianness() ? "SWAP" : "NATIVE";
+ break;
+ case HashInquiryKeyword("DECIMAL"):
+ str = unit().isUnformatted ? "UNDEFINED"
+ : unit().modes.editingFlags & decimalComma ? "COMMA"
+ : "POINT";
+ break;
+ case HashInquiryKeyword("DELIM"):
+ if (unit().isUnformatted) {
+ str = "UNDEFINED";
+ } else {
+ switch (unit().modes.delim) {
+ case '\'':
+ str = "APOSTROPHE";
+ break;
+ case '"':
+ str = "QUOTE";
+ break;
+ default:
+ str = "NONE";
+ break;
+ }
+ }
+ break;
+ case HashInquiryKeyword("DIRECT"):
+ str = unit().mayPosition() ? "YES" : "NO";
+ break;
+ case HashInquiryKeyword("ENCODING"):
+ str = unit().isUnformatted ? "UNDEFINED"
+ : unit().isUTF8 ? "UTF-8"
+ : "ASCII";
+ break;
+ case HashInquiryKeyword("FORM"):
+ str = unit().isUnformatted ? "UNFORMATTED" : "FORMATTED";
+ break;
+ case HashInquiryKeyword("FORMATTED"):
+ str = "YES";
+ break;
+ case HashInquiryKeyword("NAME"):
+ str = unit().path();
+ if (!str) {
+ return true; // result is undefined
+ }
+ break;
+ case HashInquiryKeyword("PAD"):
+ str = unit().isUnformatted ? "UNDEFINED" : unit().modes.pad ? "YES" : "NO";
+ break;
+ case HashInquiryKeyword("POSITION"):
+ if (unit().access == Access::Direct) {
+ str = "UNDEFINED";
+ } else {
+ auto size{unit().knownSize()};
+ auto pos{unit().position()};
+ if (pos == size.value_or(pos + 1)) {
+ str = "APPEND";
+ } else if (pos == 0) {
+ str = "REWIND";
+ } else {
+ str = "ASIS"; // processor-dependent & no common behavior
+ }
+ }
+ break;
+ case HashInquiryKeyword("READ"):
+ str = unit().mayRead() ? "YES" : "NO";
+ break;
+ case HashInquiryKeyword("READWRITE"):
+ str = unit().mayRead() && unit().mayWrite() ? "YES" : "NO";
+ break;
+ case HashInquiryKeyword("ROUND"):
+ if (unit().isUnformatted) {
+ str = "UNDEFINED";
+ } else {
+ switch (unit().modes.round) {
+ case decimal::FortranRounding::RoundNearest:
+ str = "NEAREST";
+ break;
+ case decimal::FortranRounding::RoundUp:
+ str = "UP";
+ break;
+ case decimal::FortranRounding::RoundDown:
+ str = "DOWN";
+ break;
+ case decimal::FortranRounding::RoundToZero:
+ str = "ZERO";
+ break;
+ case decimal::FortranRounding::RoundCompatible:
+ str = "COMPATIBLE";
+ break;
+ }
+ }
+ break;
+ case HashInquiryKeyword("SEQUENTIAL"):
+ str = "YES";
+ break;
+ case HashInquiryKeyword("SIGN"):
+ str = unit().isUnformatted ? "UNDEFINED"
+ : unit().modes.editingFlags & signPlus ? "PLUS"
+ : "SUPPRESS";
+ break;
+ case HashInquiryKeyword("STREAM"):
+ str = "YES";
+ break;
+ case HashInquiryKeyword("WRITE"):
+ str = unit().mayWrite() ? "YES" : "NO";
+ break;
+ case HashInquiryKeyword("UNFORMATTED"):
+ str = "YES";
+ break;
+ }
+ if (str) {
+ ToFortranDefaultCharacter(result, length, str);
+ return true;
+ } else {
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+bool InquireUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
+ switch (inquiry) {
+ case HashInquiryKeyword("EXIST"):
+ result = true;
+ return true;
+ case HashInquiryKeyword("NAMED"):
+ result = unit().path() != nullptr;
+ return true;
+ case HashInquiryKeyword("OPENED"):
+ result = true;
+ return true;
+ case HashInquiryKeyword("PENDING"):
+ result = false; // asynchronous I/O is not implemented
+ return true;
+ default:
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+bool InquireUnitState::Inquire(
+ InquiryKeywordHash inquiry, std::int64_t, bool &result) {
+ switch (inquiry) {
+ case HashInquiryKeyword("PENDING"):
+ result = false; // asynchronous I/O is not implemented
+ return true;
+ default:
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+bool InquireUnitState::Inquire(
+ InquiryKeywordHash inquiry, std::int64_t &result) {
+ switch (inquiry) {
+ case HashInquiryKeyword("NEXTREC"):
+ if (unit().access == Access::Direct) {
+ result = unit().currentRecordNumber;
+ }
+ return true;
+ case HashInquiryKeyword("NUMBER"):
+ result = unit().unitNumber();
+ return true;
+ case HashInquiryKeyword("POS"):
+ result = unit().position();
+ return true;
+ case HashInquiryKeyword("RECL"):
+ if (unit().access == Access::Stream) {
+ result = -2;
+ } else if (unit().isFixedRecordLength && unit().recordLength) {
+ result = *unit().recordLength;
+ } else {
+ result = std::numeric_limits<std::uint32_t>::max();
+ }
+ return true;
+ case HashInquiryKeyword("SIZE"):
+ if (auto size{unit().knownSize()}) {
+ result = *size;
+ } else {
+ result = -1;
+ }
+ return true;
+ default:
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+InquireNoUnitState::InquireNoUnitState(const char *sourceFile, int sourceLine)
+ : NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
+
+bool InquireNoUnitState::Inquire(
+ InquiryKeywordHash inquiry, char *result, std::size_t length) {
+ switch (inquiry) {
+ case HashInquiryKeyword("ACCESS"):
+ case HashInquiryKeyword("ACTION"):
+ case HashInquiryKeyword("ASYNCHRONOUS"):
+ case HashInquiryKeyword("BLANK"):
+ case HashInquiryKeyword("CONVERT"):
+ case HashInquiryKeyword("DECIMAL"):
+ case HashInquiryKeyword("DELIM"):
+ case HashInquiryKeyword("FORM"):
+ case HashInquiryKeyword("NAME"):
+ case HashInquiryKeyword("PAD"):
+ case HashInquiryKeyword("POSITION"):
+ case HashInquiryKeyword("ROUND"):
+ case HashInquiryKeyword("SIGN"):
+ ToFortranDefaultCharacter(result, length, "UNDEFINED");
+ return true;
+ case HashInquiryKeyword("DIRECT"):
+ case HashInquiryKeyword("ENCODING"):
+ case HashInquiryKeyword("FORMATTED"):
+ case HashInquiryKeyword("READ"):
+ case HashInquiryKeyword("READWRITE"):
+ case HashInquiryKeyword("SEQUENTIAL"):
+ case HashInquiryKeyword("STREAM"):
+ case HashInquiryKeyword("WRITE"):
+ case HashInquiryKeyword("UNFORMATTED"):
+ ToFortranDefaultCharacter(result, length, "UNKNONN");
+ return true;
+ default:
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+bool InquireNoUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
+ switch (inquiry) {
+ case HashInquiryKeyword("EXIST"):
+ result = true;
+ return true;
+ case HashInquiryKeyword("NAMED"):
+ case HashInquiryKeyword("OPENED"):
+ case HashInquiryKeyword("PENDING"):
+ result = false;
+ return true;
+ default:
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+bool InquireNoUnitState::Inquire(
+ InquiryKeywordHash inquiry, std::int64_t, bool &result) {
+ switch (inquiry) {
+ case HashInquiryKeyword("PENDING"):
+ result = false;
+ return true;
+ default:
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+bool InquireNoUnitState::Inquire(
+ InquiryKeywordHash inquiry, std::int64_t &result) {
+ switch (inquiry) {
+ case HashInquiryKeyword("NEXTREC"):
+ case HashInquiryKeyword("NUMBER"):
+ case HashInquiryKeyword("POS"):
+ case HashInquiryKeyword("RECL"):
+ case HashInquiryKeyword("SIZE"):
+ result = -1;
+ return true;
+ default:
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+InquireUnconnectedFileState::InquireUnconnectedFileState(
+ OwningPtr<char> &&path, const char *sourceFile, int sourceLine)
+ : NoUnitIoStatementState{sourceFile, sourceLine, *this}, path_{std::move(
+ path)} {}
+
+bool InquireUnconnectedFileState::Inquire(
+ InquiryKeywordHash inquiry, char *result, std::size_t length) {
+ const char *str{nullptr};
+ switch (inquiry) {
+ case HashInquiryKeyword("ACCESS"):
+ case HashInquiryKeyword("ACTION"):
+ case HashInquiryKeyword("ASYNCHRONOUS"):
+ case HashInquiryKeyword("BLANK"):
+ case HashInquiryKeyword("CONVERT"):
+ case HashInquiryKeyword("DECIMAL"):
+ case HashInquiryKeyword("DELIM"):
+ case HashInquiryKeyword("FORM"):
+ case HashInquiryKeyword("PAD"):
+ case HashInquiryKeyword("POSITION"):
+ case HashInquiryKeyword("ROUND"):
+ case HashInquiryKeyword("SIGN"):
+ str = "UNDEFINED";
+ break;
+ case HashInquiryKeyword("DIRECT"):
+ case HashInquiryKeyword("ENCODING"):
+ str = "UNKNONN";
+ break;
+ case HashInquiryKeyword("READ"):
+ str = MayRead(path_.get()) ? "YES" : "NO";
+ break;
+ case HashInquiryKeyword("READWRITE"):
+ str = MayReadAndWrite(path_.get()) ? "YES" : "NO";
+ break;
+ case HashInquiryKeyword("WRITE"):
+ str = MayWrite(path_.get()) ? "YES" : "NO";
+ break;
+ case HashInquiryKeyword("FORMATTED"):
+ case HashInquiryKeyword("SEQUENTIAL"):
+ case HashInquiryKeyword("STREAM"):
+ case HashInquiryKeyword("UNFORMATTED"):
+ str = "YES";
+ break;
+ case HashInquiryKeyword("NAME"):
+ str = path_.get();
+ return true;
+ }
+ if (str) {
+ ToFortranDefaultCharacter(result, length, str);
+ return true;
+ } else {
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+bool InquireUnconnectedFileState::Inquire(
+ InquiryKeywordHash inquiry, bool &result) {
+ switch (inquiry) {
+ case HashInquiryKeyword("EXIST"):
+ result = IsExtant(path_.get());
+ return true;
+ case HashInquiryKeyword("NAMED"):
+ result = true;
+ return true;
+ case HashInquiryKeyword("OPENED"):
+ result = false;
+ return true;
+ case HashInquiryKeyword("PENDING"):
+ result = false;
+ return true;
+ default:
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+bool InquireUnconnectedFileState::Inquire(
+ InquiryKeywordHash inquiry, std::int64_t, bool &result) {
+ switch (inquiry) {
+ case HashInquiryKeyword("PENDING"):
+ result = false;
+ return true;
+ default:
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+bool InquireUnconnectedFileState::Inquire(
+ InquiryKeywordHash inquiry, std::int64_t &result) {
+ switch (inquiry) {
+ case HashInquiryKeyword("NEXTREC"):
+ case HashInquiryKeyword("NUMBER"):
+ case HashInquiryKeyword("POS"):
+ case HashInquiryKeyword("RECL"):
+ case HashInquiryKeyword("SIZE"):
+ result = -1;
+ return true;
+ default:
+ BadInquiryKeywordHashCrash(inquiry);
+ return false;
+ }
+}
+
+InquireIOLengthState::InquireIOLengthState(
+ const char *sourceFile, int sourceLine)
+ : NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
+
+bool InquireIOLengthState::Emit(
+ const char *, std::size_t n, std::size_t /*elementBytes*/) {
+ bytes_ += n;
+ return true;
+}
+
} // namespace Fortran::runtime::io
diff --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h
index ddc264aea360..9e68deab2e64 100644
--- a/flang/runtime/io-stmt.h
+++ b/flang/runtime/io-stmt.h
@@ -16,6 +16,7 @@
#include "file.h"
#include "format.h"
#include "internal-unit.h"
+#include "io-api.h"
#include "io-error.h"
#include <functional>
#include <type_traits>
@@ -26,6 +27,11 @@ namespace Fortran::runtime::io {
class ExternalFileUnit;
class OpenStatementState;
+class InquireUnitState;
+class InquireNoUnitState;
+class InquireUnconnectedFileState;
+class InquireIOLengthState;
+class ExternalMiscIoStatementState;
class CloseStatementState;
class NoopCloseStatementState;
@@ -36,7 +42,6 @@ template <Direction, typename CHAR = char>
class ExternalFormattedIoStatementState;
template <Direction> class ExternalListIoStatementState;
template <Direction> class UnformattedIoStatementState;
-class ExternalMiscIoStatementState;
// The Cookie type in the I/O API is a pointer (for C) to this class.
class IoStatementState {
@@ -60,6 +65,10 @@ class IoStatementState {
ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit
MutableModes &mutableModes();
void BeginReadingRecord();
+ bool Inquire(InquiryKeywordHash, char *, std::size_t);
+ bool Inquire(InquiryKeywordHash, bool &);
+ bool Inquire(InquiryKeywordHash, std::int64_t, bool &); // PENDING=
+ bool Inquire(InquiryKeywordHash, std::int64_t &);
// N.B.: this also works with base classes
template <typename A> A *get_if() const {
@@ -98,6 +107,10 @@ class IoStatementState {
std::reference_wrapper<ExternalListIoStatementState<Direction::Input>>,
std::reference_wrapper<UnformattedIoStatementState<Direction::Output>>,
std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>,
+ std::reference_wrapper<InquireUnitState>,
+ std::reference_wrapper<InquireNoUnitState>,
+ std::reference_wrapper<InquireUnconnectedFileState>,
+ std::reference_wrapper<InquireIOLengthState>,
std::reference_wrapper<ExternalMiscIoStatementState>>
u_;
};
@@ -110,6 +123,12 @@ struct IoStatementBase : public DefaultFormatControlCallbacks {
std::optional<DataEdit> GetNextDataEdit(IoStatementState &, int = 1);
ExternalFileUnit *GetExternalFileUnit() const { return nullptr; }
void BeginReadingRecord() {}
+
+ bool Inquire(InquiryKeywordHash, char *, std::size_t);
+ bool Inquire(InquiryKeywordHash, bool &);
+ bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
+ bool Inquire(InquiryKeywordHash, std::int64_t &);
+ void BadInquiryKeywordHashCrash(InquiryKeywordHash);
};
struct InputStatementState {};
@@ -303,10 +322,12 @@ class OpenStatementState : public ExternalIoStatementBase {
wasExtant} {}
bool wasExtant() const { return wasExtant_; }
void set_status(OpenStatus status) { status_ = status; } // STATUS=
- void set_path(const char *, std::size_t, int kind); // FILE=
+ void set_path(const char *, std::size_t); // FILE=
void set_position(Position position) { position_ = position; } // POSITION=
void set_action(Action action) { action_ = action; } // ACTION=
void set_convert(Convert convert) { convert_ = convert; } // CONVERT=
+ void set_access(Access access) { access_ = access; } // ACCESS=
+ void set_isUnformatted(bool yes = true) { isUnformatted_ = yes; } // FORM=
int EndIoStatement();
private:
@@ -317,6 +338,8 @@ class OpenStatementState : public ExternalIoStatementBase {
Convert convert_{Convert::Native};
OwningPtr<char> path_;
std::size_t pathLength_;
+ std::optional<bool> isUnformatted_;
+ std::optional<Access> access_;
};
class CloseStatementState : public ExternalIoStatementBase {
@@ -331,21 +354,31 @@ class CloseStatementState : public ExternalIoStatementBase {
CloseStatus status_{CloseStatus::Keep};
};
-class NoopCloseStatementState : public IoStatementBase {
+// For CLOSE(bad unit) and INQUIRE(unconnected unit)
+class NoUnitIoStatementState : public IoStatementBase {
public:
- NoopCloseStatementState(const char *sourceFile, int sourceLine)
- : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{*this} {}
IoStatementState &ioStatementState() { return ioStatementState_; }
- void set_status(CloseStatus) {} // discards
MutableModes &mutableModes() { return connection_.modes; }
ConnectionState &GetConnectionState() { return connection_; }
int EndIoStatement();
+protected:
+ template <typename A>
+ NoUnitIoStatementState(const char *sourceFile, int sourceLine, A &stmt)
+ : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{stmt} {}
+
private:
IoStatementState ioStatementState_; // points to *this
ConnectionState connection_;
};
+class NoopCloseStatementState : public NoUnitIoStatementState {
+public:
+ NoopCloseStatementState(const char *sourceFile, int sourceLine)
+ : NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
+ void set_status(CloseStatus) {} // discards
+};
+
extern template class InternalIoStatementState<Direction::Output>;
extern template class InternalIoStatementState<Direction::Input>;
extern template class InternalFormattedIoStatementState<Direction::Output>;
@@ -369,6 +402,49 @@ extern template class FormatControl<
extern template class FormatControl<
ExternalFormattedIoStatementState<Direction::Input>>;
+class InquireUnitState : public ExternalIoStatementBase {
+public:
+ InquireUnitState(ExternalFileUnit &unit, const char *sourceFile = nullptr,
+ int sourceLine = 0);
+ bool Inquire(InquiryKeywordHash, char *, std::size_t);
+ bool Inquire(InquiryKeywordHash, bool &);
+ bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
+ bool Inquire(InquiryKeywordHash, std::int64_t &);
+};
+
+class InquireNoUnitState : public NoUnitIoStatementState {
+public:
+ InquireNoUnitState(const char *sourceFile = nullptr, int sourceLine = 0);
+ bool Inquire(InquiryKeywordHash, char *, std::size_t);
+ bool Inquire(InquiryKeywordHash, bool &);
+ bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
+ bool Inquire(InquiryKeywordHash, std::int64_t &);
+};
+
+class InquireUnconnectedFileState : public NoUnitIoStatementState {
+public:
+ InquireUnconnectedFileState(OwningPtr<char> &&path,
+ const char *sourceFile = nullptr, int sourceLine = 0);
+ bool Inquire(InquiryKeywordHash, char *, std::size_t);
+ bool Inquire(InquiryKeywordHash, bool &);
+ bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
+ bool Inquire(InquiryKeywordHash, std::int64_t &);
+
+private:
+ OwningPtr<char> path_; // trimmed and NUL terminated
+};
+
+class InquireIOLengthState : public NoUnitIoStatementState,
+ public OutputStatementState {
+public:
+ InquireIOLengthState(const char *sourceFile = nullptr, int sourceLine = 0);
+ std::size_t bytes() const { return bytes_; }
+ bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
+
+private:
+ std::size_t bytes_{0};
+};
+
class ExternalMiscIoStatementState : public ExternalIoStatementBase {
public:
enum Which { Flush, Backspace, Endfile, Rewind };
diff --git a/flang/runtime/memory.h b/flang/runtime/memory.h
index f21b237f3905..4b09fe80772e 100644
--- a/flang/runtime/memory.h
+++ b/flang/runtime/memory.h
@@ -42,7 +42,7 @@ template <typename A> class SizedNew {
public:
explicit SizedNew(const Terminator &terminator) : terminator_{terminator} {}
template <typename... X>
- [[nodiscard]] OwningPtr<A> operator()(std::size_t bytes, X &&... x) {
+ [[nodiscard]] OwningPtr<A> operator()(std::size_t bytes, X &&...x) {
return OwningPtr<A>{new (AllocateMemoryOrCrash(terminator_, bytes))
A{std::forward<X>(x)...}};
}
@@ -53,7 +53,7 @@ template <typename A> class SizedNew {
template <typename A> struct New : public SizedNew<A> {
using SizedNew<A>::SizedNew;
- template <typename... X> [[nodiscard]] OwningPtr<A> operator()(X &&... x) {
+ template <typename... X> [[nodiscard]] OwningPtr<A> operator()(X &&...x) {
return SizedNew<A>::operator()(sizeof(A), std::forward<X>(x)...);
}
};
diff --git a/flang/runtime/tools.cpp b/flang/runtime/tools.cpp
index ea9ad9063344..219daaf2880b 100644
--- a/flang/runtime/tools.cpp
+++ b/flang/runtime/tools.cpp
@@ -12,6 +12,13 @@
namespace Fortran::runtime {
+std::size_t TrimTrailingSpaces(const char *s, std::size_t n) {
+ while (n > 0 && s[n - 1] == ' ') {
+ --n;
+ }
+ return n;
+}
+
OwningPtr<char> SaveDefaultCharacter(
const char *s, std::size_t length, const Terminator &terminator) {
if (s) {
diff --git a/flang/runtime/tools.h b/flang/runtime/tools.h
index fad19f607c68..6c5eb63cc8c1 100644
--- a/flang/runtime/tools.h
+++ b/flang/runtime/tools.h
@@ -18,6 +18,8 @@ namespace Fortran::runtime {
class Terminator;
+std::size_t TrimTrailingSpaces(const char *, std::size_t);
+
OwningPtr<char> SaveDefaultCharacter(
const char *, std::size_t, const Terminator &);
diff --git a/flang/runtime/unit-map.cpp b/flang/runtime/unit-map.cpp
index 905beb4d084f..1cd2115f4aa1 100644
--- a/flang/runtime/unit-map.cpp
+++ b/flang/runtime/unit-map.cpp
@@ -72,6 +72,20 @@ void UnitMap::FlushAll(IoErrorHandler &handler) {
}
}
+ExternalFileUnit *UnitMap::Find(const char *path) {
+ if (path) {
+ // TODO: Faster data structure
+ for (int j{0}; j < buckets_; ++j) {
+ for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) {
+ if (p->unit.path() && std::strcmp(p->unit.path(), path) == 0) {
+ return &p->unit;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
ExternalFileUnit &UnitMap::Create(int n, const Terminator &terminator) {
Chain &chain{*New<Chain>{terminator}(n).release()};
chain.next.reset(&chain);
diff --git a/flang/runtime/unit-map.h b/flang/runtime/unit-map.h
index be244f5ae463..961962a2d635 100644
--- a/flang/runtime/unit-map.h
+++ b/flang/runtime/unit-map.h
@@ -34,6 +34,12 @@ class UnitMap {
return p ? *p : Create(n, terminator);
}
+ // Unit look-up by name is needed for INQUIRE(FILE="...")
+ ExternalFileUnit *LookUp(const char *path) {
+ CriticalSection critical{lock_};
+ return Find(path);
+ }
+
ExternalFileUnit &NewUnit(const Terminator &terminator) {
CriticalSection critical{lock_};
return Create(nextNewUnit_--, terminator);
@@ -72,6 +78,7 @@ class UnitMap {
}
return nullptr;
}
+ ExternalFileUnit *Find(const char *path);
ExternalFileUnit &Create(int, const Terminator &);
diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp
index a4c69df8d6a9..be36666f66e4 100644
--- a/flang/runtime/unit.cpp
+++ b/flang/runtime/unit.cpp
@@ -59,20 +59,19 @@ ExternalFileUnit &ExternalFileUnit::LookUpOrCreateAnonymous(
ExternalFileUnit &result{
GetUnitMap().LookUpOrCreate(unit, terminator, exists)};
if (!exists) {
- // I/O to an unconnected unit reads/creates a local file, e.g. fort.7
- std::size_t pathMaxLen{32};
- auto path{SizedNew<char>{terminator}(pathMaxLen)};
- std::snprintf(path.get(), pathMaxLen, "fort.%d", unit);
IoErrorHandler handler{terminator};
- result.OpenUnit(
- dir == Direction::Input ? OpenStatus::Old : OpenStatus::Replace,
- Action::ReadWrite, Position::Rewind, std::move(path),
- std::strlen(path.get()), Convert::Native, handler);
+ result.OpenAnonymousUnit(
+ dir == Direction::Input ? OpenStatus::Unknown : OpenStatus::Replace,
+ Action::ReadWrite, Position::Rewind, Convert::Native, handler);
result.isUnformatted = isUnformatted;
}
return result;
}
+ExternalFileUnit *ExternalFileUnit::LookUp(const char *path) {
+ return GetUnitMap().LookUp(path);
+}
+
ExternalFileUnit &ExternalFileUnit::CreateNew(
int unit, const Terminator &terminator) {
bool wasExtant{false};
@@ -125,10 +124,7 @@ void ExternalFileUnit::OpenUnit(OpenStatus status, std::optional<Action> action,
handler.SignalError(IostatOpenBadRecl,
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is invalid",
unitNumber(), static_cast<std::intmax_t>(*recordLength));
- } else if (!totalBytes) {
- handler.SignalError(IostatOpenUnknownSize,
- "OPEN(UNIT=%d,ACCESS='DIRECT'): file size is not known");
- } else if (*totalBytes % *recordLength != 0) {
+ } else if (totalBytes && (*totalBytes % *recordLength != 0)) {
handler.SignalError(IostatOpenBadAppend,
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is not an "
"even divisor of the file size %jd",
@@ -137,7 +133,7 @@ void ExternalFileUnit::OpenUnit(OpenStatus status, std::optional<Action> action,
}
}
if (position == Position::Append) {
- if (*totalBytes && recordLength && *recordLength) {
+ if (totalBytes && recordLength && *recordLength) {
endfileRecordNumber = 1 + (*totalBytes / *recordLength);
} else {
// Fake it so that we can backspace relative from the end
@@ -149,6 +145,17 @@ void ExternalFileUnit::OpenUnit(OpenStatus status, std::optional<Action> action,
}
}
+void ExternalFileUnit::OpenAnonymousUnit(OpenStatus status,
+ std::optional<Action> action, Position position, Convert convert,
+ IoErrorHandler &handler) {
+ // I/O to an unconnected unit reads/creates a local file, e.g. fort.7
+ std::size_t pathMaxLen{32};
+ auto path{SizedNew<char>{handler}(pathMaxLen)};
+ std::snprintf(path.get(), pathMaxLen, "fort.%d", unitNumber_);
+ OpenUnit(status, action, position, std::move(path), std::strlen(path.get()),
+ convert, handler);
+}
+
void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) {
DoImpliedEndfile(handler);
Flush(handler);
diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h
index f94e4229cd4c..9d66d962bc56 100644
--- a/flang/runtime/unit.h
+++ b/flang/runtime/unit.h
@@ -35,6 +35,7 @@ class ExternalFileUnit : public ConnectionState,
public:
explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {}
int unitNumber() const { return unitNumber_; }
+ bool swapEndianness() const { return swapEndianness_; }
static ExternalFileUnit *LookUp(int unit);
static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &);
@@ -42,6 +43,7 @@ class ExternalFileUnit : public ConnectionState,
int unit, const Terminator &, bool &wasExtant);
static ExternalFileUnit &LookUpOrCreateAnonymous(
int unit, Direction, bool isUnformatted, const Terminator &);
+ static ExternalFileUnit *LookUp(const char *path);
static ExternalFileUnit &CreateNew(int unit, const Terminator &);
static ExternalFileUnit *LookUpForClose(int unit);
static int NewUnit(const Terminator &);
@@ -51,13 +53,15 @@ class ExternalFileUnit : public ConnectionState,
void OpenUnit(OpenStatus, std::optional<Action>, Position,
OwningPtr<char> &&path, std::size_t pathLength, Convert,
IoErrorHandler &);
+ void OpenAnonymousUnit(
+ OpenStatus, std::optional<Action>, Position, Convert, IoErrorHandler &);
void CloseUnit(CloseStatus, IoErrorHandler &);
void DestroyClosed();
bool SetDirection(Direction, IoErrorHandler &);
template <typename A, typename... X>
- IoStatementState &BeginIoStatement(X &&... xs) {
+ IoStatementState &BeginIoStatement(X &&...xs) {
// TODO: Child data transfer statements vs. locking
lock_.Take(); // dropped in EndIoStatement()
A &state{u_.emplace<A>(std::forward<X>(xs)...)};
@@ -111,7 +115,7 @@ class ExternalFileUnit : public ConnectionState,
ExternalListIoStatementState<Direction::Output>,
ExternalListIoStatementState<Direction::Input>,
UnformattedIoStatementState<Direction::Output>,
- UnformattedIoStatementState<Direction::Input>,
+ UnformattedIoStatementState<Direction::Input>, InquireUnitState,
ExternalMiscIoStatementState>
u_;
More information about the flang-commits
mailing list