//===-- runtime/io-stmt.h ---------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Representations of the state of an I/O statement in progress #ifndef FORTRAN_RUNTIME_IO_STMT_H_ #define FORTRAN_RUNTIME_IO_STMT_H_ #include "connection.h" #include "file.h" #include "format.h" #include "internal-unit.h" #include "io-error.h" #include "flang/Common/visit.h" #include "flang/Runtime/descriptor.h" #include "flang/Runtime/io-api.h" #include #include #include namespace Fortran::runtime::io { class ExternalFileUnit; class ChildIo; class OpenStatementState; class InquireUnitState; class InquireNoUnitState; class InquireUnconnectedFileState; class InquireIOLengthState; class ExternalMiscIoStatementState; class CloseStatementState; class NoopStatementState; // CLOSE or FLUSH on unknown unit class ErroneousIoStatementState; template class InternalFormattedIoStatementState; template class InternalListIoStatementState; template class ExternalFormattedIoStatementState; template class ExternalListIoStatementState; template class ExternalUnformattedIoStatementState; template class ChildFormattedIoStatementState; template class ChildListIoStatementState; template class ChildUnformattedIoStatementState; struct InputStatementState {}; struct OutputStatementState {}; template using IoDirectionState = std::conditional_t; // Common state for all kinds of formatted I/O template class FormattedIoStatementState {}; template <> class FormattedIoStatementState { public: std::size_t GetEditDescriptorChars() const; void GotChar(int); private: // Account of characters read for edit descriptors (i.e., formatted I/O // with a FORMAT, not list-directed or NAMELIST), not including padding. std::size_t chars_{0}; // for READ(SIZE=) }; // The Cookie type in the I/O API is a pointer (for C) to this class. class IoStatementState { public: template explicit IoStatementState(A &x) : u_{x} {} // These member functions each project themselves into the active alternative. // They're used by per-data-item routines in the I/O API (e.g., OutputReal64) // to interact with the state of the I/O statement in progress. // This design avoids virtual member functions and function pointers, // which may not have good support in some runtime environments. // CompleteOperation() is the last opportunity to raise an I/O error. // It is called by EndIoStatement(), but it can be invoked earlier to // catch errors for (e.g.) GetIoMsg() and GetNewUnit(). If called // more than once, it is a no-op. void CompleteOperation(); // Completes an I/O statement and reclaims storage. int EndIoStatement(); bool Emit(const char *, std::size_t bytes, std::size_t elementBytes = 0); bool Receive(char *, std::size_t, std::size_t elementBytes = 0); std::size_t GetNextInputBytes(const char *&); bool AdvanceRecord(int = 1); void BackspaceRecord(); void HandleRelativePosition(std::int64_t); void HandleAbsolutePosition(std::int64_t); // for r* in list I/O std::optional GetNextDataEdit(int maxRepeat = 1); ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit bool BeginReadingRecord(); void FinishReadingRecord(); 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 &); std::int64_t InquirePos(); void GotChar(signed int = 1); // for READ(SIZE=); can be <0 MutableModes &mutableModes(); ConnectionState &GetConnectionState(); IoErrorHandler &GetIoErrorHandler() const; // N.B.: this also works with base classes template A *get_if() const { return common::visit( [](auto &x) -> A * { if constexpr (std::is_convertible_v) { return &x.get(); } return nullptr; }, u_); } // Vacant after the end of the current record std::optional GetCurrentChar(std::size_t &byteCount); // For fixed-width fields, return the number of remaining characters. // Skip over leading blanks. std::optional CueUpInput(const DataEdit &edit) { std::optional remaining; if (edit.IsListDirected()) { std::size_t byteCount{0}; GetNextNonBlank(byteCount); } else { if (edit.width.value_or(0) > 0) { remaining = *edit.width; } SkipSpaces(remaining); } return remaining; } std::optional SkipSpaces(std::optional &remaining) { while (!remaining || *remaining > 0) { std::size_t byteCount{0}; if (auto ch{GetCurrentChar(byteCount)}) { if (*ch != ' ' && *ch != '\t') { return ch; } if (remaining) { if (static_cast(*remaining) < byteCount) { break; } GotChar(byteCount); *remaining -= byteCount; } HandleRelativePosition(byteCount); } else { break; } } return std::nullopt; } // Acquires the next input character, respecting any applicable field width // or separator character. std::optional NextInField( std::optional &remaining, const DataEdit &); // Detect and signal any end-of-record condition after input. // Returns true if at EOR and remaining input should be padded with blanks. bool CheckForEndOfRecord(std::size_t afterReading); // Skips spaces, advances records, and ignores NAMELIST comments std::optional GetNextNonBlank(std::size_t &byteCount) { auto ch{GetCurrentChar(byteCount)}; bool inNamelist{mutableModes().inNamelist}; while (!ch || *ch == ' ' || *ch == '\t' || (inNamelist && *ch == '!')) { if (ch && (*ch == ' ' || *ch == '\t')) { HandleRelativePosition(byteCount); } else if (!AdvanceRecord()) { return std::nullopt; } ch = GetCurrentChar(byteCount); } return ch; } template bool CheckFormattedStmtType(const char *name) { if (get_if>()) { return true; } else { auto &handler{GetIoErrorHandler()}; if (!handler.InError()) { handler.Crash("%s called for I/O statement that is not formatted %s", name, D == Direction::Output ? "output" : "input"); } return false; } } private: std::variant, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper< InternalFormattedIoStatementState>, std::reference_wrapper< InternalFormattedIoStatementState>, std::reference_wrapper>, std::reference_wrapper>, std::reference_wrapper< ExternalFormattedIoStatementState>, std::reference_wrapper< ExternalFormattedIoStatementState>, std::reference_wrapper>, std::reference_wrapper>, std::reference_wrapper< ExternalUnformattedIoStatementState>, std::reference_wrapper< ExternalUnformattedIoStatementState>, std::reference_wrapper>, std::reference_wrapper>, std::reference_wrapper>, std::reference_wrapper>, std::reference_wrapper< ChildUnformattedIoStatementState>, std::reference_wrapper< ChildUnformattedIoStatementState>, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper> u_; }; // Base class for all per-I/O statement state classes. class IoStatementBase : public IoErrorHandler { public: using IoErrorHandler::IoErrorHandler; bool completedOperation() const { return completedOperation_; } void CompleteOperation() { completedOperation_ = true; } int EndIoStatement() { return GetIoStat(); } // These are default no-op backstops that can be overridden by descendants. bool Emit(const char *, std::size_t bytes, std::size_t elementBytes = 0); bool Receive(char *, std::size_t bytes, std::size_t elementBytes = 0); std::size_t GetNextInputBytes(const char *&); bool AdvanceRecord(int); void BackspaceRecord(); void HandleRelativePosition(std::int64_t); void HandleAbsolutePosition(std::int64_t); std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1); ExternalFileUnit *GetExternalFileUnit() const; bool BeginReadingRecord(); void FinishReadingRecord(); bool Inquire(InquiryKeywordHash, char *, std::size_t); bool Inquire(InquiryKeywordHash, bool &); bool Inquire(InquiryKeywordHash, std::int64_t, bool &); bool Inquire(InquiryKeywordHash, std::int64_t &); std::int64_t InquirePos(); void BadInquiryKeywordHashCrash(InquiryKeywordHash); protected: bool completedOperation_{false}; }; // Common state for list-directed & NAMELIST I/O, both internal & external template class ListDirectedStatementState; template <> class ListDirectedStatementState : public FormattedIoStatementState { public: bool EmitLeadingSpaceOrAdvance( IoStatementState &, std::size_t = 1, bool isCharacter = false); std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1); bool lastWasUndelimitedCharacter() const { return lastWasUndelimitedCharacter_; } void set_lastWasUndelimitedCharacter(bool yes = true) { lastWasUndelimitedCharacter_ = yes; } private: bool lastWasUndelimitedCharacter_{false}; }; template <> class ListDirectedStatementState : public FormattedIoStatementState { public: bool inNamelistArray() const { return inNamelistArray_; } void set_inNamelistArray(bool yes = true) { inNamelistArray_ = yes; } // Skips value separators, handles repetition and null values. // Vacant when '/' appears; present with descriptor == ListDirectedNullValue // when a null value appears. std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1); // Each NAMELIST input item is treated like a distinct list-directed // input statement. This member function resets some state so that // repetition and null values work correctly for each successive // NAMELIST input item. void ResetForNextNamelistItem(bool inNamelistArray) { remaining_ = 0; eatComma_ = false; realPart_ = imaginaryPart_ = false; inNamelistArray_ = inNamelistArray; } private: int remaining_{0}; // for "r*" repetition std::optional repeatPosition_; bool eatComma_{false}; // consume comma after previously read item bool hitSlash_{false}; // once '/' is seen, nullify further items bool realPart_{false}; bool imaginaryPart_{false}; bool inNamelistArray_{false}; }; template class InternalIoStatementState : public IoStatementBase, public IoDirectionState { public: using Buffer = std::conditional_t; InternalIoStatementState(Buffer, std::size_t, const char *sourceFile = nullptr, int sourceLine = 0); InternalIoStatementState( const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); int EndIoStatement(); bool Emit(const char *data, std::size_t bytes, std::size_t elementBytes = 0); std::size_t GetNextInputBytes(const char *&); bool AdvanceRecord(int = 1); void BackspaceRecord(); ConnectionState &GetConnectionState() { return unit_; } MutableModes &mutableModes() { return unit_.modes; } void HandleRelativePosition(std::int64_t); void HandleAbsolutePosition(std::int64_t); std::int64_t InquirePos(); protected: bool free_{true}; InternalDescriptorUnit unit_; }; template class InternalFormattedIoStatementState : public InternalIoStatementState, public FormattedIoStatementState { public: using CharType = CHAR; using typename InternalIoStatementState::Buffer; InternalFormattedIoStatementState(Buffer internal, std::size_t internalLength, const CharType *format, std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, const char *sourceFile = nullptr, int sourceLine = 0); InternalFormattedIoStatementState(const Descriptor &, const CharType *format, std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, const char *sourceFile = nullptr, int sourceLine = 0); IoStatementState &ioStatementState() { return ioStatementState_; } void CompleteOperation(); int EndIoStatement(); std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1) { return format_.GetNextDataEdit(*this, maxRepeat); } private: IoStatementState ioStatementState_; // points to *this using InternalIoStatementState::unit_; // format_ *must* be last; it may be partial someday FormatControl format_; }; template class InternalListIoStatementState : public InternalIoStatementState, public ListDirectedStatementState { public: using typename InternalIoStatementState::Buffer; InternalListIoStatementState(Buffer internal, std::size_t internalLength, const char *sourceFile = nullptr, int sourceLine = 0); InternalListIoStatementState( const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); IoStatementState &ioStatementState() { return ioStatementState_; } using ListDirectedStatementState::GetNextDataEdit; private: IoStatementState ioStatementState_; // points to *this using InternalIoStatementState::unit_; }; class ExternalIoStatementBase : public IoStatementBase { public: ExternalIoStatementBase( ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); ExternalFileUnit &unit() { return unit_; } MutableModes &mutableModes(); ConnectionState &GetConnectionState(); int asynchronousID() const { return asynchronousID_; } int EndIoStatement(); ExternalFileUnit *GetExternalFileUnit() const { return &unit_; } void SetAsynchronous(); std::int64_t InquirePos(); private: ExternalFileUnit &unit_; int asynchronousID_{-1}; }; template class ExternalIoStatementState : public ExternalIoStatementBase, public IoDirectionState { public: ExternalIoStatementState( ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); MutableModes &mutableModes() { return mutableModes_; } void CompleteOperation(); int EndIoStatement(); bool Emit(const char *, std::size_t bytes, std::size_t elementBytes = 0); std::size_t GetNextInputBytes(const char *&); bool AdvanceRecord(int = 1); void BackspaceRecord(); void HandleRelativePosition(std::int64_t); void HandleAbsolutePosition(std::int64_t); bool BeginReadingRecord(); void FinishReadingRecord(); private: // These are forked from ConnectionState's modes at the beginning // of each formatted I/O statement so they may be overridden by control // edit descriptors during the statement. MutableModes mutableModes_; }; template class ExternalFormattedIoStatementState : public ExternalIoStatementState, public FormattedIoStatementState { public: using CharType = CHAR; ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format, std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, const char *sourceFile = nullptr, int sourceLine = 0); void CompleteOperation(); int EndIoStatement(); std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1) { return format_.GetNextDataEdit(*this, maxRepeat); } private: FormatControl format_; }; template class ExternalListIoStatementState : public ExternalIoStatementState, public ListDirectedStatementState { public: using ExternalIoStatementState::ExternalIoStatementState; using ListDirectedStatementState::GetNextDataEdit; }; template class ExternalUnformattedIoStatementState : public ExternalIoStatementState { public: using ExternalIoStatementState::ExternalIoStatementState; bool Receive(char *, std::size_t, std::size_t elementBytes = 0); }; template class ChildIoStatementState : public IoStatementBase, public IoDirectionState { public: ChildIoStatementState( ChildIo &, const char *sourceFile = nullptr, int sourceLine = 0); ChildIo &child() { return child_; } MutableModes &mutableModes(); ConnectionState &GetConnectionState(); ExternalFileUnit *GetExternalFileUnit() const; int EndIoStatement(); bool Emit(const char *, std::size_t bytes, std::size_t elementBytes = 0); std::size_t GetNextInputBytes(const char *&); void HandleRelativePosition(std::int64_t); void HandleAbsolutePosition(std::int64_t); private: ChildIo &child_; }; template class ChildFormattedIoStatementState : public ChildIoStatementState, public FormattedIoStatementState { public: using CharType = CHAR; ChildFormattedIoStatementState(ChildIo &, const CharType *format, std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, const char *sourceFile = nullptr, int sourceLine = 0); MutableModes &mutableModes() { return mutableModes_; } void CompleteOperation(); int EndIoStatement(); bool AdvanceRecord(int = 1); std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1) { return format_.GetNextDataEdit(*this, maxRepeat); } private: MutableModes mutableModes_; FormatControl format_; }; template class ChildListIoStatementState : public ChildIoStatementState, public ListDirectedStatementState { public: using ChildIoStatementState::ChildIoStatementState; using ListDirectedStatementState::GetNextDataEdit; }; template class ChildUnformattedIoStatementState : public ChildIoStatementState { public: using ChildIoStatementState::ChildIoStatementState; bool Receive(char *, std::size_t, std::size_t elementBytes = 0); }; // OPEN class OpenStatementState : public ExternalIoStatementBase { public: OpenStatementState(ExternalFileUnit &unit, bool wasExtant, bool isNewUnit, const char *sourceFile = nullptr, int sourceLine = 0) : ExternalIoStatementBase{unit, sourceFile, sourceLine}, wasExtant_{wasExtant}, isNewUnit_{isNewUnit} {} bool wasExtant() const { return wasExtant_; } void set_status(OpenStatus status) { status_ = status; } // STATUS= 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= void CompleteOperation(); int EndIoStatement(); private: bool wasExtant_; bool isNewUnit_; std::optional status_; std::optional position_; std::optional action_; Convert convert_{Convert::Unknown}; OwningPtr path_; std::size_t pathLength_; std::optional isUnformatted_; std::optional access_; }; class CloseStatementState : public ExternalIoStatementBase { public: CloseStatementState(ExternalFileUnit &unit, const char *sourceFile = nullptr, int sourceLine = 0) : ExternalIoStatementBase{unit, sourceFile, sourceLine} {} void set_status(CloseStatus status) { status_ = status; } int EndIoStatement(); private: CloseStatus status_{CloseStatus::Keep}; }; // For CLOSE(bad unit), WAIT(bad unit, ID=nonzero), INQUIRE(unconnected unit), // and recoverable BACKSPACE(bad unit) class NoUnitIoStatementState : public IoStatementBase { public: IoStatementState &ioStatementState() { return ioStatementState_; } MutableModes &mutableModes() { return connection_.modes; } ConnectionState &GetConnectionState() { return connection_; } int badUnitNumber() const { return badUnitNumber_; } void CompleteOperation(); int EndIoStatement(); protected: template NoUnitIoStatementState(A &stmt, const char *sourceFile = nullptr, int sourceLine = 0, int badUnitNumber = -1) : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{stmt}, badUnitNumber_{badUnitNumber} {} private: IoStatementState ioStatementState_; // points to *this ConnectionState connection_; int badUnitNumber_; }; class NoopStatementState : public NoUnitIoStatementState { public: NoopStatementState( const char *sourceFile = nullptr, int sourceLine = 0, int unitNumber = -1) : NoUnitIoStatementState{*this, sourceFile, sourceLine, unitNumber} {} void set_status(CloseStatus) {} // discards }; extern template class InternalIoStatementState; extern template class InternalIoStatementState; extern template class InternalFormattedIoStatementState; extern template class InternalFormattedIoStatementState; extern template class InternalListIoStatementState; extern template class InternalListIoStatementState; extern template class ExternalIoStatementState; extern template class ExternalIoStatementState; extern template class ExternalFormattedIoStatementState; extern template class ExternalFormattedIoStatementState; extern template class ExternalListIoStatementState; extern template class ExternalListIoStatementState; extern template class ExternalUnformattedIoStatementState; extern template class ExternalUnformattedIoStatementState; extern template class ChildIoStatementState; extern template class ChildIoStatementState; extern template class ChildFormattedIoStatementState; extern template class ChildFormattedIoStatementState; extern template class ChildListIoStatementState; extern template class ChildListIoStatementState; extern template class ChildUnformattedIoStatementState; extern template class ChildUnformattedIoStatementState; extern template class FormatControl< InternalFormattedIoStatementState>; extern template class FormatControl< InternalFormattedIoStatementState>; extern template class FormatControl< ExternalFormattedIoStatementState>; extern template class FormatControl< ExternalFormattedIoStatementState>; extern template class FormatControl< ChildFormattedIoStatementState>; extern template class FormatControl< ChildFormattedIoStatementState>; 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, int badUnitNumber = -1); 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 &&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 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 bytes, std::size_t elementBytes = 0); private: std::size_t bytes_{0}; }; class ExternalMiscIoStatementState : public ExternalIoStatementBase { public: enum Which { Flush, Backspace, Endfile, Rewind, Wait }; ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which, const char *sourceFile = nullptr, int sourceLine = 0) : ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {} void CompleteOperation(); int EndIoStatement(); private: Which which_; }; class ErroneousIoStatementState : public IoStatementBase { public: explicit ErroneousIoStatementState(Iostat iostat, ExternalFileUnit *unit = nullptr, const char *sourceFile = nullptr, int sourceLine = 0) : IoStatementBase{sourceFile, sourceLine}, unit_{unit} { SetPendingError(iostat); } int EndIoStatement(); ConnectionState &GetConnectionState() { return connection_; } MutableModes &mutableModes() { return connection_.modes; } private: ConnectionState connection_; ExternalFileUnit *unit_{nullptr}; }; } // namespace Fortran::runtime::io #endif // FORTRAN_RUNTIME_IO_STMT_H_