[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:
peter klausler 2020-07-03 17:57:28 -07:00
parent 85e144329c
commit 45b5c79a31
3 changed files with 92 additions and 34 deletions

View File

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

View File

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

View File

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