[llvm] [Flang] [Runtime ]Fix write endfile abort (PR #191633)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 4 00:17:43 PDT 2026
https://github.com/blazie2004 updated https://github.com/llvm/llvm-project/pull/191633
>From f4ae666b2999bdcb0e428c03d5eda06339210fee Mon Sep 17 00:00:00 2001
From: Jay Satish Kumar Patel <kumarpat at pe31.hpc.amslabs.hpecorp.net>
Date: Thu, 23 Apr 2026 01:19:41 -0500
Subject: [PATCH 1/4] Fix WRITE after ENDFILE handling using pending IOSTAT
---
flang-rt/include/flang-rt/runtime/io-error.h | 4 ++++
flang-rt/lib/runtime/unit.cpp | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/flang-rt/include/flang-rt/runtime/io-error.h b/flang-rt/include/flang-rt/runtime/io-error.h
index d2180a83f8c3c..088fc76a7909a 100644
--- a/flang-rt/include/flang-rt/runtime/io-error.h
+++ b/flang-rt/include/flang-rt/runtime/io-error.h
@@ -34,6 +34,10 @@ class IoErrorHandler : public Terminator {
RT_API_ATTRS void HasEorLabel() { flags_ |= hasEor; }
RT_API_ATTRS void HasIoMsg() { flags_ |= hasIoMsg; }
RT_API_ATTRS void HasRec() { flags_ |= hasRec; }
+ RT_API_ATTRS void SignalPendingIoStat(int iostat) {
+ if (ioStat_ == IostatOk || ioStat_ == IostatEnd || ioStat_ == IostatEor)
+ ioStat_ = iostat;
+ }
RT_API_ATTRS bool InError() const {
return ioStat_ != IostatOk || pendingError_ != IostatOk;
diff --git a/flang-rt/lib/runtime/unit.cpp b/flang-rt/lib/runtime/unit.cpp
index c577ae7673127..5dfc1702234a3 100644
--- a/flang-rt/lib/runtime/unit.cpp
+++ b/flang-rt/lib/runtime/unit.cpp
@@ -84,7 +84,7 @@ bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
beganReadingRecord_ = false;
}
if (IsAfterEndfile()) {
- handler.SignalError(IostatWriteAfterEndfile);
+ handler.SignalPendingIoStat(IostatWriteAfterEndfile);
return false;
}
CheckDirectAccess(handler);
>From f2b7191afa41588e66da0d6f04fc1431f86c6c01 Mon Sep 17 00:00:00 2001
From: Jay Satish Kumar Patel <kumarpat at pe31.hpc.amslabs.hpecorp.net>
Date: Thu, 14 May 2026 01:06:39 -0500
Subject: [PATCH 2/4] [flang-rt] Check IsAfterEndfile() before
BeginIoStatement() for proper error handling
---
flang-rt/include/flang-rt/runtime/io-error.h | 4 ----
flang-rt/lib/runtime/io-api-common.h | 5 +++++
flang-rt/lib/runtime/io-api.cpp | 10 ++++++++++
flang-rt/lib/runtime/unit.cpp | 2 +-
4 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/flang-rt/include/flang-rt/runtime/io-error.h b/flang-rt/include/flang-rt/runtime/io-error.h
index 088fc76a7909a..d2180a83f8c3c 100644
--- a/flang-rt/include/flang-rt/runtime/io-error.h
+++ b/flang-rt/include/flang-rt/runtime/io-error.h
@@ -34,10 +34,6 @@ class IoErrorHandler : public Terminator {
RT_API_ATTRS void HasEorLabel() { flags_ |= hasEor; }
RT_API_ATTRS void HasIoMsg() { flags_ |= hasIoMsg; }
RT_API_ATTRS void HasRec() { flags_ |= hasRec; }
- RT_API_ATTRS void SignalPendingIoStat(int iostat) {
- if (ioStat_ == IostatOk || ioStat_ == IostatEnd || ioStat_ == IostatEor)
- ioStat_ = iostat;
- }
RT_API_ATTRS bool InError() const {
return ioStat_ != IostatOk || pendingError_ != IostatOk;
diff --git a/flang-rt/lib/runtime/io-api-common.h b/flang-rt/lib/runtime/io-api-common.h
index ad6e79d747242..5a268d271ee9f 100644
--- a/flang-rt/lib/runtime/io-api-common.h
+++ b/flang-rt/lib/runtime/io-api-common.h
@@ -83,6 +83,11 @@ RT_API_ATTRS Cookie BeginExternalListIO(
if (iostat == IostatOk) {
iostat = unit->SetDirection(DIR);
}
+ if (iostat == IostatOk) {
+ if (unit->IsAfterEndfile() && DIR == Direction::Output) {
+ iostat = IostatWriteAfterEndfile;
+ }
+ }
if (iostat == IostatOk) {
return &unit->BeginIoStatement<STATE<DIR>>(
terminator, std::forward<A>(xs)..., *unit, sourceFile, sourceLine);
diff --git a/flang-rt/lib/runtime/io-api.cpp b/flang-rt/lib/runtime/io-api.cpp
index aa3ad9254fe0c..b909e5f169f5d 100644
--- a/flang-rt/lib/runtime/io-api.cpp
+++ b/flang-rt/lib/runtime/io-api.cpp
@@ -186,6 +186,11 @@ RT_API_ATTRS Cookie BeginExternalFormattedIO(const char *format,
if (iostat == IostatOk) {
iostat = unit->SetDirection(DIR);
}
+ if (iostat == IostatOk) {
+ if (unit->IsAfterEndfile() && DIR == Direction::Output) {
+ iostat = IostatWriteAfterEndfile;
+ }
+ }
if (iostat == IostatOk) {
return &unit->BeginIoStatement<ExternalFormattedIoStatementState<DIR>>(
terminator, *unit, format, formatLength, formatDescriptor, sourceFile,
@@ -243,6 +248,11 @@ RT_API_ATTRS Cookie BeginUnformattedIO(
if (iostat == IostatOk) {
iostat = unit->SetDirection(DIR);
}
+ if (iostat == IostatOk) {
+ if (unit->IsAfterEndfile() && DIR == Direction::Output) {
+ iostat = IostatWriteAfterEndfile;
+ }
+ }
if (iostat == IostatOk) {
IoStatementState &io{
unit->BeginIoStatement<ExternalUnformattedIoStatementState<DIR>>(
diff --git a/flang-rt/lib/runtime/unit.cpp b/flang-rt/lib/runtime/unit.cpp
index 5dfc1702234a3..c577ae7673127 100644
--- a/flang-rt/lib/runtime/unit.cpp
+++ b/flang-rt/lib/runtime/unit.cpp
@@ -84,7 +84,7 @@ bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
beganReadingRecord_ = false;
}
if (IsAfterEndfile()) {
- handler.SignalPendingIoStat(IostatWriteAfterEndfile);
+ handler.SignalError(IostatWriteAfterEndfile);
return false;
}
CheckDirectAccess(handler);
>From 12c87007489e4b4a44e037ff51f8323cfa38bbcb Mon Sep 17 00:00:00 2001
From: Jay Satish Kumar Patel <kumarpat at pe31.hpc.amslabs.hpecorp.net>
Date: Thu, 21 May 2026 04:11:57 -0500
Subject: [PATCH 3/4] Removed unnecessary checks
---
flang-rt/lib/runtime/io-api-common.h | 5 -----
flang-rt/lib/runtime/io-api.cpp | 5 -----
2 files changed, 10 deletions(-)
diff --git a/flang-rt/lib/runtime/io-api-common.h b/flang-rt/lib/runtime/io-api-common.h
index 5a268d271ee9f..ad6e79d747242 100644
--- a/flang-rt/lib/runtime/io-api-common.h
+++ b/flang-rt/lib/runtime/io-api-common.h
@@ -83,11 +83,6 @@ RT_API_ATTRS Cookie BeginExternalListIO(
if (iostat == IostatOk) {
iostat = unit->SetDirection(DIR);
}
- if (iostat == IostatOk) {
- if (unit->IsAfterEndfile() && DIR == Direction::Output) {
- iostat = IostatWriteAfterEndfile;
- }
- }
if (iostat == IostatOk) {
return &unit->BeginIoStatement<STATE<DIR>>(
terminator, std::forward<A>(xs)..., *unit, sourceFile, sourceLine);
diff --git a/flang-rt/lib/runtime/io-api.cpp b/flang-rt/lib/runtime/io-api.cpp
index b909e5f169f5d..2c97a39ea2555 100644
--- a/flang-rt/lib/runtime/io-api.cpp
+++ b/flang-rt/lib/runtime/io-api.cpp
@@ -186,11 +186,6 @@ RT_API_ATTRS Cookie BeginExternalFormattedIO(const char *format,
if (iostat == IostatOk) {
iostat = unit->SetDirection(DIR);
}
- if (iostat == IostatOk) {
- if (unit->IsAfterEndfile() && DIR == Direction::Output) {
- iostat = IostatWriteAfterEndfile;
- }
- }
if (iostat == IostatOk) {
return &unit->BeginIoStatement<ExternalFormattedIoStatementState<DIR>>(
terminator, *unit, format, formatLength, formatDescriptor, sourceFile,
>From a85da6e10dd9118e16257d8132ad4b0092d6e8b4 Mon Sep 17 00:00:00 2001
From: Jay Satish Kumar Patel <kumarpat at pe31.hpc.amslabs.hpecorp.net>
Date: Thu, 4 Jun 2026 02:16:57 -0500
Subject: [PATCH 4/4] added test for unformatted write after endfile with
iostat
---
flang-rt/unittests/Runtime/ExternalIOTest.cpp | 54 +++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/flang-rt/unittests/Runtime/ExternalIOTest.cpp b/flang-rt/unittests/Runtime/ExternalIOTest.cpp
index 5f9987e817276..1414dede7a2d1 100644
--- a/flang-rt/unittests/Runtime/ExternalIOTest.cpp
+++ b/flang-rt/unittests/Runtime/ExternalIOTest.cpp
@@ -747,6 +747,60 @@ TEST(ExternalIOTests, TestWriteAfterEndfile) {
<< "EndIoStatement() for Close";
}
+TEST(ExternalIOTests, TestUnformattedWriteAfterEndfile) {
+ // Test that unformatted write after ENDFILE with IOSTAT= returns
+ // IostatWriteAfterEndfile instead of crashing. This specifically tests
+ // the fix in BeginUnformattedIO() that checks IsAfterEndfile() before
+ // calling io.Emit().
+ //
+ // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
+ // FORM='UNFORMATTED',STATUS='SCRATCH')
+ auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
+ ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
+ << "SetAccess(SEQUENTIAL)";
+ ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
+ ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
+ ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
+ int unit{-1};
+ ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement() for OpenNewUnit";
+
+ // Set up descriptor for I/O
+ StaticDescriptor<0> staticDescriptor;
+ Descriptor &desc{staticDescriptor.descriptor()};
+ std::int64_t buffer{1234};
+ static constexpr std::size_t recl{sizeof buffer};
+ desc.Establish(TypeCode{CFI_type_int64_t}, recl, &buffer, 0);
+ desc.Check();
+
+ // WRITE(unit) buffer
+ io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
+ ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)) << "OutputDescriptor()";
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement for WRITE before ENDFILE";
+
+ // ENDFILE(unit)
+ io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__);
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement for ENDFILE";
+
+ // WRITE(unit,IOSTAT=iostat) buffer - should return error, not crash
+ buffer = 5678;
+ io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
+ IONAME(EnableHandlers)(io, true /*IOSTAT=*/);
+ // The write may or may not succeed at OutputDescriptor depending on
+ // when the error is detected, but EndIoStatement must return the error
+ IONAME(OutputDescriptor)(io, desc);
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile)
+ << "EndIoStatement for unformatted WRITE after ENDFILE";
+
+ // CLOSE(UNIT=unit)
+ io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement() for Close";
+}
+
TEST(ExternalIOTests, TestUTF8Encoding) {
// OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
// FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
More information about the llvm-commits
mailing list