[llvm] [flang][runtime] Refine state associated with child I/O (PR #150461)
Peter Klausler via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 24 09:58:49 PDT 2025
https://github.com/klausler created https://github.com/llvm/llvm-project/pull/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 https://github.com/llvm/llvm-project/issues/149885.
>From 98ee0fa566b8bef251eec4e180551352311d808a Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Thu, 24 Jul 2025 08:57:02 -0700
Subject: [PATCH] [flang][runtime] Refine state associated with child I/O
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 https://github.com/llvm/llvm-project/issues/149885.
---
flang-rt/include/flang-rt/runtime/io-stmt.h | 24 +++++++++--
flang-rt/lib/runtime/descriptor-io.cpp | 16 ++++++--
flang-rt/lib/runtime/edit-input.cpp | 9 +----
flang-rt/lib/runtime/io-stmt.cpp | 44 ++++++++++++++++-----
4 files changed, 69 insertions(+), 24 deletions(-)
diff --git a/flang-rt/include/flang-rt/runtime/io-stmt.h b/flang-rt/include/flang-rt/runtime/io-stmt.h
index bfd5f69c76337..95b2ee7370981 100644
--- a/flang-rt/include/flang-rt/runtime/io-stmt.h
+++ b/flang-rt/include/flang-rt/runtime/io-stmt.h
@@ -84,6 +84,9 @@ class IoStatementState {
// This design avoids virtual member functions and function pointers,
// which may not have good support in some runtime environments.
+ RT_API_ATTRS const NonTbpDefinedIoTable *nonTbpDefinedIoTable() const;
+ RT_API_ATTRS void set_nonTbpDefinedIoTable(const NonTbpDefinedIoTable *);
+
// CompleteOperation() is the last opportunity to raise an I/O error.
// It is called by EndIoStatement(), but it can be invoked earlier to
// catch errors for (e.g.) GetIoMsg() and GetNewUnit(). If called
@@ -363,6 +366,13 @@ class IoStatementBase : public IoErrorHandler {
using IoErrorHandler::IoErrorHandler;
RT_API_ATTRS bool completedOperation() const { return completedOperation_; }
+ RT_API_ATTRS const NonTbpDefinedIoTable *nonTbpDefinedIoTable() const {
+ return nonTbpDefinedIoTable_;
+ }
+ RT_API_ATTRS void set_nonTbpDefinedIoTable(
+ const NonTbpDefinedIoTable *table) {
+ nonTbpDefinedIoTable_ = table;
+ }
RT_API_ATTRS void CompleteOperation() { completedOperation_ = true; }
RT_API_ATTRS int EndIoStatement() { return GetIoStat(); }
@@ -397,6 +407,11 @@ class IoStatementBase : public IoErrorHandler {
protected:
bool completedOperation_{false};
+
+private:
+ // Original NonTbpDefinedIoTable argument to Input/OutputDerivedType,
+ // saved here so that it can also be used in child I/O statements.
+ const NonTbpDefinedIoTable *nonTbpDefinedIoTable_{nullptr};
};
// Common state for list-directed & NAMELIST I/O, both internal & external
@@ -630,8 +645,10 @@ class ChildIoStatementState : public IoStatementBase,
public:
RT_API_ATTRS ChildIoStatementState(
ChildIo &, const char *sourceFile = nullptr, int sourceLine = 0);
+ RT_API_ATTRS const NonTbpDefinedIoTable *nonTbpDefinedIoTable() const;
+ RT_API_ATTRS void set_nonTbpDefinedIoTable(const NonTbpDefinedIoTable *);
RT_API_ATTRS ChildIo &child() { return child_; }
- RT_API_ATTRS MutableModes &mutableModes();
+ RT_API_ATTRS MutableModes &mutableModes() { return mutableModes_; }
RT_API_ATTRS ConnectionState &GetConnectionState();
RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const;
RT_API_ATTRS int EndIoStatement();
@@ -644,6 +661,7 @@ class ChildIoStatementState : public IoStatementBase,
private:
ChildIo &child_;
+ MutableModes mutableModes_;
};
template <Direction DIR, typename CHAR>
@@ -654,7 +672,6 @@ class ChildFormattedIoStatementState : public ChildIoStatementState<DIR>,
RT_API_ATTRS ChildFormattedIoStatementState(ChildIo &, const CharType *format,
std::size_t formatLength, const Descriptor *formatDescriptor = nullptr,
const char *sourceFile = nullptr, int sourceLine = 0);
- RT_API_ATTRS MutableModes &mutableModes() { return mutableModes_; }
RT_API_ATTRS void CompleteOperation();
RT_API_ATTRS int EndIoStatement();
RT_API_ATTRS bool AdvanceRecord(int = 1);
@@ -664,7 +681,6 @@ class ChildFormattedIoStatementState : public ChildIoStatementState<DIR>,
}
private:
- MutableModes mutableModes_;
FormatControl<ChildFormattedIoStatementState> format_;
};
@@ -840,7 +856,7 @@ class InquireUnconnectedFileState : public NoUnitIoStatementState {
};
class InquireIOLengthState : public NoUnitIoStatementState,
- public OutputStatementState {
+ public IoDirectionState<Direction::Output> {
public:
RT_API_ATTRS InquireIOLengthState(
const char *sourceFile = nullptr, int sourceLine = 0);
diff --git a/flang-rt/lib/runtime/descriptor-io.cpp b/flang-rt/lib/runtime/descriptor-io.cpp
index 3868c8ddce19f..47273f9720016 100644
--- a/flang-rt/lib/runtime/descriptor-io.cpp
+++ b/flang-rt/lib/runtime/descriptor-io.cpp
@@ -48,10 +48,10 @@ static RT_API_ATTRS Fortran::common::optional<bool> DefinedFormattedIo(
const typeInfo::SpecialBinding &special,
const SubscriptValue subscripts[]) {
Fortran::common::optional<DataEdit> peek{
- io.GetNextDataEdit(0 /*to peek at it*/)};
+ io.GetNextDataEdit(/*maxRepeat=*/0 /*to just peek at it*/)};
if (peek &&
(peek->descriptor == DataEdit::DefinedDerivedType ||
- peek->descriptor == DataEdit::ListDirected)) {
+ peek->IsListDirected())) {
// Defined formatting
IoErrorHandler &handler{io.GetIoErrorHandler()};
DataEdit edit{*io.GetNextDataEdit(1)}; // now consume it; no repeats
@@ -836,13 +836,23 @@ template RT_API_ATTRS int DescriptorIoTicket<Direction::Input>::Continue(
template <Direction DIR>
RT_API_ATTRS bool DescriptorIO(IoStatementState &io,
- const Descriptor &descriptor, const NonTbpDefinedIoTable *table) {
+ const Descriptor &descriptor, const NonTbpDefinedIoTable *originalTable) {
bool anyIoTookPlace{false};
+ const NonTbpDefinedIoTable *defaultTable{io.nonTbpDefinedIoTable()};
+ const NonTbpDefinedIoTable *table{originalTable};
+ if (!table) {
+ table = defaultTable;
+ } else if (table != defaultTable) {
+ io.set_nonTbpDefinedIoTable(table); // for nested I/O
+ }
WorkQueue workQueue{io.GetIoErrorHandler()};
if (workQueue.BeginDescriptorIo<DIR>(io, descriptor, table, anyIoTookPlace) ==
StatContinue) {
workQueue.Run();
}
+ if (defaultTable != table) {
+ io.set_nonTbpDefinedIoTable(defaultTable);
+ }
return anyIoTookPlace;
}
diff --git a/flang-rt/lib/runtime/edit-input.cpp b/flang-rt/lib/runtime/edit-input.cpp
index 13557678f6057..85fc812ceba87 100644
--- a/flang-rt/lib/runtime/edit-input.cpp
+++ b/flang-rt/lib/runtime/edit-input.cpp
@@ -37,9 +37,7 @@ static RT_API_ATTRS bool CheckCompleteListDirectedField(
if (edit.IsListDirected()) {
std::size_t byteCount;
if (auto ch{io.GetCurrentChar(byteCount)}) {
- if (IsCharValueSeparator(edit, *ch)) {
- return true;
- } else {
+ if (!IsCharValueSeparator(edit, *ch)) {
const auto &connection{io.GetConnectionState()};
io.GetIoErrorHandler().SignalError(IostatBadListDirectedInputSeparator,
"invalid character (0x%x) after list-directed input value, "
@@ -49,12 +47,9 @@ static RT_API_ATTRS bool CheckCompleteListDirectedField(
static_cast<int>(connection.currentRecordNumber));
return false;
}
- } else {
- return true; // end of record: ok
}
- } else {
- return true;
}
+ return true;
}
template <int LOG2_BASE>
diff --git a/flang-rt/lib/runtime/io-stmt.cpp b/flang-rt/lib/runtime/io-stmt.cpp
index 8056c8db8525b..066a2c5cb794d 100644
--- a/flang-rt/lib/runtime/io-stmt.cpp
+++ b/flang-rt/lib/runtime/io-stmt.cpp
@@ -526,6 +526,17 @@ Fortran::common::optional<DataEdit> IoStatementState::GetNextDataEdit(int n) {
[&](auto &x) { return x.get().GetNextDataEdit(*this, n); }, u_);
}
+const NonTbpDefinedIoTable *IoStatementState::nonTbpDefinedIoTable() const {
+ return common::visit(
+ [&](auto &x) { return x.get().nonTbpDefinedIoTable(); }, u_);
+}
+
+void IoStatementState::set_nonTbpDefinedIoTable(
+ const NonTbpDefinedIoTable *table) {
+ common::visit(
+ [&](auto &x) { return x.get().set_nonTbpDefinedIoTable(table); }, u_);
+}
+
bool IoStatementState::Emit(
const char *data, std::size_t bytes, std::size_t elementBytes) {
return common::visit(
@@ -633,10 +644,10 @@ IoStatementState::FastAsciiField IoStatementState::GetUpcomingFastAsciiField() {
if (!connection.isUTF8 && connection.internalIoCharKind <= 1) {
const char *p{nullptr};
if (std::size_t bytes{GetNextInputBytes(p)}) {
- return FastAsciiField(connection, p, bytes);
+ return FastAsciiField{connection, p, bytes};
}
}
- return FastAsciiField(connection);
+ return FastAsciiField{connection};
}
Fortran::common::optional<char32_t> IoStatementState::NextInField(
@@ -824,6 +835,7 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
edit.descriptor = DataEdit::ListDirected;
edit.repeat = 1; // may be overridden below
edit.modes = io.mutableModes();
+ bool isLookAhead{maxRepeat == 0};
if (hitSlash_) { // everything after '/' is nullified
edit.descriptor = DataEdit::ListDirectedNullValue;
return edit;
@@ -921,8 +933,10 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
}
}
if (!imaginaryPart_ && ch && *ch == '(') {
- realPart_ = true;
- fastField.connection().HandleRelativePosition(byteCount);
+ if (!isLookAhead) {
+ realPart_ = true;
+ fastField.connection().HandleRelativePosition(byteCount);
+ }
edit.descriptor = DataEdit::ListDirectedRealPart;
}
return edit;
@@ -952,12 +966,24 @@ bool ExternalUnformattedIoStatementState<DIR>::Receive(
template <Direction DIR>
ChildIoStatementState<DIR>::ChildIoStatementState(
ChildIo &child, const char *sourceFile, int sourceLine)
- : IoStatementBase{sourceFile, sourceLine}, child_{child} {}
+ : IoStatementBase{sourceFile, sourceLine}, child_{child},
+ mutableModes_{child.parent().mutableModes()} {}
+
+template <Direction DIR>
+const NonTbpDefinedIoTable *
+ChildIoStatementState<DIR>::nonTbpDefinedIoTable() const {
+#if !defined(RT_DEVICE_AVOID_RECURSION)
+ return child_.parent().nonTbpDefinedIoTable();
+#else
+ ReportUnsupportedChildIo();
+#endif
+}
template <Direction DIR>
-MutableModes &ChildIoStatementState<DIR>::mutableModes() {
+void ChildIoStatementState<DIR>::set_nonTbpDefinedIoTable(
+ const NonTbpDefinedIoTable *table) {
#if !defined(RT_DEVICE_AVOID_RECURSION)
- return child_.parent().mutableModes();
+ child_.parent().set_nonTbpDefinedIoTable(table);
#else
ReportUnsupportedChildIo();
#endif
@@ -1030,9 +1056,7 @@ ChildFormattedIoStatementState<DIR, CHAR>::ChildFormattedIoStatementState(
ChildIo &child, const CHAR *format, std::size_t formatLength,
const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)
: ChildIoStatementState<DIR>{child, sourceFile, sourceLine},
- mutableModes_{child.parent().mutableModes()}, format_{*this, format,
- formatLength,
- formatDescriptor} {}
+ format_{*this, format, formatLength, formatDescriptor} {}
template <Direction DIR, typename CHAR>
void ChildFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {
More information about the llvm-commits
mailing list