[flang] [llvm] [flang][runtime] OPEN(STATUS='NEW') should fail on extant file (PR #180605)

Peter Klausler via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 9 14:48:06 PST 2026


https://github.com/klausler updated https://github.com/llvm/llvm-project/pull/180605

>From 5108dc9161253eed1c4dd33319cd75aec45a0ca3 Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Mon, 9 Feb 2026 11:53:15 -0800
Subject: [PATCH] [flang][runtime] OPEN(STATUS='NEW') should fail on extant
 file

An OPEN(..., STATUS='NEW') statement should fail when the named
file exists, and also should not delete it when the failure is a
recoverable error.
---
 flang-rt/lib/runtime/file.cpp                 |  9 +++++
 flang-rt/lib/runtime/iostat.cpp               |  2 ++
 flang-rt/unittests/Runtime/ExternalIOTest.cpp | 34 +++++++++++++++++++
 flang/include/flang/Runtime/iostat-consts.h   |  1 +
 4 files changed, 46 insertions(+)

diff --git a/flang-rt/lib/runtime/file.cpp b/flang-rt/lib/runtime/file.cpp
index 8255ec8691886..1122f1ee43d88 100644
--- a/flang-rt/lib/runtime/file.cpp
+++ b/flang-rt/lib/runtime/file.cpp
@@ -99,6 +99,9 @@ void OpenFile::Open(OpenStatus status, common::optional<Action> action,
     }
     if (status == OpenStatus::New) {
       flags |= O_EXCL;
+      if (!action) {
+        action = Action::ReadWrite;
+      }
     } else if (status == OpenStatus::Replace) {
       flags |= O_TRUNC;
     }
@@ -131,6 +134,12 @@ void OpenFile::Open(OpenStatus status, common::optional<Action> action,
       }
       fd_ = ::open(path_.get(), flags, 0600);
       if (fd_ < 0) {
+        if (errno == EEXIST && status == OpenStatus::New) {
+          handler.SignalError(IostatOpenNewExtant,
+              "OPEN(STATUS='NEW') on existing file '%s'", path_.get());
+          path_.reset(); // prevent unlink
+          return;
+        }
         handler.SignalErrno();
       }
     }
diff --git a/flang-rt/lib/runtime/iostat.cpp b/flang-rt/lib/runtime/iostat.cpp
index c2577e7caf0e1..202b40d6abdcd 100644
--- a/flang-rt/lib/runtime/iostat.cpp
+++ b/flang-rt/lib/runtime/iostat.cpp
@@ -119,6 +119,8 @@ const char *IostatErrorString(int iostat) {
     return "List-directed input value has trailing unused characters";
   case IostatNonExternalDefinedUnformattedIo:
     return "Defined unformatted I/O without an external unit";
+  case IostatOpenNewExtant:
+    return "OPEN(STATUS='NEW') on existing file";
   default:
     return nullptr;
   }
diff --git a/flang-rt/unittests/Runtime/ExternalIOTest.cpp b/flang-rt/unittests/Runtime/ExternalIOTest.cpp
index 6421194f45141..82c3fc38ef161 100644
--- a/flang-rt/unittests/Runtime/ExternalIOTest.cpp
+++ b/flang-rt/unittests/Runtime/ExternalIOTest.cpp
@@ -951,3 +951,37 @@ TEST(ExternalIOTests, BigUnitNumbers) {
     EXPECT_EQ(std::strncmp(msg, expectedMsg, n), 0);
   }
 }
+
+TEST(ExternalIOTests, OpenNewExtant) {
+  // OPEN(10,STATUS='REPLACE')
+  Cookie io{IONAME(BeginOpenUnit)(10, __FILE__, __LINE__)};
+  ASSERT_TRUE(IONAME(SetFile)(io, "opennewextant", 13))
+      << "SetFile(opennewextant)";
+  ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)";
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement() for OpenUnit(REPLACE)";
+  // CLOSE(10)
+  io = IONAME(BeginClose)(10, __FILE__, __LINE__);
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement() for Close";
+  // OPEN(10,STATUS='NEW') - should fail
+  io = IONAME(BeginOpenUnit)(10, __FILE__, __LINE__);
+  ASSERT_TRUE(IONAME(SetFile)(io, "opennewextant", 13))
+      << "SetFile(opennewextant)";
+  ASSERT_TRUE(IONAME(SetStatus)(io, "NEW", 3)) << "SetStatus(NEW)";
+  IONAME(EnableHandlers)(io, /*hasIoStat=*/true);
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOpenNewExtant)
+      << "EndIoStatement() for OpenUnit(NEW)";
+  // OPEN(10,STATUS='OLD')
+  io = IONAME(BeginOpenUnit)(10, __FILE__, __LINE__);
+  ASSERT_TRUE(IONAME(SetFile)(io, "opennewextant", 15))
+      << "SetFile(opennewextant)";
+  ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement() for OpenUnit(OLD)";
+  // CLOSE(UNIT=10,STATUS='DELETE')
+  io = IONAME(BeginClose)(10, __FILE__, __LINE__);
+  ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement() for CLOSE(DELETE)";
+}
diff --git a/flang/include/flang/Runtime/iostat-consts.h b/flang/include/flang/Runtime/iostat-consts.h
index 47093d971bf77..bc627424eb1c3 100644
--- a/flang/include/flang/Runtime/iostat-consts.h
+++ b/flang/include/flang/Runtime/iostat-consts.h
@@ -85,6 +85,7 @@ enum Iostat {
   IostatBadNewUnit,
   IostatBadListDirectedInputSeparator,
   IostatNonExternalDefinedUnformattedIo,
+  IostatOpenNewExtant,
 };
 
 } // namespace Fortran::runtime::io



More information about the llvm-commits mailing list