[flang] Extend & fix per-I/O-statement state (ext. I/O work part 7)
The per-I/O-statement state structures need to support missing external I/O statements, and some bugs found in testing with formatted input and record advancement are fixed. The effects of these changes will not be visible until further patches to the I/O API handlers are pushed. Reviewed By: tskeith Differential Revision: https://reviews.llvm.org/D83151
This commit is contained in:
parent
85e144329c
commit
45b5c79a31
|
@ -13,6 +13,7 @@
|
|||
#include "tools.h"
|
||||
#include "unit.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
|
@ -182,10 +183,10 @@ int NoopCloseStatementState::EndIoStatement() {
|
|||
}
|
||||
|
||||
template <Direction DIR> int ExternalIoStatementState<DIR>::EndIoStatement() {
|
||||
if (!unit().nonAdvancing) {
|
||||
unit().AdvanceRecord(*this);
|
||||
}
|
||||
if constexpr (DIR == Direction::Output) {
|
||||
if (!unit().nonAdvancing) {
|
||||
unit().AdvanceRecord(*this);
|
||||
}
|
||||
unit().FlushIfTerminal(*this);
|
||||
}
|
||||
return ExternalIoStatementBase::EndIoStatement();
|
||||
|
@ -291,7 +292,7 @@ void IoStatementState::BackspaceRecord() {
|
|||
}
|
||||
|
||||
void IoStatementState::HandleRelativePosition(std::int64_t n) {
|
||||
return std::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_);
|
||||
std::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_);
|
||||
}
|
||||
|
||||
int IoStatementState::EndIoStatement() {
|
||||
|
@ -347,15 +348,22 @@ bool IoStatementState::EmitField(
|
|||
}
|
||||
}
|
||||
|
||||
void IoStatementState::SkipSpaces(std::optional<int> &remaining) {
|
||||
if (!remaining || *remaining > 0) {
|
||||
for (auto ch{GetCurrentChar()}; ch && ch == ' '; ch = GetCurrentChar()) {
|
||||
HandleRelativePosition(1);
|
||||
if (remaining && !--*remaining) {
|
||||
break;
|
||||
std::optional<char32_t> IoStatementState::SkipSpaces(
|
||||
std::optional<int> &remaining) {
|
||||
while (!remaining || *remaining > 0) {
|
||||
if (auto ch{GetCurrentChar()}) {
|
||||
if (*ch != ' ') {
|
||||
return ch;
|
||||
}
|
||||
HandleRelativePosition(1);
|
||||
if (remaining) {
|
||||
--*remaining;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<char32_t> IoStatementState::NextInField(
|
||||
|
@ -372,6 +380,7 @@ std::optional<char32_t> IoStatementState::NextInField(
|
|||
case '\'':
|
||||
case '"':
|
||||
case '*':
|
||||
case '\n': // for stream access
|
||||
break;
|
||||
default:
|
||||
HandleRelativePosition(1);
|
||||
|
@ -385,7 +394,8 @@ std::optional<char32_t> IoStatementState::NextInField(
|
|||
return next;
|
||||
}
|
||||
const ConnectionState &connection{GetConnectionState()};
|
||||
if (!connection.IsAtEOF() && connection.recordLength &&
|
||||
if (!connection.IsAtEOF() && connection.isFixedRecordLength &&
|
||||
connection.recordLength &&
|
||||
connection.positionInRecord >= *connection.recordLength) {
|
||||
if (connection.modes.pad) { // PAD='YES'
|
||||
--*remaining;
|
||||
|
@ -553,29 +563,39 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
|
|||
return edit;
|
||||
}
|
||||
|
||||
template <Direction DIR>
|
||||
bool UnformattedIoStatementState<DIR>::Receive(char *data, std::size_t bytes) {
|
||||
if constexpr (DIR == Direction::Output) {
|
||||
this->Crash(
|
||||
"UnformattedIoStatementState::Receive() called for output statement");
|
||||
}
|
||||
return this->unit().Receive(data, bytes, *this);
|
||||
}
|
||||
|
||||
template <Direction DIR>
|
||||
int UnformattedIoStatementState<DIR>::EndIoStatement() {
|
||||
auto &ext{static_cast<ExternalIoStatementState<DIR> &>(*this)};
|
||||
ExternalFileUnit &unit{ext.unit()};
|
||||
if (unit.access == Access::Sequential && !unit.recordLength.has_value()) {
|
||||
// Overwrite the first four bytes of the record with its length,
|
||||
// and also append the length. These four bytes were skipped over
|
||||
// in BeginUnformattedOutput().
|
||||
// TODO: Break very large records up into subrecords with negative
|
||||
// headers &/or footers
|
||||
union {
|
||||
std::uint32_t u;
|
||||
char c[sizeof u];
|
||||
} u;
|
||||
u.u = unit.furthestPositionInRecord - sizeof u.c;
|
||||
// TODO: Convert record length to little-endian on big-endian host?
|
||||
if (!(ext.Emit(u.c, sizeof u.c) &&
|
||||
(ext.HandleAbsolutePosition(0), ext.Emit(u.c, sizeof u.c)) &&
|
||||
ext.AdvanceRecord())) {
|
||||
return false;
|
||||
ExternalFileUnit &unit{this->unit()};
|
||||
if constexpr (DIR == Direction::Output) {
|
||||
if (unit.access == Access::Sequential && !unit.isFixedRecordLength) {
|
||||
// Append the length of a sequential unformatted variable-length record
|
||||
// as its footer, then overwrite the reserved first four bytes of the
|
||||
// record with its length as its header. These four bytes were skipped
|
||||
// over in BeginUnformattedOutput().
|
||||
// TODO: Break very large records up into subrecords with negative
|
||||
// headers &/or footers
|
||||
union {
|
||||
std::uint32_t u;
|
||||
char c[sizeof u];
|
||||
} u;
|
||||
u.u = unit.furthestPositionInRecord - sizeof u;
|
||||
// TODO: Convert record length to little-endian on big-endian host?
|
||||
if (!(this->Emit(u.c, sizeof u) &&
|
||||
(this->HandleAbsolutePosition(0), this->Emit(u.c, sizeof u)))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ext.EndIoStatement();
|
||||
return ExternalIoStatementState<DIR>::EndIoStatement();
|
||||
}
|
||||
|
||||
template class InternalIoStatementState<Direction::Output>;
|
||||
|
@ -592,4 +612,25 @@ template class ExternalListIoStatementState<Direction::Output>;
|
|||
template class ExternalListIoStatementState<Direction::Input>;
|
||||
template class UnformattedIoStatementState<Direction::Output>;
|
||||
template class UnformattedIoStatementState<Direction::Input>;
|
||||
|
||||
int ExternalMiscIoStatementState::EndIoStatement() {
|
||||
ExternalFileUnit &ext{unit()};
|
||||
switch (which_) {
|
||||
case Flush:
|
||||
ext.Flush(*this);
|
||||
std::fflush(nullptr); // flushes C stdio output streams (12.9(2))
|
||||
break;
|
||||
case Backspace:
|
||||
ext.BackspaceRecord(*this);
|
||||
break;
|
||||
case Endfile:
|
||||
ext.Endfile(*this);
|
||||
break;
|
||||
case Rewind:
|
||||
ext.Rewind(*this);
|
||||
break;
|
||||
}
|
||||
return ExternalIoStatementBase::EndIoStatement();
|
||||
}
|
||||
|
||||
} // namespace Fortran::runtime::io
|
||||
|
|
|
@ -36,6 +36,7 @@ template <Direction, typename CHAR = char>
|
|||
class ExternalFormattedIoStatementState;
|
||||
template <Direction> class ExternalListIoStatementState;
|
||||
template <Direction> class UnformattedIoStatementState;
|
||||
class ExternalMiscIoStatementState;
|
||||
|
||||
// The Cookie type in the I/O API is a pointer (for C) to this class.
|
||||
class IoStatementState {
|
||||
|
@ -73,7 +74,8 @@ public:
|
|||
|
||||
bool EmitRepeated(char, std::size_t);
|
||||
bool EmitField(const char *, std::size_t length, std::size_t width);
|
||||
void SkipSpaces(std::optional<int> &remaining);
|
||||
|
||||
std::optional<char32_t> SkipSpaces(std::optional<int> &remaining);
|
||||
std::optional<char32_t> NextInField(std::optional<int> &remaining);
|
||||
std::optional<char32_t> GetNextNonBlank(); // can advance record
|
||||
|
||||
|
@ -94,7 +96,8 @@ private:
|
|||
std::reference_wrapper<ExternalListIoStatementState<Direction::Output>>,
|
||||
std::reference_wrapper<ExternalListIoStatementState<Direction::Input>>,
|
||||
std::reference_wrapper<UnformattedIoStatementState<Direction::Output>>,
|
||||
std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>>
|
||||
std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>,
|
||||
std::reference_wrapper<ExternalMiscIoStatementState>>
|
||||
u_;
|
||||
};
|
||||
|
||||
|
@ -235,7 +238,7 @@ class ExternalIoStatementState : public ExternalIoStatementBase,
|
|||
public:
|
||||
using ExternalIoStatementBase::ExternalIoStatementBase;
|
||||
int EndIoStatement();
|
||||
bool Emit(const char *, std::size_t chars /* not bytes */);
|
||||
bool Emit(const char *, std::size_t);
|
||||
bool Emit(const char16_t *, std::size_t chars /* not bytes */);
|
||||
bool Emit(const char32_t *, std::size_t chars /* not bytes */);
|
||||
std::optional<char32_t> GetCurrentChar();
|
||||
|
@ -280,6 +283,7 @@ template <Direction DIR>
|
|||
class UnformattedIoStatementState : public ExternalIoStatementState<DIR> {
|
||||
public:
|
||||
using ExternalIoStatementState<DIR>::ExternalIoStatementState;
|
||||
bool Receive(char *, std::size_t);
|
||||
int EndIoStatement();
|
||||
};
|
||||
|
||||
|
@ -353,5 +357,17 @@ extern template class FormatControl<
|
|||
extern template class FormatControl<
|
||||
ExternalFormattedIoStatementState<Direction::Input>>;
|
||||
|
||||
class ExternalMiscIoStatementState : public ExternalIoStatementBase {
|
||||
public:
|
||||
enum Which { Flush, Backspace, Endfile, Rewind };
|
||||
ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which,
|
||||
const char *sourceFile = nullptr, int sourceLine = 0)
|
||||
: ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {}
|
||||
int EndIoStatement();
|
||||
|
||||
private:
|
||||
Which which_;
|
||||
};
|
||||
|
||||
} // namespace Fortran::runtime::io
|
||||
#endif // FORTRAN_RUNTIME_IO_STMT_H_
|
||||
|
|
|
@ -106,7 +106,8 @@ private:
|
|||
ExternalListIoStatementState<Direction::Output>,
|
||||
ExternalListIoStatementState<Direction::Input>,
|
||||
UnformattedIoStatementState<Direction::Output>,
|
||||
UnformattedIoStatementState<Direction::Input>>
|
||||
UnformattedIoStatementState<Direction::Input>,
|
||||
ExternalMiscIoStatementState>
|
||||
u_;
|
||||
|
||||
// Points to the active alternative (if any) in u_ for use as a Cookie
|
||||
|
|
Loading…
Reference in New Issue