Skip to content

Commit 673e305

Browse files
authored
[flang][runtime] Let more list-directed child input advance (llvm#160590)
Whether list-directed child READ statements should be allowed to advance to further records is neither explicit in the standard nor consistent in existing Fortran implementations. We allow child namelist READ statements to advance, but not other list- directed child input. This patch refines our interpretation of this case. Child namelist READ statements continue to be able to advance; in addition, non-namelist child READ statements can now advance if their parent READ statement is a list-directed input statement at the top level, or a child that could. But non-namelist list-directed child input taking place in a context with explicit format control won't advance to following records, so that the format-controlled parent READ statement can retain control over record advancement. Also corrects two cases of record repositioning in numeric input editing, which were failing under child input because they weren't allowing for left tab limits. Fixes llvm#160351.
1 parent 2780c20 commit 673e305

File tree

6 files changed

+65
-19
lines changed

6 files changed

+65
-19
lines changed

flang-rt/include/flang-rt/runtime/io-error.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ class IoErrorHandler : public Terminator {
6767
RT_API_ATTRS int GetIoStat() const { return ioStat_; }
6868
RT_API_ATTRS bool GetIoMsg(char *, std::size_t);
6969

70+
// Sets the HasEnd flag so that EOF isn't fatal; used to peek ahead
71+
RT_API_ATTRS bool SetHasEnd(bool yes = true) {
72+
bool oldValue{(flags_ & hasEnd) != 0};
73+
if (yes) {
74+
flags_ |= hasEnd;
75+
} else {
76+
flags_ &= ~hasEnd;
77+
}
78+
return oldValue;
79+
}
80+
7081
private:
7182
enum Flag : std::uint8_t {
7283
hasIoStat = 1, // IOSTAT=

flang-rt/include/flang-rt/runtime/io-stmt.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,13 @@ class ChildListIoStatementState : public ChildIoStatementState<DIR>,
703703
using ListDirectedStatementState<DIR>::GetNextDataEdit;
704704
RT_API_ATTRS bool AdvanceRecord(int = 1);
705705
RT_API_ATTRS int EndIoStatement();
706+
RT_API_ATTRS bool CanAdvance() {
707+
return DIR == Direction::Input &&
708+
(canAdvance_ || this->mutableModes().inNamelist);
709+
}
710+
711+
private:
712+
bool canAdvance_{false};
706713
};
707714

708715
template <Direction DIR>

flang-rt/lib/runtime/descriptor-io.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ static RT_API_ATTRS common::optional<bool> DefinedFormattedIo(
4747
const typeInfo::DerivedType &derived,
4848
const typeInfo::SpecialBinding &special,
4949
const SubscriptValue subscripts[]) {
50-
// Look at the next data edit descriptor. If this is list-directed I/O, the
51-
// "maxRepeat=0" argument will prevent the input from advancing over an
50+
// Look at the next data edit descriptor. If this is list-directed input,
51+
// the "maxRepeat=0" argument will prevent the input from advancing over an
5252
// initial '(' that shouldn't be consumed now as the start of a real part.
53+
// It also allows reaching EOF without crashing, since the EOF only matters
54+
// if a child READ is actually performed.
5355
common::optional<DataEdit> peek{io.GetNextDataEdit(/*maxRepeat=*/0)};
5456
if (peek &&
5557
(peek->descriptor == DataEdit::DefinedDerivedType ||

flang-rt/lib/runtime/edit-input.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ static RT_API_ATTRS bool EditBOZInput(
5353
IoStatementState &io, const DataEdit &edit, void *n, std::size_t bytes) {
5454
// Skip leading white space & zeroes
5555
common::optional<int> remaining{io.CueUpInput(edit)};
56-
auto start{io.GetConnectionState().positionInRecord};
56+
const ConnectionState &connection{io.GetConnectionState()};
57+
auto leftTabLimit{connection.leftTabLimit.value_or(0)};
58+
auto start{connection.positionInRecord - leftTabLimit};
5759
common::optional<char32_t> next{io.NextInField(remaining, edit)};
5860
if (next.value_or('?') == '0') {
5961
do {
60-
start = io.GetConnectionState().positionInRecord;
62+
start = connection.positionInRecord - leftTabLimit;
6163
next = io.NextInField(remaining, edit);
6264
} while (next && *next == '0');
6365
}
@@ -447,7 +449,9 @@ static RT_API_ATTRS ScannedRealInput ScanRealInput(
447449
}
448450
// In list-directed input, a bad exponent is not consumed.
449451
auto nextBeforeExponent{next};
450-
auto startExponent{io.GetConnectionState().positionInRecord};
452+
const ConnectionState &connection{io.GetConnectionState()};
453+
auto leftTabLimit{connection.leftTabLimit.value_or(0)};
454+
auto startExponent{connection.positionInRecord - leftTabLimit};
451455
bool hasGoodExponent{false};
452456
if (next) {
453457
if (isHexadecimal) {

flang-rt/lib/runtime/io-stmt.cpp

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,9 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
880880
edit.descriptor = DataEdit::ListDirectedImaginaryPart;
881881
}
882882
auto fastField{io.GetUpcomingFastAsciiField()};
883+
// Reaching EOF is okay when peeking at list-directed defined input;
884+
// pretend that there's an END= in that case.
885+
bool oldHasEnd{maxRepeat == 0 && !io.GetIoErrorHandler().SetHasEnd()};
883886
auto ch{io.GetNextNonBlank(byteCount, &fastField)};
884887
if (ch && *ch == comma && eatComma_) {
885888
// Consume comma & whitespace after previous item.
@@ -890,19 +893,23 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
890893
ch = io.GetNextNonBlank(byteCount, &fastField);
891894
}
892895
eatComma_ = true;
893-
if (!ch) {
894-
return common::nullopt;
896+
if (maxRepeat == 0 && !oldHasEnd) {
897+
io.GetIoErrorHandler().SetHasEnd(false);
895898
}
896-
if (*ch == '/') {
899+
if (!ch) { // EOF
900+
if (maxRepeat == 0) {
901+
return edit; // DataEdit::ListDirected for look-ahead
902+
} else {
903+
return common::nullopt;
904+
}
905+
} else if (*ch == '/') {
897906
hitSlash_ = true;
898907
edit.descriptor = DataEdit::ListDirectedNullValue;
899908
return edit;
900-
}
901-
if (*ch == comma) { // separator: null value
909+
} else if (*ch == comma) { // separator: null value
902910
edit.descriptor = DataEdit::ListDirectedNullValue;
903911
return edit;
904-
}
905-
if (imaginaryPart_) { // can't repeat components
912+
} else if (imaginaryPart_) { // can't repeat components
906913
return edit;
907914
}
908915
if (*ch >= '0' && *ch <= '9' && fastField.MightBeRepetitionCount()) {
@@ -1103,10 +1110,19 @@ ChildListIoStatementState<DIR>::ChildListIoStatementState(
11031110
: ChildIoStatementState<DIR>{child, sourceFile, sourceLine} {
11041111
#if !defined(RT_DEVICE_AVOID_RECURSION)
11051112
if constexpr (DIR == Direction::Input) {
1106-
if (auto *listInput{child.parent()
1113+
if (const auto *listInput{child.parent()
11071114
.get_if<ListDirectedStatementState<Direction::Input>>()}) {
11081115
this->set_eatComma(listInput->eatComma());
11091116
this->namelistGroup_ = listInput->namelistGroup();
1117+
if (auto *childListInput{child.parent()
1118+
.get_if<ChildListIoStatementState<Direction::Input>>()}) {
1119+
// Child list input whose parent is child list input: can advance
1120+
// if the parent can.
1121+
this->canAdvance_ = childListInput->CanAdvance();
1122+
} else {
1123+
// Child list input of top-level list input: can advance.
1124+
this->canAdvance_ = true;
1125+
}
11101126
}
11111127
}
11121128
#else
@@ -1117,12 +1133,7 @@ ChildListIoStatementState<DIR>::ChildListIoStatementState(
11171133
template <Direction DIR>
11181134
bool ChildListIoStatementState<DIR>::AdvanceRecord(int n) {
11191135
#if !defined(RT_DEVICE_AVOID_RECURSION)
1120-
// Allow child NAMELIST input to advance
1121-
if (DIR == Direction::Input && this->mutableModes().inNamelist) {
1122-
return this->child().parent().AdvanceRecord(n);
1123-
} else {
1124-
return false;
1125-
}
1136+
return this->CanAdvance() && this->child().parent().AdvanceRecord(n);
11261137
#else
11271138
this->ReportUnsupportedChildIo();
11281139
#endif

flang/docs/Extensions.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,17 @@ print *, [(j,j=1,10)]
928928
and the portable interpretation across the most common Fortran
929929
compilers.
930930

931+
* `NAMELIST` child input statements are allowed to advance to further
932+
input records.
933+
Further, advancement is allowed when the parent input statement is
934+
a non-child (top level) list-directed input statement, or, recursively,
935+
an intermediate child list-directed input statement that can advance.
936+
This means that non-`NAMELIST` list-directed child input statements are
937+
not allowed to advance when they have an ancestor formatted input statement
938+
that is not list-directed and there is no intervening `NAMELIST`.
939+
This design allows format-driven input with `DT` editing to retain
940+
control over advancement in child input, while otherwise allowing it.
941+
931942
## De Facto Standard Features
932943

933944
* `EXTENDS_TYPE_OF()` returns `.TRUE.` if both of its arguments have the

0 commit comments

Comments
 (0)