[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