[libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support the 'x' (exclusive/noreplace) mode suffix. (PR #206498)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jul 6 02:19:47 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Himadhith

<details>
<summary>Changes</summary>

AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix. 
Return null for all instances of `noreplace` as the check happens non-atomically.
Note that the wording of this mode in C23, Specifically (para 5):

`If the implementation is not capable of performing the check for the existence of the file and the
creation of the file atomically, it shall fail instead of performing a non-atomic check and creation` 
Ref: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf


---
Full diff: https://github.com/llvm/llvm-project/pull/206498.diff


6 Files Affected:

- (modified) libcxx/include/fstream (+18-1) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp (+8-2) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp (+8-2) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp (+8-2) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp (+8-2) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp (+9-2) 


``````````diff
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b5fb65820628a..9975be836304a 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -634,6 +636,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined on AIX by all supported compilers.
+#      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -644,6 +655,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
@@ -736,7 +748,6 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   const wchar_t* __mdstr = __make_mdwstring(__mode);
   if (!__mdstr)
     return nullptr;
-
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1178,6 +1189,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1336,6 +1349,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1500,6 +1515,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..37cfd985d1c11 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -83,7 +81,11 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -102,7 +104,11 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX)
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..2a297ca87903e 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -98,7 +96,11 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -115,7 +117,11 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX)
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..9178788b60354 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,11 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,7 +103,11 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX)
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..31a944236d1d0 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -152,7 +150,11 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -169,7 +171,11 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX)
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..0b77f012b345d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -90,7 +88,11 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -103,12 +105,17 @@ int main(int, char**)
             f.open(tmp.c_str(), mode);
             assert(!f.is_open()); // since it already exists
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX)
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif

``````````

</details>


https://github.com/llvm/llvm-project/pull/206498


More information about the libcxx-commits mailing list