[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
This commit is contained in:
Peter Klausler 2021-11-24 16:05:37 -08:00
parent 0e85232fa3
commit 80cdf0db67
6 changed files with 109 additions and 57 deletions

View File

@ -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<bool> isUnformatted; // FORM='UNFORMATTED' if true

View File

@ -149,6 +149,7 @@ void OpenFile::Open(OpenStatus status, std::optional<Action> 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();

View File

@ -39,7 +39,7 @@ public:
bool isTerminal() const { return isTerminal_; }
std::optional<FileOffset> knownSize() const { return knownSize_; }
bool IsOpen() const { return fd_ >= 0; }
bool IsConnected() const { return fd_ >= 0; }
void Open(OpenStatus, std::optional<Action>, 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<char> path_;
@ -86,6 +93,7 @@ private:
bool mayWrite_{false};
bool mayPosition_{false};
bool mayAsynchronous_{false};
std::optional<Position> openPosition_; // from Open(); reset after positioning
FileOffset position_{0};
std::optional<FileOffset> knownSize_;
bool isTerminal_{false};

View File

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

View File

@ -107,7 +107,7 @@ void ExternalFileUnit::OpenUnit(std::optional<OpenStatus> 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) {

View File

@ -35,6 +35,8 @@ class ExternalFileUnit : public ConnectionState,
public FileFrame<ExternalFileUnit> {
public:
explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {}
~ExternalFileUnit() {}
int unitNumber() const { return unitNumber_; }
bool swapEndianness() const { return swapEndianness_; }
bool createdForInternalChildIo() const { return createdForInternalChildIo_; }