From 80cdf0db67e2c0f231f4fefc2b873690b44f84cc Mon Sep 17 00:00:00 2001 From: Peter Klausler Date: Wed, 24 Nov 2021 16:05:37 -0800 Subject: [PATCH] [flang] Correct INQUIRE(POSITION= & PAD=) INQUIRE(POSITION=)'s results need to reflect the POSITION= specifier used for the OPEN statement until the unit has been repositioned. Preserve the POSITION= from OPEN and used it for INQUIRE(POSITION=) until is becomes obsolete. INQUIRE(PAD=) is implemented here in the case of an unconnected unit with Fortran 2018 semantics; i.e., "UNDEFINED", rather than Fortran 90's "YES"/"NO" (see 4.3.6 para 2). Apparent failures with F'90-only tests will persist with INQUIRE(PAD=); these discrepancies don't seem to warrant an option or environment variable. To make the implementation of INQUIRE more closely match the language in the standard, rename IsOpen() to IsConnected(), and use it explicitly for the various INQUIRE specifiers. Differential Revision: https://reviews.llvm.org/D114755 --- flang/runtime/connection.h | 3 +- flang/runtime/file.cpp | 21 ++++++-- flang/runtime/file.h | 10 +++- flang/runtime/io-stmt.cpp | 128 +++++++++++++++++++++++++++------------------ flang/runtime/unit.cpp | 2 +- flang/runtime/unit.h | 2 + 6 files changed, 109 insertions(+), 57 deletions(-) diff --git a/flang/runtime/connection.h b/flang/runtime/connection.h index 0eb2038..6e7e0a6 100644 --- a/flang/runtime/connection.h +++ b/flang/runtime/connection.h @@ -25,7 +25,8 @@ enum class Access { Sequential, Direct, Stream }; inline bool IsRecordFile(Access a) { return a != Access::Stream; } // These characteristics of a connection are immutable after being -// established in an OPEN statement. +// established in an OPEN statement, except for recordLength, +// which is immutable only when isFixedRecordLength is true. struct ConnectionAttributes { Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM' std::optional isUnformatted; // FORM='UNFORMATTED' if true diff --git a/flang/runtime/file.cpp b/flang/runtime/file.cpp index b8e349b..aaa2e30 100644 --- a/flang/runtime/file.cpp +++ b/flang/runtime/file.cpp @@ -149,6 +149,7 @@ void OpenFile::Open(OpenStatus status, std::optional action, knownSize_ = 0; mayPosition_ = true; } + openPosition_ = position; // for INQUIRE(POSITION=) } void OpenFile::Predefine(int fd) { @@ -208,7 +209,7 @@ std::size_t OpenFile::Read(FileOffset at, char *buffer, std::size_t minBytes, break; } } else { - position_ += chunk; + SetPosition(position_ + chunk); got += chunk; } } @@ -228,7 +229,7 @@ std::size_t OpenFile::Write(FileOffset at, const char *buffer, while (put < bytes) { auto chunk{::write(fd_, buffer + put, bytes - put)}; if (chunk >= 0) { - position_ += chunk; + SetPosition(position_ + chunk); put += chunk; } else { auto err{errno}; @@ -351,6 +352,20 @@ void OpenFile::WaitAll(IoErrorHandler &handler) { } } +Position OpenFile::InquirePosition() const { + if (openPosition_) { // from OPEN statement + return *openPosition_; + } else { // unit has been repositioned since opening + if (position_ == knownSize_.value_or(position_ + 1)) { + return Position::Append; + } else if (position_ == 0 && mayPosition_) { + return Position::Rewind; + } else { + return Position::AsIs; // processor-dependent & no common behavior + } + } +} + void OpenFile::CheckOpen(const Terminator &terminator) { RUNTIME_CHECK(terminator, fd_ >= 0); } @@ -359,7 +374,7 @@ bool OpenFile::Seek(FileOffset at, IoErrorHandler &handler) { if (at == position_) { return true; } else if (RawSeek(at)) { - position_ = at; + SetPosition(at); return true; } else { handler.SignalErrno(); diff --git a/flang/runtime/file.h b/flang/runtime/file.h index 3770b19..cde3c19 100644 --- a/flang/runtime/file.h +++ b/flang/runtime/file.h @@ -39,7 +39,7 @@ public: bool isTerminal() const { return isTerminal_; } std::optional knownSize() const { return knownSize_; } - bool IsOpen() const { return fd_ >= 0; } + bool IsConnected() const { return fd_ >= 0; } void Open(OpenStatus, std::optional, Position, IoErrorHandler &); void Predefine(int fd); void Close(CloseStatus, IoErrorHandler &); @@ -66,6 +66,9 @@ public: void Wait(int id, IoErrorHandler &); void WaitAll(IoErrorHandler &); + // INQUIRE(POSITION=) + Position InquirePosition() const; + private: struct Pending { int id; @@ -78,6 +81,10 @@ private: bool RawSeek(FileOffset); bool RawSeekToEnd(); int PendingResult(const Terminator &, int); + void SetPosition(FileOffset pos) { + position_ = pos; + openPosition_.reset(); + } int fd_{-1}; OwningPtr path_; @@ -86,6 +93,7 @@ private: bool mayWrite_{false}; bool mayPosition_{false}; bool mayAsynchronous_{false}; + std::optional openPosition_; // from Open(); reset after positioning FileOffset position_{0}; std::optional knownSize_; bool isTerminal_{false}; diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp index 10560bf..d003390 100644 --- a/flang/runtime/io-stmt.cpp +++ b/flang/runtime/io-stmt.cpp @@ -824,26 +824,35 @@ bool InquireUnitState::Inquire( 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; + if (!unit().IsConnected()) { + str = "UNDEFINED"; + } else { + 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"; + str = !unit().IsConnected() ? "UNDEFINED" + : unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE" + : "READ"; break; case HashInquiryKeyword("ASYNCHRONOUS"): - str = unit().mayAsynchronous() ? "YES" : "NO"; + str = !unit().IsConnected() ? "UNDEFINED" + : unit().mayAsynchronous() ? "YES" + : "NO"; break; case HashInquiryKeyword("BLANK"): - str = unit().isUnformatted.value_or(true) ? "UNDEFINED" + str = !unit().IsConnected() || unit().isUnformatted.value_or(true) + ? "UNDEFINED" : unit().modes.editingFlags & blankZero ? "ZERO" : "NULL"; break; @@ -854,12 +863,13 @@ bool InquireUnitState::Inquire( str = unit().swapEndianness() ? "SWAP" : "NATIVE"; break; case HashInquiryKeyword("DECIMAL"): - str = unit().isUnformatted.value_or(true) ? "UNDEFINED" + str = !unit().IsConnected() || unit().isUnformatted.value_or(true) + ? "UNDEFINED" : unit().modes.editingFlags & decimalComma ? "COMMA" : "POINT"; break; case HashInquiryKeyword("DELIM"): - if (unit().isUnformatted.value_or(true)) { + if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) { str = "UNDEFINED"; } else { switch (unit().modes.delim) { @@ -876,23 +886,26 @@ bool InquireUnitState::Inquire( } break; case HashInquiryKeyword("DIRECT"): - str = unit().access == Access::Direct || + str = !unit().IsConnected() ? "UNKNOWN" + : unit().access == Access::Direct || (unit().mayPosition() && unit().isFixedRecordLength) ? "YES" : "NO"; break; case HashInquiryKeyword("ENCODING"): - str = unit().isUnformatted.value_or(true) ? "UNDEFINED" + str = !unit().IsConnected() ? "UNKNOWN" + : unit().isUnformatted.value_or(true) ? "UNDEFINED" : unit().isUTF8 ? "UTF-8" : "ASCII"; break; case HashInquiryKeyword("FORM"): - str = !unit().isUnformatted ? "UNDEFINED" - : *unit().isUnformatted ? "UNFORMATTED" - : "FORMATTED"; + str = !unit().IsConnected() || !unit().isUnformatted ? "UNDEFINED" + : *unit().isUnformatted ? "UNFORMATTED" + : "FORMATTED"; break; case HashInquiryKeyword("FORMATTED"): - str = !unit().isUnformatted ? "UNKNOWN" + str = !unit().IsConnected() ? "UNDEFINED" + : !unit().isUnformatted ? "UNKNOWN" : *unit().isUnformatted ? "NO" : "YES"; break; @@ -903,33 +916,38 @@ bool InquireUnitState::Inquire( } break; case HashInquiryKeyword("PAD"): - str = unit().isUnformatted.value_or(true) ? "UNDEFINED" - : unit().modes.pad ? "YES" - : "NO"; + str = !unit().IsConnected() || unit().isUnformatted.value_or(true) + ? "UNDEFINED" + : unit().modes.pad ? "YES" + : "NO"; break; case HashInquiryKeyword("POSITION"): - if (unit().access == Access::Direct) { + if (!unit().IsConnected() || 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 && unit().mayPosition()) { + switch (unit().InquirePosition()) { + case Position::Rewind: str = "REWIND"; - } else { - str = "ASIS"; // processor-dependent & no common behavior + break; + case Position::Append: + str = "APPEND"; + break; + case Position::AsIs: + str = "ASIS"; + break; } } break; case HashInquiryKeyword("READ"): - str = unit().mayRead() ? "YES" : "NO"; + str = !unit().IsConnected() ? "UNDEFINED" : unit().mayRead() ? "YES" : "NO"; break; case HashInquiryKeyword("READWRITE"): - str = unit().mayRead() && unit().mayWrite() ? "YES" : "NO"; + str = !unit().IsConnected() ? "UNDEFINED" + : unit().mayRead() && unit().mayWrite() ? "YES" + : "NO"; break; case HashInquiryKeyword("ROUND"): - if (unit().isUnformatted.value_or(true)) { + if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) { str = "UNDEFINED"; } else { switch (unit().modes.round) { @@ -954,23 +972,28 @@ bool InquireUnitState::Inquire( case HashInquiryKeyword("SEQUENTIAL"): // "NO" for Direct, since Sequential would not work if // the unit were reopened without RECL=. - str = unit().access == Access::Sequential ? "YES" : "NO"; + str = !unit().IsConnected() ? "UNKNOWN" + : unit().access == Access::Sequential ? "YES" + : "NO"; break; case HashInquiryKeyword("SIGN"): - str = unit().isUnformatted.value_or(true) ? "UNDEFINED" + str = !unit().IsConnected() || unit().isUnformatted.value_or(true) + ? "UNDEFINED" : unit().modes.editingFlags & signPlus ? "PLUS" : "SUPPRESS"; break; case HashInquiryKeyword("STREAM"): - str = unit().access == Access::Stream ? "YES" : "NO"; - break; - case HashInquiryKeyword("WRITE"): - str = unit().mayWrite() ? "YES" : "NO"; + str = !unit().IsConnected() ? "UNKNOWN" + : unit().access == Access::Stream ? "YES" + : "NO"; break; case HashInquiryKeyword("UNFORMATTED"): - str = !unit().isUnformatted ? "UNKNOWN" - : *unit().isUnformatted ? "YES" - : "NO"; + str = !unit().IsConnected() || !unit().isUnformatted ? "UNKNOWN" + : *unit().isUnformatted ? "YES" + : "NO"; + break; + case HashInquiryKeyword("WRITE"): + str = !unit().IsConnected() ? "UNKNOWN" : unit().mayWrite() ? "YES" : "NO"; break; } if (str) { @@ -991,7 +1014,7 @@ bool InquireUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) { result = unit().path() != nullptr; return true; case HashInquiryKeyword("OPENED"): - result = true; + result = unit().IsConnected(); return true; case HashInquiryKeyword("PENDING"): result = false; // asynchronous I/O is not implemented @@ -1023,13 +1046,15 @@ bool InquireUnitState::Inquire( } return true; case HashInquiryKeyword("NUMBER"): - result = unit().unitNumber(); + result = unit().IsConnected() ? unit().unitNumber() : -1; return true; case HashInquiryKeyword("POS"): result = unit().position(); return true; case HashInquiryKeyword("RECL"): - if (unit().access == Access::Stream) { + if (!unit().IsConnected()) { + result = -1; + } else if (unit().access == Access::Stream) { result = -2; } else if (unit().isFixedRecordLength && unit().recordLength) { result = *unit().recordLength; @@ -1038,10 +1063,11 @@ bool InquireUnitState::Inquire( } return true; case HashInquiryKeyword("SIZE"): - if (auto size{unit().knownSize()}) { - result = *size; - } else { - result = -1; + result = -1; + if (unit().IsConnected()) { + if (auto size{unit().knownSize()}) { + result = *size; + } } return true; default: diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp index e24914b..f1d2b2c 100644 --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -107,7 +107,7 @@ void ExternalFileUnit::OpenUnit(std::optional status, swapEndianness_ = convert == Convert::Swap || (convert == Convert::LittleEndian && !isHostLittleEndian) || (convert == Convert::BigEndian && isHostLittleEndian); - if (IsOpen()) { + if (IsConnected()) { bool isSamePath{newPath.get() && path() && pathLength() == newPathLength && std::memcmp(path(), newPath.get(), newPathLength) == 0}; if (status && *status != OpenStatus::Old && isSamePath) { diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h index 4f7a18e..a63921c9 100644 --- a/flang/runtime/unit.h +++ b/flang/runtime/unit.h @@ -35,6 +35,8 @@ class ExternalFileUnit : public ConnectionState, public FileFrame { public: explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {} + ~ExternalFileUnit() {} + int unitNumber() const { return unitNumber_; } bool swapEndianness() const { return swapEndianness_; } bool createdForInternalChildIo() const { return createdForInternalChildIo_; } -- 2.7.4