diff --git a/flang/include/flang/Runtime/io-api.h b/flang/include/flang/Runtime/io-api.h --- a/flang/include/flang/Runtime/io-api.h +++ b/flang/include/flang/Runtime/io-api.h @@ -143,14 +143,9 @@ Cookie IONAME(BeginUnformattedInput)(ExternalUnit = DefaultUnit, const char *sourceFile = nullptr, int sourceLine = 0); -// Asynchronous I/O is supported (at most) for unformatted direct access -// block transfers. -AsynchronousId IONAME(BeginAsynchronousOutput)(ExternalUnit, std::int64_t REC, - const char *, std::size_t, const char *sourceFile = nullptr, - int sourceLine = 0); -AsynchronousId IONAME(BeginAsynchronousInput)(ExternalUnit, std::int64_t REC, - char *, std::size_t, const char *sourceFile = nullptr, int sourceLine = 0); +// WAIT(ID=) Cookie IONAME(BeginWait)(ExternalUnit, AsynchronousId); +// WAIT(no ID=) Cookie IONAME(BeginWaitAll)(ExternalUnit); // Other I/O statements @@ -199,6 +194,9 @@ void IONAME(EnableHandlers)(Cookie, bool hasIoStat = false, bool hasErr = false, bool hasEnd = false, bool hasEor = false, bool hasIoMsg = false); +// ASYNCHRONOUS='YES' or 'NO' with no ID= on READ/WRITE/OPEN +bool IONAME(SetAsynchronous)(Cookie, const char *, std::size_t); + // Control list options. These return false on a error that the // Begin...() call has specified will be handled by the caller. // The interfaces that pass a default-kind CHARACTER argument @@ -270,14 +268,12 @@ // Additional specifier interfaces for the connection-list of // on OPEN statement (only). SetBlank(), SetDecimal(), -// SetDelim(), GetIoMsg(), SetPad(), SetRound(), & SetSign() -// are also acceptable for OPEN. +// SetDelim(), GetIoMsg(), SetPad(), SetRound(), SetSign(), +// & SetAsynchronous() are also acceptable for OPEN. // ACCESS=SEQUENTIAL, DIRECT, STREAM bool IONAME(SetAccess)(Cookie, const char *, std::size_t); // ACTION=READ, WRITE, or READWRITE bool IONAME(SetAction)(Cookie, const char *, std::size_t); -// ASYNCHRONOUS=YES, NO -bool IONAME(SetAsynchronous)(Cookie, const char *, std::size_t); // CARRIAGECONTROL=LIST, FORTRAN, NONE bool IONAME(SetCarriagecontrol)(Cookie, const char *, std::size_t); // CONVERT=NATIVE, LITTLE_ENDIAN, BIG_ENDIAN, or SWAP @@ -310,6 +306,9 @@ // end-of-record/file condition is present. void IONAME(GetIoMsg)(Cookie, char *, std::size_t); // IOMSG= +// TODO: for ID= on READ/WRITE(ASYNCHRONOUS='YES') +// int IONAME(GetAsynchronousId)(Cookie); + // INQUIRE() specifiers are mostly identified by their NUL-terminated // case-insensitive names. // ACCESS, ACTION, ASYNCHRONOUS, BLANK, CONVERT, DECIMAL, DELIM, DIRECT, diff --git a/flang/include/flang/Runtime/iostat.h b/flang/include/flang/Runtime/iostat.h --- a/flang/include/flang/Runtime/iostat.h +++ b/flang/include/flang/Runtime/iostat.h @@ -70,6 +70,8 @@ IostatUnitOverflow, IostatBadRealInput, IostatBadScaleFactor, + IostatBadAsynchronous, + IostatBadWaitUnit, }; const char *IostatErrorString(int); diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp --- a/flang/lib/Lower/IO.cpp +++ b/flang/lib/Lower/IO.cpp @@ -75,8 +75,7 @@ mkIOKey(BeginInternalFormattedInput), mkIOKey(BeginExternalListOutput), mkIOKey(BeginExternalListInput), mkIOKey(BeginExternalFormattedOutput), mkIOKey(BeginExternalFormattedInput), mkIOKey(BeginUnformattedOutput), - mkIOKey(BeginUnformattedInput), mkIOKey(BeginAsynchronousOutput), - mkIOKey(BeginAsynchronousInput), mkIOKey(BeginWait), mkIOKey(BeginWaitAll), + mkIOKey(BeginUnformattedInput), mkIOKey(BeginWait), mkIOKey(BeginWaitAll), mkIOKey(BeginClose), mkIOKey(BeginFlush), mkIOKey(BeginBackspace), mkIOKey(BeginEndfile), mkIOKey(BeginRewind), mkIOKey(BeginOpenUnit), mkIOKey(BeginOpenNewUnit), mkIOKey(BeginInquireUnit), @@ -1784,8 +1783,6 @@ bool isFormatted, bool isListOrNml, bool isInternal, bool isInternalWithDesc, bool isAsync) { if constexpr (isInput) { - if (isAsync) - return getIORuntimeFunc(loc, builder); if (isFormatted || isListOrNml) { if (isInternal) { if (isInternalWithDesc) { @@ -1808,8 +1805,6 @@ } return getIORuntimeFunc(loc, builder); } else { - if (isAsync) - return getIORuntimeFunc(loc, builder); if (isFormatted || isListOrNml) { if (isInternal) { if (isInternalWithDesc) { @@ -1873,12 +1868,9 @@ getDefaultScratch(builder, loc, ioFuncTy.getInput(ioArgs.size()))); ioArgs.push_back( // buffer length getDefaultScratchLen(builder, loc, ioFuncTy.getInput(ioArgs.size()))); - } else if (isAsync) { // unit; REC; buffer and length - ioArgs.push_back(getIOUnit(converter, loc, stmt, - ioFuncTy.getInput(ioArgs.size()), csi, - stmtCtx)); - TODO(loc, "asynchronous"); } else { // external IO - maybe explicit format; unit + if (isAsync) + TODO(loc, "asynchronous"); maybeGetFormatArgs(); ioArgs.push_back(getIOUnit(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()), csi, diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp --- a/flang/runtime/io-api.cpp +++ b/flang/runtime/io-api.cpp @@ -337,6 +337,21 @@ unit, false /*was an existing file*/, sourceFile, sourceLine); } +Cookie IONAME(BeginWait)(ExternalUnit unitNumber, AsynchronousId id) { + // TODO: add and use sourceFile & sourceLine here + Terminator oom; + // TODO: add and use sourceFile & sourceLine here + auto &io{ + New{oom}(nullptr, 0).release()->ioStatementState()}; + if (id != 0 && !ExternalFileUnit::LookUp(unitNumber)) { + io.GetIoErrorHandler().SetPendingError(IostatBadWaitUnit); + } + return &io; +} +Cookie IONAME(BeginWaitAll)(ExternalUnit unitNumber) { + return IONAME(BeginWait)(unitNumber, 0 /*no ID=*/); +} + Cookie IONAME(BeginClose)( ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) { @@ -475,15 +490,14 @@ bool IONAME(SetAdvance)( Cookie cookie, const char *keyword, std::size_t length) { IoStatementState &io{*cookie}; - bool nonAdvancing{ - !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler())}; + IoErrorHandler &handler{io.GetIoErrorHandler()}; + bool nonAdvancing{!YesOrNo(keyword, length, "ADVANCE", handler)}; if (nonAdvancing && io.GetConnectionState().access == Access::Direct) { - io.GetIoErrorHandler().SignalError( - "Non-advancing I/O attempted on direct access file"); + handler.SignalError("Non-advancing I/O attempted on direct access file"); } else { io.mutableModes().nonAdvancing = nonAdvancing; } - return true; + return !handler.InError(); } bool IONAME(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) { @@ -543,9 +557,9 @@ bool IONAME(SetPad)(Cookie cookie, const char *keyword, std::size_t length) { IoStatementState &io{*cookie}; - io.mutableModes().pad = - YesOrNo(keyword, length, "PAD", io.GetIoErrorHandler()); - return true; + IoErrorHandler &handler{io.GetIoErrorHandler()}; + io.mutableModes().pad = YesOrNo(keyword, length, "PAD", handler); + return !handler.InError(); } bool IONAME(SetPos)(Cookie cookie, std::int64_t pos) { @@ -713,27 +727,23 @@ bool IONAME(SetAsynchronous)( Cookie cookie, const char *keyword, std::size_t length) { IoStatementState &io{*cookie}; - auto *open{io.get_if()}; - if (!open) { - io.GetIoErrorHandler().Crash( - "SetAsynchronous() called when not in an OPEN statement"); - } else if (open->completedOperation()) { - io.GetIoErrorHandler().Crash( - "SetAsynchronous() called after GetNewUnit() for an OPEN statement"); - } - static const char *keywords[]{"YES", "NO", nullptr}; - switch (IdentifyValue(keyword, length, keywords)) { - case 0: - open->unit().set_mayAsynchronous(true); - return true; - case 1: - open->unit().set_mayAsynchronous(false); - return true; - default: - open->SignalError(IostatErrorInKeyword, "Invalid ASYNCHRONOUS='%.*s'", - static_cast(length), keyword); - return false; + IoErrorHandler &handler{io.GetIoErrorHandler()}; + bool isYes{YesOrNo(keyword, length, "ASYNCHRONOUS", handler)}; + if (auto *open{io.get_if()}) { + if (open->completedOperation()) { + handler.Crash( + "SetAsynchronous() called after GetNewUnit() for an OPEN statement"); + } + open->unit().set_mayAsynchronous(isYes); + } else if (ExternalFileUnit * unit{io.GetExternalFileUnit()}) { + if (isYes && !unit->mayAsynchronous()) { + handler.SignalError(IostatBadAsynchronous); + } + } else { + handler.Crash("SetAsynchronous() called when not in an OPEN or external " + "I/O statement"); } + return !handler.InError(); } bool IONAME(SetCarriagecontrol)( diff --git a/flang/runtime/io-error.h b/flang/runtime/io-error.h --- a/flang/runtime/io-error.h +++ b/flang/runtime/io-error.h @@ -32,9 +32,6 @@ void HasEndLabel() { flags_ |= hasEnd; } void HasEorLabel() { flags_ |= hasEor; } void HasIoMsg() { flags_ |= hasIoMsg; } - void HandleAnything() { - flags_ = hasIoStat | hasErr | hasEnd | hasEor | hasIoMsg; - } bool InError() const { return ioStat_ != IostatOk || pendingError_ != IostatOk; diff --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h --- a/flang/runtime/io-stmt.h +++ b/flang/runtime/io-stmt.h @@ -582,7 +582,7 @@ CloseStatus status_{CloseStatus::Keep}; }; -// For CLOSE(bad unit) and INQUIRE(unconnected unit) +// For CLOSE(bad unit), WAIT(bad unit, ID=nonzero) and INQUIRE(unconnected unit) class NoUnitIoStatementState : public IoStatementBase { public: IoStatementState &ioStatementState() { return ioStatementState_; } diff --git a/flang/runtime/iostat.cpp b/flang/runtime/iostat.cpp --- a/flang/runtime/iostat.cpp +++ b/flang/runtime/iostat.cpp @@ -83,6 +83,11 @@ return "Bad REAL input value"; case IostatBadScaleFactor: return "Bad REAL output scale factor (kP)"; + case IostatBadAsynchronous: + return "READ/WRITE(ASYNCHRONOUS='YES') on unit without " + "OPEN(ASYNCHRONOUS='YES')"; + case IostatBadWaitUnit: + return "WAIT(ID=nonzero) for a bad unit number"; default: return nullptr; } diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h --- a/flang/runtime/unit.h +++ b/flang/runtime/unit.h @@ -124,7 +124,8 @@ Lock lock_; - // When an I/O statement is in progress on this unit, holds its state. + // When a synchronous I/O statement is in progress on this unit, holds its + // state. std::variant, ExternalFormattedIoStatementState,