[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