mirror of
https://github.com/intel/llvm.git
synced 2026-01-13 02:38:07 +08:00
[flang][runtime] Let more list-directed child input advance (#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 https://github.com/llvm/llvm-project/issues/160351.
This commit is contained in:
@@ -67,6 +67,17 @@ public:
|
||||
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=
|
||||
|
||||
@@ -703,6 +703,13 @@ public:
|
||||
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>
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
@@ -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()) {
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -928,6 +928,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
|
||||
|
||||
Reference in New Issue
Block a user