[flang-commits] [flang] 79f6b81 - [flang][runtime] Corrections to formatted child I/O
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Fri Mar 10 10:21:01 PST 2023
Author: Peter Klausler
Date: 2023-03-10T10:09:44-08:00
New Revision: 79f6b812356880f64975998fda72d06d46735ad6
URL: https://github.com/llvm/llvm-project/commit/79f6b812356880f64975998fda72d06d46735ad6
DIFF: https://github.com/llvm/llvm-project/commit/79f6b812356880f64975998fda72d06d46735ad6.diff
LOG: [flang][runtime] Corrections to formatted child I/O
A handful of I/O statements (OPEN, CLOSE, positioning) are not allowed
on units during child I/O; catch violations and report errors.
Also finesse error handling during FORMAT runtime parsing of DT
derived type edit descriptors, and ensure that formatted child
I/O is nonadvancing.
Differential Revision: https://reviews.llvm.org/D145751
Added:
Modified:
flang/include/flang/Runtime/iostat.h
flang/runtime/descriptor-io.cpp
flang/runtime/format-implementation.h
flang/runtime/format.h
flang/runtime/io-api.cpp
flang/runtime/io-error.cpp
flang/runtime/iostat.cpp
flang/runtime/unit.h
flang/unittests/Runtime/Format.cpp
Removed:
################################################################################
diff --git a/flang/include/flang/Runtime/iostat.h b/flang/include/flang/Runtime/iostat.h
index 6d6be7c7b8d57..ddb82ce0ca238 100644
--- a/flang/include/flang/Runtime/iostat.h
+++ b/flang/include/flang/Runtime/iostat.h
@@ -82,6 +82,7 @@ enum Iostat {
IostatBadBackspaceUnit,
IostatBadUnitNumber,
IostatBadFlushUnit,
+ IostatBadOpOnChildUnit,
};
const char *IostatErrorString(int);
diff --git a/flang/runtime/descriptor-io.cpp b/flang/runtime/descriptor-io.cpp
index c6b57bf5f088d..4a17a53b495a5 100644
--- a/flang/runtime/descriptor-io.cpp
+++ b/flang/runtime/descriptor-io.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "descriptor-io.h"
+#include "flang/Common/restorer.h"
namespace Fortran::runtime::io::descr {
@@ -47,6 +48,8 @@ std::optional<bool> DefinedFormattedIo(IoStatementState &io,
external = &ExternalFileUnit::NewUnit(handler, true);
}
ChildIo &child{external->PushChildIo(io)};
+ // Child formatted I/O is nonadvancing by definition (F'2018 12.6.2.4).
+ auto restorer{common::ScopedSet(io.mutableModes().nonAdvancing, true)};
int unit{external->unitNumber()};
int ioStat{IostatOk};
char ioMsg[100];
diff --git a/flang/runtime/format-implementation.h b/flang/runtime/format-implementation.h
index 630fbf68082e4..89c700eaa4b2d 100644
--- a/flang/runtime/format-implementation.h
+++ b/flang/runtime/format-implementation.h
@@ -64,11 +64,14 @@ FormatControl<CONTEXT>::FormatControl(const Terminator &terminator,
template <typename CONTEXT>
int FormatControl<CONTEXT>::GetIntField(
- IoErrorHandler &handler, CharType firstCh) {
+ IoErrorHandler &handler, CharType firstCh, bool *hadError) {
CharType ch{firstCh ? firstCh : PeekNext()};
if (ch != '-' && ch != '+' && (ch < '0' || ch > '9')) {
handler.SignalError(IostatErrorInFormat,
"Invalid FORMAT: integer expected at '%c'", static_cast<char>(ch));
+ if (hadError) {
+ *hadError = true;
+ }
return 0;
}
int result{0};
@@ -86,6 +89,9 @@ int FormatControl<CONTEXT>::GetIntField(
std::numeric_limits<int>::max() / 10 - (static_cast<int>(ch) - '0')) {
handler.SignalError(
IostatErrorInFormat, "FORMAT integer field out of range");
+ if (hadError) {
+ *hadError = true;
+ }
return result;
}
result = 10 * result + ch - '0';
@@ -99,6 +105,9 @@ int FormatControl<CONTEXT>::GetIntField(
if (negate && (result *= -1) > 0) {
handler.SignalError(
IostatErrorInFormat, "FORMAT integer field out of range");
+ if (hadError) {
+ *hadError = true;
+ }
}
return result;
}
@@ -401,7 +410,7 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
// Returns the next data edit descriptor
template <typename CONTEXT>
-DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
+std::optional<DataEdit> FormatControl<CONTEXT>::GetNextDataEdit(
Context &context, int maxRepeat) {
int repeat{CueUpNextDataEdit(context)};
auto start{offset_};
@@ -420,7 +429,7 @@ DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
if (auto quote{static_cast<char>(PeekNext())};
quote == '\'' || quote == '"') {
// Capture the quoted 'iotype'
- bool ok{false}, tooLong{false};
+ bool ok{false};
for (++offset_; offset_ < formatLength_;) {
auto ch{static_cast<char>(format_[offset_++])};
if (ch == quote &&
@@ -428,31 +437,36 @@ DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
static_cast<char>(format_[offset_]) != quote)) {
ok = true;
break; // that was terminating quote
- } else if (edit.ioTypeChars >= edit.maxIoTypeChars) {
- tooLong = true;
- } else {
- edit.ioType[edit.ioTypeChars++] = ch;
- if (ch == quote) {
- ++offset_;
- }
+ }
+ if (edit.ioTypeChars >= edit.maxIoTypeChars) {
+ ReportBadFormat(context, "Excessive DT'iotype' in FORMAT", start);
+ return std::nullopt;
+ }
+ edit.ioType[edit.ioTypeChars++] = ch;
+ if (ch == quote) {
+ ++offset_;
}
}
if (!ok) {
ReportBadFormat(context, "Unclosed DT'iotype' in FORMAT", start);
- } else if (tooLong) {
- ReportBadFormat(context, "Excessive DT'iotype' in FORMAT", start);
+ return std::nullopt;
}
}
if (PeekNext() == '(') {
// Capture the v_list arguments
- bool ok{false}, tooLong{false};
+ bool ok{false};
for (++offset_; offset_ < formatLength_;) {
- int n{GetIntField(context)};
+ bool hadError{false};
+ int n{GetIntField(context, '\0', &hadError)};
+ if (hadError) {
+ ok = false;
+ break;
+ }
if (edit.vListEntries >= edit.maxVListEntries) {
- tooLong = true;
- } else {
- edit.vList[edit.vListEntries++] = n;
+ ReportBadFormat(context, "Excessive DT(v_list) in FORMAT", start);
+ return std::nullopt;
}
+ edit.vList[edit.vListEntries++] = n;
auto ch{static_cast<char>(GetNextChar(context))};
if (ch != ',') {
ok = ch == ')';
@@ -461,8 +475,7 @@ DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
}
if (!ok) {
ReportBadFormat(context, "Unclosed DT(v_list) in FORMAT", start);
- } else if (tooLong) {
- ReportBadFormat(context, "Excessive DT(v_list) in FORMAT", start);
+ return std::nullopt;
}
}
}
diff --git a/flang/runtime/format.h b/flang/runtime/format.h
index 718a2677c14c9..9077a849eaec6 100644
--- a/flang/runtime/format.h
+++ b/flang/runtime/format.h
@@ -102,7 +102,7 @@ template <typename CONTEXT> class FormatControl {
// Extracts the next data edit descriptor, handling control edit descriptors
// along the way. If maxRepeat==0, this is a peek at the next data edit
// descriptor.
- DataEdit GetNextDataEdit(Context &, int maxRepeat = 1);
+ std::optional<DataEdit> GetNextDataEdit(Context &, int maxRepeat = 1);
// Emit any remaining character literals after the last data item (on output)
// and perform remaining record positioning actions.
@@ -142,7 +142,8 @@ template <typename CONTEXT> class FormatControl {
}
return format_[offset_++];
}
- int GetIntField(IoErrorHandler &, CharType firstCh = '\0');
+ int GetIntField(
+ IoErrorHandler &, CharType firstCh = '\0', bool *hadError = nullptr);
// Advances through the FORMAT until the next data edit
// descriptor has been found; handles control edit descriptors
diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp
index 197cfcc2f7f3d..faf062cd8f596 100644
--- a/flang/runtime/io-api.cpp
+++ b/flang/runtime/io-api.cpp
@@ -374,8 +374,14 @@ Cookie IONAME(BeginOpenUnit)( // OPEN(without NEWUNIT=)
if (ExternalFileUnit *
unit{ExternalFileUnit::LookUpOrCreate(
unitNumber, terminator, wasExtant)}) {
- return &unit->BeginIoStatement<OpenStatementState>(
- terminator, *unit, wasExtant, sourceFile, sourceLine);
+ if (ChildIo * child{unit->GetChildIo()}) {
+ return &child->BeginIoStatement<ErroneousIoStatementState>(
+ IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
+ sourceLine);
+ } else {
+ return &unit->BeginIoStatement<OpenStatementState>(
+ terminator, *unit, wasExtant, sourceFile, sourceLine);
+ }
} else {
return NoopUnit(terminator, unitNumber, IostatBadUnitNumber);
}
@@ -414,6 +420,13 @@ Cookie IONAME(BeginWaitAll)(
Cookie IONAME(BeginClose)(
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
+ if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
+ if (ChildIo * child{unit->GetChildIo()}) {
+ return &child->BeginIoStatement<ErroneousIoStatementState>(
+ IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
+ sourceLine);
+ }
+ }
if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) {
return &unit->BeginIoStatement<CloseStatementState>(
terminator, *unit, sourceFile, sourceLine);
@@ -427,8 +440,13 @@ Cookie IONAME(BeginFlush)(
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
- return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
- *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
+ if (ChildIo * child{unit->GetChildIo()}) {
+ return &child->BeginIoStatement<ExternalMiscIoStatementState>(
+ *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
+ } else {
+ return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
+ *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
+ }
} else {
// FLUSH(UNIT=bad unit) is an error; an unconnected unit is a no-op
return NoopUnit(terminator, unitNumber,
@@ -440,8 +458,15 @@ Cookie IONAME(BeginBackspace)(
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
- return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
- *unit, ExternalMiscIoStatementState::Backspace, sourceFile, sourceLine);
+ if (ChildIo * child{unit->GetChildIo()}) {
+ return &child->BeginIoStatement<ErroneousIoStatementState>(
+ IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
+ sourceLine);
+ } else {
+ return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
+ *unit, ExternalMiscIoStatementState::Backspace, sourceFile,
+ sourceLine);
+ }
} else {
return NoopUnit(terminator, unitNumber, IostatBadBackspaceUnit);
}
@@ -454,8 +479,14 @@ Cookie IONAME(BeginEndfile)(
if (ExternalFileUnit *
unit{GetOrCreateUnit(unitNumber, Direction::Output, std::nullopt,
terminator, errorCookie)}) {
- return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
- *unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
+ if (ChildIo * child{unit->GetChildIo()}) {
+ return &child->BeginIoStatement<ErroneousIoStatementState>(
+ IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
+ sourceLine);
+ } else {
+ return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
+ *unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
+ }
} else {
return errorCookie;
}
@@ -468,8 +499,14 @@ Cookie IONAME(BeginRewind)(
if (ExternalFileUnit *
unit{GetOrCreateUnit(unitNumber, Direction::Input, std::nullopt,
terminator, errorCookie)}) {
- return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
- *unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
+ if (ChildIo * child{unit->GetChildIo()}) {
+ return &child->BeginIoStatement<ErroneousIoStatementState>(
+ IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
+ sourceLine);
+ } else {
+ return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
+ *unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
+ }
} else {
return errorCookie;
}
@@ -504,8 +541,13 @@ Cookie IONAME(BeginInquireFile)(const char *path, std::size_t pathLength,
unit{ExternalFileUnit::LookUp(
trimmed.get(), std::strlen(trimmed.get()))}) {
// INQUIRE(FILE=) to a connected unit
- return &unit->BeginIoStatement<InquireUnitState>(
- terminator, *unit, sourceFile, sourceLine);
+ if (ChildIo * child{unit->GetChildIo()}) {
+ return &child->BeginIoStatement<InquireUnitState>(
+ *unit, sourceFile, sourceLine);
+ } else {
+ return &unit->BeginIoStatement<InquireUnitState>(
+ terminator, *unit, sourceFile, sourceLine);
+ }
} else {
return &New<InquireUnconnectedFileState>{terminator}(
std::move(trimmed), sourceFile, sourceLine)
@@ -566,7 +608,12 @@ bool IONAME(SetAdvance)(
if (nonAdvancing && io.GetConnectionState().access == Access::Direct) {
handler.SignalError("Non-advancing I/O attempted on direct access file");
} else {
- io.mutableModes().nonAdvancing = nonAdvancing;
+ auto *unit{io.GetExternalFileUnit()};
+ if (unit && unit->GetChildIo()) {
+ // ADVANCE= is ignored for child I/O (12.6.4.8.3 p3)
+ } else {
+ io.mutableModes().nonAdvancing = nonAdvancing;
+ }
}
return !handler.InError();
}
@@ -648,7 +695,12 @@ bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) {
IoStatementState &io{*cookie};
IoErrorHandler &handler{io.GetIoErrorHandler()};
if (auto *unit{io.GetExternalFileUnit()}) {
- unit->SetDirectRec(rec, handler);
+ if (unit->GetChildIo()) {
+ handler.SignalError(
+ IostatBadOpOnChildUnit, "REC= specifier on child I/O");
+ } else {
+ unit->SetDirectRec(rec, handler);
+ }
} else if (!io.get_if<ErroneousIoStatementState>()) {
handler.Crash("SetRec() called on internal unit");
}
diff --git a/flang/runtime/io-error.cpp b/flang/runtime/io-error.cpp
index 790c579e1c43c..e333f6d75bc4d 100644
--- a/flang/runtime/io-error.cpp
+++ b/flang/runtime/io-error.cpp
@@ -59,13 +59,12 @@ void IoErrorHandler::SignalError(int iostatOrErrno) {
void IoErrorHandler::Forward(
int ioStatOrErrno, const char *msg, std::size_t length) {
- if (ioStat_ != IostatOk && msg && (flags_ & hasIoMsg)) {
- ioMsg_ = SaveDefaultCharacter(msg, length, *this);
- }
- if (ioStatOrErrno != IostatOk && msg) {
- SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg);
- } else {
- SignalError(ioStatOrErrno);
+ if (ioStatOrErrno != IostatOk) {
+ if (msg) {
+ SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg);
+ } else {
+ SignalError(ioStatOrErrno);
+ }
}
}
diff --git a/flang/runtime/iostat.cpp b/flang/runtime/iostat.cpp
index 747a776aae7cc..f34bde28be9d0 100644
--- a/flang/runtime/iostat.cpp
+++ b/flang/runtime/iostat.cpp
@@ -109,6 +109,8 @@ const char *IostatErrorString(int iostat) {
return "Negative unit number is not allowed";
case IostatBadFlushUnit:
return "FLUSH attempted on a bad or unconnected unit number";
+ case IostatBadOpOnChildUnit:
+ return "Impermissible I/O statement on child I/O unit";
default:
return nullptr;
}
diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h
index c49f479830338..aad896afce513 100644
--- a/flang/runtime/unit.h
+++ b/flang/runtime/unit.h
@@ -203,7 +203,7 @@ class ChildIo {
ChildListIoStatementState<Direction::Input>,
ChildUnformattedIoStatementState<Direction::Output>,
ChildUnformattedIoStatementState<Direction::Input>, InquireUnitState,
- ErroneousIoStatementState>
+ ErroneousIoStatementState, ExternalMiscIoStatementState>
u_;
std::optional<IoStatementState> io_;
};
diff --git a/flang/unittests/Runtime/Format.cpp b/flang/unittests/Runtime/Format.cpp
index 970373043131a..1f55b39d59053 100644
--- a/flang/unittests/Runtime/Format.cpp
+++ b/flang/unittests/Runtime/Format.cpp
@@ -10,6 +10,7 @@
#include "../runtime/connection.h"
#include "../runtime/format-implementation.h"
#include "../runtime/io-error.h"
+#include <optional>
#include <string>
#include <tuple>
#include <vector>
@@ -29,7 +30,7 @@ class TestFormatContext : public IoErrorHandler {
bool AdvanceRecord(int = 1);
void HandleRelativePosition(std::int64_t);
void HandleAbsolutePosition(std::int64_t);
- void Report(const DataEdit &);
+ void Report(const std::optional<DataEdit> &);
ResultsTy results;
MutableModes &mutableModes() { return mutableModes_; }
ConnectionState &GetConnectionState() { return connectionState_; }
@@ -64,25 +65,29 @@ void TestFormatContext::HandleRelativePosition(std::int64_t n) {
}
}
-void TestFormatContext::Report(const DataEdit &edit) {
- std::string str{edit.descriptor};
- if (edit.repeat != 1) {
- str = std::to_string(edit.repeat) + '*' + str;
- }
- if (edit.variation) {
- str += edit.variation;
- }
- if (edit.width) {
- str += std::to_string(*edit.width);
- }
- if (edit.digits) {
- str += "."s + std::to_string(*edit.digits);
- }
- if (edit.expoDigits) {
- str += "E"s + std::to_string(*edit.expoDigits);
+void TestFormatContext::Report(const std::optional<DataEdit> &edit) {
+ if (edit) {
+ std::string str{edit->descriptor};
+ if (edit->repeat != 1) {
+ str = std::to_string(edit->repeat) + '*' + str;
+ }
+ if (edit->variation) {
+ str += edit->variation;
+ }
+ if (edit->width) {
+ str += std::to_string(*edit->width);
+ }
+ if (edit->digits) {
+ str += "."s + std::to_string(*edit->digits);
+ }
+ if (edit->expoDigits) {
+ str += "E"s + std::to_string(*edit->expoDigits);
+ }
+ // modes?
+ results.push_back(str);
+ } else {
+ results.push_back("(nullopt)"s);
}
- // modes?
- results.push_back(str);
}
struct FormatTests : public CrashHandlerFixture {};
More information about the flang-commits
mailing list