Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions flang-rt/include/flang-rt/runtime/io-error.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ class IoErrorHandler : public Terminator {
RT_API_ATTRS int GetIoStat() const { return ioStat_; }
RT_API_ATTRS bool GetIoMsg(char *, std::size_t);

// Sets the HasEnd flag so that EOF isn't fatal; used to peek ahead
RT_API_ATTRS bool SetHasEnd(bool yes = true) {
bool oldValue{(flags_ & hasEnd) != 0};
if (yes) {
flags_ |= hasEnd;
} else {
flags_ &= ~hasEnd;
}
return oldValue;
}

private:
enum Flag : std::uint8_t {
hasIoStat = 1, // IOSTAT=
Expand Down
7 changes: 7 additions & 0 deletions flang-rt/include/flang-rt/runtime/io-stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,13 @@ class ChildListIoStatementState : public ChildIoStatementState<DIR>,
using ListDirectedStatementState<DIR>::GetNextDataEdit;
RT_API_ATTRS bool AdvanceRecord(int = 1);
RT_API_ATTRS int EndIoStatement();
RT_API_ATTRS bool CanAdvance() {
return DIR == Direction::Input &&
(canAdvance_ || this->mutableModes().inNamelist);
}

private:
bool canAdvance_{false};
};

template <Direction DIR>
Expand Down
6 changes: 4 additions & 2 deletions flang-rt/lib/runtime/descriptor-io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ static RT_API_ATTRS common::optional<bool> DefinedFormattedIo(
const typeInfo::DerivedType &derived,
const typeInfo::SpecialBinding &special,
const SubscriptValue subscripts[]) {
// Look at the next data edit descriptor. If this is list-directed I/O, the
// "maxRepeat=0" argument will prevent the input from advancing over an
// Look at the next data edit descriptor. If this is list-directed input,
// the "maxRepeat=0" argument will prevent the input from advancing over an
// initial '(' that shouldn't be consumed now as the start of a real part.
// It also allows reaching EOF without crashing, since the EOF only matters
// if a child READ is actually performed.
common::optional<DataEdit> peek{io.GetNextDataEdit(/*maxRepeat=*/0)};
if (peek &&
(peek->descriptor == DataEdit::DefinedDerivedType ||
Expand Down
10 changes: 7 additions & 3 deletions flang-rt/lib/runtime/edit-input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ static RT_API_ATTRS bool EditBOZInput(
IoStatementState &io, const DataEdit &edit, void *n, std::size_t bytes) {
// Skip leading white space & zeroes
common::optional<int> remaining{io.CueUpInput(edit)};
auto start{io.GetConnectionState().positionInRecord};
const ConnectionState &connection{io.GetConnectionState()};
auto leftTabLimit{connection.leftTabLimit.value_or(0)};
auto start{connection.positionInRecord - leftTabLimit};
common::optional<char32_t> next{io.NextInField(remaining, edit)};
if (next.value_or('?') == '0') {
do {
start = io.GetConnectionState().positionInRecord;
start = connection.positionInRecord - leftTabLimit;
next = io.NextInField(remaining, edit);
} while (next && *next == '0');
}
Expand Down Expand Up @@ -447,7 +449,9 @@ static RT_API_ATTRS ScannedRealInput ScanRealInput(
}
// In list-directed input, a bad exponent is not consumed.
auto nextBeforeExponent{next};
auto startExponent{io.GetConnectionState().positionInRecord};
const ConnectionState &connection{io.GetConnectionState()};
auto leftTabLimit{connection.leftTabLimit.value_or(0)};
auto startExponent{connection.positionInRecord - leftTabLimit};
bool hasGoodExponent{false};
if (next) {
if (isHexadecimal) {
Expand Down
39 changes: 25 additions & 14 deletions flang-rt/lib/runtime/io-stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,9 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
edit.descriptor = DataEdit::ListDirectedImaginaryPart;
}
auto fastField{io.GetUpcomingFastAsciiField()};
// Reaching EOF is okay when peeking at list-directed defined input;
// pretend that there's an END= in that case.
bool oldHasEnd{maxRepeat == 0 && !io.GetIoErrorHandler().SetHasEnd()};
auto ch{io.GetNextNonBlank(byteCount, &fastField)};
if (ch && *ch == comma && eatComma_) {
// Consume comma & whitespace after previous item.
Expand All @@ -890,19 +893,23 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
ch = io.GetNextNonBlank(byteCount, &fastField);
}
eatComma_ = true;
if (!ch) {
return common::nullopt;
if (maxRepeat == 0 && !oldHasEnd) {
io.GetIoErrorHandler().SetHasEnd(false);
}
if (*ch == '/') {
if (!ch) { // EOF
if (maxRepeat == 0) {
return edit; // DataEdit::ListDirected for look-ahead
} else {
return common::nullopt;
}
} else if (*ch == '/') {
hitSlash_ = true;
edit.descriptor = DataEdit::ListDirectedNullValue;
return edit;
}
if (*ch == comma) { // separator: null value
} else if (*ch == comma) { // separator: null value
edit.descriptor = DataEdit::ListDirectedNullValue;
return edit;
}
if (imaginaryPart_) { // can't repeat components
} else if (imaginaryPart_) { // can't repeat components
return edit;
}
if (*ch >= '0' && *ch <= '9' && fastField.MightBeRepetitionCount()) {
Expand Down Expand Up @@ -1103,10 +1110,19 @@ ChildListIoStatementState<DIR>::ChildListIoStatementState(
: ChildIoStatementState<DIR>{child, sourceFile, sourceLine} {
#if !defined(RT_DEVICE_AVOID_RECURSION)
if constexpr (DIR == Direction::Input) {
if (auto *listInput{child.parent()
if (const auto *listInput{child.parent()
.get_if<ListDirectedStatementState<Direction::Input>>()}) {
this->set_eatComma(listInput->eatComma());
this->namelistGroup_ = listInput->namelistGroup();
if (auto *childListInput{child.parent()
.get_if<ChildListIoStatementState<Direction::Input>>()}) {
// Child list input whose parent is child list input: can advance
// if the parent can.
this->canAdvance_ = childListInput->CanAdvance();
} else {
// Child list input of top-level list input: can advance.
this->canAdvance_ = true;
}
}
}
#else
Expand All @@ -1117,12 +1133,7 @@ ChildListIoStatementState<DIR>::ChildListIoStatementState(
template <Direction DIR>
bool ChildListIoStatementState<DIR>::AdvanceRecord(int n) {
#if !defined(RT_DEVICE_AVOID_RECURSION)
// Allow child NAMELIST input to advance
if (DIR == Direction::Input && this->mutableModes().inNamelist) {
return this->child().parent().AdvanceRecord(n);
} else {
return false;
}
return this->CanAdvance() && this->child().parent().AdvanceRecord(n);
#else
this->ReportUnsupportedChildIo();
#endif
Expand Down
11 changes: 11 additions & 0 deletions flang/docs/Extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,17 @@ print *, [(j,j=1,10)]
and the portable interpretation across the most common Fortran
compilers.

* `NAMELIST` child input statements are allowed to advance to further
input records.
Further, advancement is allowed when the parent input statement is
a non-child (top level) list-directed input statement, or, recursively,
an intermediate child list-directed input statement that can advance.
This means that non-`NAMELIST` list-directed child input statements are
not allowed to advance when they have an ancestor formatted input statement
that is not list-directed and there is no intervening `NAMELIST`.
This design allows format-driven input with `DT` editing to retain
control over advancement in child input, while otherwise allowing it.

## De Facto Standard Features

* `EXTENDS_TYPE_OF()` returns `.TRUE.` if both of its arguments have the
Expand Down
Loading