diff --git a/flang/runtime/connection.h b/flang/runtime/connection.h --- a/flang/runtime/connection.h +++ b/flang/runtime/connection.h @@ -49,7 +49,6 @@ std::int64_t currentRecordNumber{1}; // 1 is first std::int64_t positionInRecord{0}; // offset in current record std::int64_t furthestPositionInRecord{0}; // max(position+bytes) - bool nonAdvancing{false}; // ADVANCE='NO' // Set at end of non-advancing I/O data transfer std::optional leftTabLimit; // offset in current record diff --git a/flang/runtime/format-implementation.h b/flang/runtime/format-implementation.h --- a/flang/runtime/format-implementation.h +++ b/flang/runtime/format-implementation.h @@ -357,6 +357,8 @@ } } else if (ch == '/') { context.AdvanceRecord(repeat && *repeat > 0 ? *repeat : 1); + } else if (ch == '$' || ch == '\\') { + context.mutableModes().nonAdvancing = true; } else { context.SignalError(IostatErrorInFormat, "Invalid character '%c' in FORMAT", static_cast(ch)); diff --git a/flang/runtime/format.h b/flang/runtime/format.h --- a/flang/runtime/format.h +++ b/flang/runtime/format.h @@ -35,6 +35,7 @@ char delim{'\0'}; // DELIM= short scale{0}; // kP bool inNamelist{false}; // skip ! comments + bool nonAdvancing{false}; // ADVANCE='NO', or $ or \ in FORMAT }; // A single edit descriptor extracted from a FORMAT 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 @@ -437,12 +437,13 @@ bool IONAME(SetAdvance)( Cookie cookie, const char *keyword, std::size_t length) { IoStatementState &io{*cookie}; - ConnectionState &connection{io.GetConnectionState()}; - connection.nonAdvancing = - !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler()); - if (connection.nonAdvancing && connection.access == Access::Direct) { + bool nonAdvancing{ + !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler())}; + if (nonAdvancing && io.GetConnectionState().access == Access::Direct) { io.GetIoErrorHandler().SignalError( "Non-advancing I/O attempted on direct access file"); + } else { + io.mutableModes().nonAdvancing = nonAdvancing; } return true; } 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 @@ -320,7 +320,9 @@ class ExternalIoStatementState : public ExternalIoStatementBase, public IoDirectionState { public: - using ExternalIoStatementBase::ExternalIoStatementBase; + ExternalIoStatementState( + ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); + MutableModes &mutableModes() { return mutableModes_; } int EndIoStatement(); bool Emit(const char *, std::size_t, std::size_t elementBytes); bool Emit(const char *, std::size_t); @@ -333,6 +335,12 @@ 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 @@ -343,7 +351,6 @@ ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format, std::size_t formatLength, const char *sourceFile = nullptr, int sourceLine = 0); - MutableModes &mutableModes() { return mutableModes_; } int EndIoStatement(); std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1) { @@ -351,10 +358,6 @@ } 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_; FormatControl format_; }; diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp --- a/flang/runtime/io-stmt.cpp +++ b/flang/runtime/io-stmt.cpp @@ -203,9 +203,8 @@ ConnectionState &ExternalIoStatementBase::GetConnectionState() { return unit_; } int ExternalIoStatementBase::EndIoStatement() { - if (unit_.nonAdvancing) { + if (mutableModes().nonAdvancing) { unit_.leftTabLimit = unit_.furthestPositionInRecord; - unit_.nonAdvancing = false; } else { unit_.leftTabLimit.reset(); } @@ -260,14 +259,20 @@ return result; } +template +ExternalIoStatementState::ExternalIoStatementState( + ExternalFileUnit &unit, const char *sourceFile, int sourceLine) + : ExternalIoStatementBase{unit, sourceFile, sourceLine}, mutableModes_{ + unit.modes} {} + template int ExternalIoStatementState::EndIoStatement() { if constexpr (DIR == Direction::Input) { BeginReadingRecord(); // in case there were no I/O items - if (!unit().nonAdvancing) { + if (!mutableModes().nonAdvancing) { FinishReadingRecord(); } } else { - if (!unit().nonAdvancing) { + if (!mutableModes().nonAdvancing) { unit().AdvanceRecord(*this); } unit().FlushIfTerminal(*this); @@ -375,7 +380,7 @@ ExternalFileUnit &unit, const CHAR *format, std::size_t formatLength, const char *sourceFile, int sourceLine) : ExternalIoStatementState{unit, sourceFile, sourceLine}, - mutableModes_{unit.modes}, format_{*this, format, formatLength} {} + format_{*this, format, formatLength} {} template int ExternalFormattedIoStatementState::EndIoStatement() { @@ -558,7 +563,7 @@ return std::optional{' '}; } IoErrorHandler &handler{GetIoErrorHandler()}; - if (connection.nonAdvancing) { + if (mutableModes().nonAdvancing) { handler.SignalEor(); } else { handler.SignalError(IostatRecordReadOverrun); @@ -867,7 +872,7 @@ ExternalFileUnit &ext{unit()}; switch (which_) { case Flush: - ext.Flush(*this); + ext.FlushOutput(*this); std::fflush(nullptr); // flushes C stdio output streams (12.9(2)) break; case Backspace: diff --git a/flang/runtime/unit-map.cpp b/flang/runtime/unit-map.cpp --- a/flang/runtime/unit-map.cpp +++ b/flang/runtime/unit-map.cpp @@ -67,7 +67,7 @@ CriticalSection critical{lock_}; for (int j{0}; j < buckets_; ++j) { for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) { - p->unit.Flush(handler); + p->unit.FlushOutput(handler); } } } diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h --- a/flang/runtime/unit.h +++ b/flang/runtime/unit.h @@ -82,6 +82,7 @@ void FinishReadingRecord(IoErrorHandler &); bool AdvanceRecord(IoErrorHandler &); void BackspaceRecord(IoErrorHandler &); + void FlushOutput(IoErrorHandler &); void FlushIfTerminal(IoErrorHandler &); void Endfile(IoErrorHandler &); void Rewind(IoErrorHandler &); @@ -107,6 +108,7 @@ bool SetSequentialVariableFormattedRecordLength(); void DoImpliedEndfile(IoErrorHandler &); void DoEndfile(IoErrorHandler &); + void CommitWrites(); int unitNumber_{-1}; Direction direction_{Direction::Output}; diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -32,7 +32,7 @@ if (defaultOutput) { IoErrorHandler handler{terminator}; handler.HasIoStat(); // prevent nested crash if flush has error - defaultOutput->Flush(handler); + defaultOutput->FlushOutput(handler); } } @@ -118,7 +118,7 @@ } // Otherwise, OPEN on open unit with new FILE= implies CLOSE DoImpliedEndfile(handler); - Flush(handler); + FlushOutput(handler); Close(CloseStatus::Keep, handler); } set_path(std::move(newPath), newPathLength); @@ -168,7 +168,7 @@ void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) { DoImpliedEndfile(handler); - Flush(handler); + FlushOutput(handler); Close(status, handler); } @@ -462,12 +462,9 @@ ok = ok && Emit("\n", 1, 1, handler); // TODO: Windows CR+LF } } - frameOffsetInFile_ += - recordOffsetInFrame_ + recordLength.value_or(furthestPositionInRecord); - recordOffsetInFrame_ = 0; + CommitWrites(); impliedEndfile_ = true; ++currentRecordNumber; - BeginRecord(); return ok; } } @@ -499,9 +496,24 @@ } } +void ExternalFileUnit::FlushOutput(IoErrorHandler &handler) { + if (!mayPosition()) { + auto frameAt{FrameAt()}; + if (frameOffsetInFile_ >= frameAt && + frameOffsetInFile_ < + static_cast(frameAt + FrameLength())) { + // A Flush() that's about to happen to a non-positionable file + // needs to advance frameOffsetInFile_ to prevent attempts at + // impossible seeks + CommitWrites(); + } + } + Flush(handler); +} + void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) { if (isTerminal()) { - Flush(handler); + FlushOutput(handler); } } @@ -533,8 +545,6 @@ } void ExternalFileUnit::EndIoStatement() { - frameOffsetInFile_ += recordOffsetInFrame_; - recordOffsetInFrame_ = 0; io_.reset(); u_.emplace(); lock_.Drop(); @@ -585,7 +595,7 @@ void ExternalFileUnit::BeginSequentialVariableFormattedInputRecord( IoErrorHandler &handler) { if (this == defaultInput && defaultOutput) { - defaultOutput->Flush(handler); + defaultOutput->FlushOutput(handler); } std::size_t length{0}; do { @@ -701,6 +711,13 @@ impliedEndfile_ = false; } +void ExternalFileUnit::CommitWrites() { + frameOffsetInFile_ += + recordOffsetInFrame_ + recordLength.value_or(furthestPositionInRecord); + recordOffsetInFrame_ = 0; + BeginRecord(); +} + ChildIo &ExternalFileUnit::PushChildIo(IoStatementState &parent) { OwningPtr current{std::move(child_)}; Terminator &terminator{parent.GetIoErrorHandler()};