Skip to content

Commit 918d6db

Browse files
authored
[flang][runtime] Refine state associated with child I/O (#150461)
Child I/O state needs to carry a pointer to the original non-type-bound defined I/O subroutine table, so that nested defined I/O can call those defined I/O subroutines. It also needs to maintain a mutableModes instance for the whole invocation of defined I/O, instead of having a mutableModes local to list-directed child I/O, so that a top-level data transfer statement with (say) DECIMAL='COMMA' propagates that setting down to nested child I/O data transfers. Fixes #149885.
1 parent 129db4d commit 918d6db

File tree

4 files changed

+81
-30
lines changed

4 files changed

+81
-30
lines changed

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ class IoStatementState {
8484
// This design avoids virtual member functions and function pointers,
8585
// which may not have good support in some runtime environments.
8686

87+
RT_API_ATTRS const NonTbpDefinedIoTable *nonTbpDefinedIoTable() const;
88+
RT_API_ATTRS void set_nonTbpDefinedIoTable(const NonTbpDefinedIoTable *);
89+
8790
// CompleteOperation() is the last opportunity to raise an I/O error.
8891
// It is called by EndIoStatement(), but it can be invoked earlier to
8992
// catch errors for (e.g.) GetIoMsg() and GetNewUnit(). If called
@@ -363,6 +366,13 @@ class IoStatementBase : public IoErrorHandler {
363366
using IoErrorHandler::IoErrorHandler;
364367

365368
RT_API_ATTRS bool completedOperation() const { return completedOperation_; }
369+
RT_API_ATTRS const NonTbpDefinedIoTable *nonTbpDefinedIoTable() const {
370+
return nonTbpDefinedIoTable_;
371+
}
372+
RT_API_ATTRS void set_nonTbpDefinedIoTable(
373+
const NonTbpDefinedIoTable *table) {
374+
nonTbpDefinedIoTable_ = table;
375+
}
366376

367377
RT_API_ATTRS void CompleteOperation() { completedOperation_ = true; }
368378
RT_API_ATTRS int EndIoStatement() { return GetIoStat(); }
@@ -397,6 +407,11 @@ class IoStatementBase : public IoErrorHandler {
397407

398408
protected:
399409
bool completedOperation_{false};
410+
411+
private:
412+
// Original NonTbpDefinedIoTable argument to Input/OutputDerivedType,
413+
// saved here so that it can also be used in child I/O statements.
414+
const NonTbpDefinedIoTable *nonTbpDefinedIoTable_{nullptr};
400415
};
401416

402417
// Common state for list-directed & NAMELIST I/O, both internal & external
@@ -630,8 +645,10 @@ class ChildIoStatementState : public IoStatementBase,
630645
public:
631646
RT_API_ATTRS ChildIoStatementState(
632647
ChildIo &, const char *sourceFile = nullptr, int sourceLine = 0);
648+
RT_API_ATTRS const NonTbpDefinedIoTable *nonTbpDefinedIoTable() const;
649+
RT_API_ATTRS void set_nonTbpDefinedIoTable(const NonTbpDefinedIoTable *);
633650
RT_API_ATTRS ChildIo &child() { return child_; }
634-
RT_API_ATTRS MutableModes &mutableModes();
651+
RT_API_ATTRS MutableModes &mutableModes() { return mutableModes_; }
635652
RT_API_ATTRS ConnectionState &GetConnectionState();
636653
RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const;
637654
RT_API_ATTRS int EndIoStatement();
@@ -644,6 +661,7 @@ class ChildIoStatementState : public IoStatementBase,
644661

645662
private:
646663
ChildIo &child_;
664+
MutableModes mutableModes_;
647665
};
648666

649667
template <Direction DIR, typename CHAR>
@@ -654,7 +672,6 @@ class ChildFormattedIoStatementState : public ChildIoStatementState<DIR>,
654672
RT_API_ATTRS ChildFormattedIoStatementState(ChildIo &, const CharType *format,
655673
std::size_t formatLength, const Descriptor *formatDescriptor = nullptr,
656674
const char *sourceFile = nullptr, int sourceLine = 0);
657-
RT_API_ATTRS MutableModes &mutableModes() { return mutableModes_; }
658675
RT_API_ATTRS void CompleteOperation();
659676
RT_API_ATTRS int EndIoStatement();
660677
RT_API_ATTRS bool AdvanceRecord(int = 1);
@@ -664,7 +681,6 @@ class ChildFormattedIoStatementState : public ChildIoStatementState<DIR>,
664681
}
665682

666683
private:
667-
MutableModes mutableModes_;
668684
FormatControl<ChildFormattedIoStatementState> format_;
669685
};
670686

@@ -840,7 +856,7 @@ class InquireUnconnectedFileState : public NoUnitIoStatementState {
840856
};
841857

842858
class InquireIOLengthState : public NoUnitIoStatementState,
843-
public OutputStatementState {
859+
public IoDirectionState<Direction::Output> {
844860
public:
845861
RT_API_ATTRS InquireIOLengthState(
846862
const char *sourceFile = nullptr, int sourceLine = 0);

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

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,19 @@ static RT_API_ATTRS Fortran::common::optional<bool> DefinedFormattedIo(
4747
const typeInfo::DerivedType &derived,
4848
const typeInfo::SpecialBinding &special,
4949
const SubscriptValue subscripts[]) {
50-
Fortran::common::optional<DataEdit> peek{
51-
io.GetNextDataEdit(0 /*to peek at it*/)};
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
52+
// initial '(' that shouldn't be consumed now as the start of a real part.
53+
Fortran::common::optional<DataEdit> peek{io.GetNextDataEdit(/*maxRepeat=*/0)};
5254
if (peek &&
5355
(peek->descriptor == DataEdit::DefinedDerivedType ||
54-
peek->descriptor == DataEdit::ListDirected)) {
56+
peek->descriptor == DataEdit::ListDirected ||
57+
peek->descriptor == DataEdit::ListDirectedRealPart)) {
5558
// Defined formatting
5659
IoErrorHandler &handler{io.GetIoErrorHandler()};
57-
DataEdit edit{*io.GetNextDataEdit(1)}; // now consume it; no repeats
58-
RUNTIME_CHECK(handler, edit.descriptor == peek->descriptor);
60+
DataEdit edit{peek->descriptor == DataEdit::ListDirectedRealPart
61+
? *peek
62+
: *io.GetNextDataEdit(1)};
5963
char ioType[2 + edit.maxIoTypeChars];
6064
auto ioTypeLen{std::size_t{2} /*"DT"*/ + edit.ioTypeChars};
6165
if (edit.descriptor == DataEdit::DefinedDerivedType) {
@@ -836,13 +840,23 @@ template RT_API_ATTRS int DescriptorIoTicket<Direction::Input>::Continue(
836840

837841
template <Direction DIR>
838842
RT_API_ATTRS bool DescriptorIO(IoStatementState &io,
839-
const Descriptor &descriptor, const NonTbpDefinedIoTable *table) {
843+
const Descriptor &descriptor, const NonTbpDefinedIoTable *originalTable) {
840844
bool anyIoTookPlace{false};
845+
const NonTbpDefinedIoTable *defaultTable{io.nonTbpDefinedIoTable()};
846+
const NonTbpDefinedIoTable *table{originalTable};
847+
if (!table) {
848+
table = defaultTable;
849+
} else if (table != defaultTable) {
850+
io.set_nonTbpDefinedIoTable(table); // for nested I/O
851+
}
841852
WorkQueue workQueue{io.GetIoErrorHandler()};
842853
if (workQueue.BeginDescriptorIo<DIR>(io, descriptor, table, anyIoTookPlace) ==
843854
StatContinue) {
844855
workQueue.Run();
845856
}
857+
if (defaultTable != table) {
858+
io.set_nonTbpDefinedIoTable(defaultTable);
859+
}
846860
return anyIoTookPlace;
847861
}
848862

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ static RT_API_ATTRS bool CheckCompleteListDirectedField(
3737
if (edit.IsListDirected()) {
3838
std::size_t byteCount;
3939
if (auto ch{io.GetCurrentChar(byteCount)}) {
40-
if (IsCharValueSeparator(edit, *ch)) {
41-
return true;
42-
} else {
40+
if (!IsCharValueSeparator(edit, *ch)) {
4341
const auto &connection{io.GetConnectionState()};
4442
io.GetIoErrorHandler().SignalError(IostatBadListDirectedInputSeparator,
4543
"invalid character (0x%x) after list-directed input value, "
@@ -49,12 +47,9 @@ static RT_API_ATTRS bool CheckCompleteListDirectedField(
4947
static_cast<int>(connection.currentRecordNumber));
5048
return false;
5149
}
52-
} else {
53-
return true; // end of record: ok
5450
}
55-
} else {
56-
return true;
5751
}
52+
return true;
5853
}
5954

6055
template <int LOG2_BASE>
@@ -537,9 +532,11 @@ static RT_API_ATTRS ScannedRealInput ScanRealInput(
537532
io.SkipSpaces(remaining);
538533
next = io.NextInField(remaining, edit);
539534
}
540-
if (!next) { // NextInField fails on separators like ')'
535+
if (!next || *next == ')') { // NextInField fails on separators like ')'
541536
std::size_t byteCount{0};
542-
next = io.GetCurrentChar(byteCount);
537+
if (!next) {
538+
next = io.GetCurrentChar(byteCount);
539+
}
543540
if (next && *next == ')') {
544541
io.HandleRelativePosition(byteCount);
545542
}

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

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,17 @@ Fortran::common::optional<DataEdit> IoStatementState::GetNextDataEdit(int n) {
526526
[&](auto &x) { return x.get().GetNextDataEdit(*this, n); }, u_);
527527
}
528528

529+
const NonTbpDefinedIoTable *IoStatementState::nonTbpDefinedIoTable() const {
530+
return common::visit(
531+
[&](auto &x) { return x.get().nonTbpDefinedIoTable(); }, u_);
532+
}
533+
534+
void IoStatementState::set_nonTbpDefinedIoTable(
535+
const NonTbpDefinedIoTable *table) {
536+
common::visit(
537+
[&](auto &x) { return x.get().set_nonTbpDefinedIoTable(table); }, u_);
538+
}
539+
529540
bool IoStatementState::Emit(
530541
const char *data, std::size_t bytes, std::size_t elementBytes) {
531542
return common::visit(
@@ -633,10 +644,10 @@ IoStatementState::FastAsciiField IoStatementState::GetUpcomingFastAsciiField() {
633644
if (!connection.isUTF8 && connection.internalIoCharKind <= 1) {
634645
const char *p{nullptr};
635646
if (std::size_t bytes{GetNextInputBytes(p)}) {
636-
return FastAsciiField(connection, p, bytes);
647+
return FastAsciiField{connection, p, bytes};
637648
}
638649
}
639-
return FastAsciiField(connection);
650+
return FastAsciiField{connection};
640651
}
641652

642653
Fortran::common::optional<char32_t> IoStatementState::NextInField(
@@ -920,9 +931,12 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
920931
fastField.connection().positionInRecord = start;
921932
}
922933
}
923-
if (!imaginaryPart_ && ch && *ch == '(') {
924-
realPart_ = true;
925-
fastField.connection().HandleRelativePosition(byteCount);
934+
if (!imaginaryPart_ && edit.descriptor == DataEdit::ListDirected && ch &&
935+
*ch == '(') {
936+
if (maxRepeat > 0) { // not being peeked at fram DefinedFormattedIo()
937+
realPart_ = true;
938+
fastField.connection().HandleRelativePosition(byteCount);
939+
}
926940
edit.descriptor = DataEdit::ListDirectedRealPart;
927941
}
928942
return edit;
@@ -952,12 +966,24 @@ bool ExternalUnformattedIoStatementState<DIR>::Receive(
952966
template <Direction DIR>
953967
ChildIoStatementState<DIR>::ChildIoStatementState(
954968
ChildIo &child, const char *sourceFile, int sourceLine)
955-
: IoStatementBase{sourceFile, sourceLine}, child_{child} {}
969+
: IoStatementBase{sourceFile, sourceLine}, child_{child},
970+
mutableModes_{child.parent().mutableModes()} {}
971+
972+
template <Direction DIR>
973+
const NonTbpDefinedIoTable *
974+
ChildIoStatementState<DIR>::nonTbpDefinedIoTable() const {
975+
#if !defined(RT_DEVICE_AVOID_RECURSION)
976+
return child_.parent().nonTbpDefinedIoTable();
977+
#else
978+
ReportUnsupportedChildIo();
979+
#endif
980+
}
956981

957982
template <Direction DIR>
958-
MutableModes &ChildIoStatementState<DIR>::mutableModes() {
983+
void ChildIoStatementState<DIR>::set_nonTbpDefinedIoTable(
984+
const NonTbpDefinedIoTable *table) {
959985
#if !defined(RT_DEVICE_AVOID_RECURSION)
960-
return child_.parent().mutableModes();
986+
child_.parent().set_nonTbpDefinedIoTable(table);
961987
#else
962988
ReportUnsupportedChildIo();
963989
#endif
@@ -1030,9 +1056,7 @@ ChildFormattedIoStatementState<DIR, CHAR>::ChildFormattedIoStatementState(
10301056
ChildIo &child, const CHAR *format, std::size_t formatLength,
10311057
const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)
10321058
: ChildIoStatementState<DIR>{child, sourceFile, sourceLine},
1033-
mutableModes_{child.parent().mutableModes()}, format_{*this, format,
1034-
formatLength,
1035-
formatDescriptor} {}
1059+
format_{*this, format, formatLength, formatDescriptor} {}
10361060

10371061
template <Direction DIR, typename CHAR>
10381062
void ChildFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {

0 commit comments

Comments
 (0)