diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -330,6 +330,8 @@ --------------------------------------------------- ----------------- ``__cpp_lib_invoke_r`` ``202106L`` --------------------------------------------------- ----------------- + ``__cpp_lib_ios_noreplace`` ``202207L`` + --------------------------------------------------- ----------------- ``__cpp_lib_is_scoped_enum`` ``202011L`` --------------------------------------------------- ----------------- ``__cpp_lib_mdspan`` ``202207L`` diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst --- a/libcxx/docs/ReleaseNotes/18.rst +++ b/libcxx/docs/ReleaseNotes/18.rst @@ -48,6 +48,7 @@ - P2538R1 - ADL-proof ``std::projected`` - P2614R2 - Deprecate ``numeric_limits::has_denorm`` - P0053R7 - C++ Synchronized Buffered Ostream (in the experimental library) +- P2467R1 - Support exclusive mode for fstreams Improvements and New Features diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -76,7 +76,7 @@ "`P2446R2 `__","LWG","``views::as_rvalue``","July 2022","|Complete|","16.0","|ranges|" "`P2460R2 `__","LWG","Relax requirements on ``wchar_t`` to match existing practices","July 2022","","" "`P2465R3 `__","LWG","Standard Library Modules ``std`` and ``std.compat``","July 2022","","" -"`P2467R1 `__","LWG","Support exclusive mode for ``fstreams``","July 2022","","" +"`P2467R1 `__","LWG","Support exclusive mode for ``fstreams``","July 2022","|Complete|","18.0","" "`P2474R2 `__","LWG","``views::repeat``","July 2022","|Complete|","17.0","|ranges|" "`P2494R2 `__","LWG","Relaxing range adaptors to allow for move only types","July 2022","|Complete|","17.0","|ranges|" "`P2499R0 `__","LWG","``string_view`` range constructor should be ``explicit``","July 2022","|Complete|","16.0","|ranges|" diff --git a/libcxx/include/fstream b/libcxx/include/fstream --- a/libcxx/include/fstream +++ b/libcxx/include/fstream @@ -552,6 +552,18 @@ case ios_base::in | ios_base::out | ios_base::app | ios_base::binary: case ios_base::in | ios_base::app | ios_base::binary: return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE; +#if _LIBCPP_STD_VER >= 23 + case ios_base::out | ios_base::noreplace: + case ios_base::out | ios_base::trunc | ios_base::noreplace: + return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE; + case ios_base::in | ios_base::out | ios_base::trunc | ios_base::noreplace: + return "w+x" _LIBCPP_FOPEN_CLOEXEC_MODE; + case ios_base::out | ios_base::binary | ios_base::noreplace: + case ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace: + 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 // _LIBCPP_STD_VER >= 23 default: return nullptr; } @@ -665,6 +677,22 @@ case ios_base::in | ios_base::app | ios_base::binary: __mdstr = L"a+b"; break; +# if _LIBCPP_STD_VER >= 23 + case ios_base::out | ios_base::noreplace: + case ios_base::out | ios_base::trunc | ios_base::noreplace: + __mdstr = L"wx"; + break; + case ios_base::in | ios_base::out | ios_base::trunc | ios_base::noreplace: + __mdstr = L"w+x"; + break; + case ios_base::out | ios_base::binary | ios_base::noreplace: + case ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace: + __mdstr = L"wbx"; + break; + case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace: + __mdstr = L"w+bx"; + break; +# endif // _LIBCPP_STD_VER >= 23 default: __rt = nullptr; break; diff --git a/libcxx/include/ios b/libcxx/include/ios --- a/libcxx/include/ios +++ b/libcxx/include/ios @@ -58,6 +58,7 @@ static constexpr openmode ate; static constexpr openmode binary; static constexpr openmode in; + static constexpr openmode noreplace; // since C++23 static constexpr openmode out; static constexpr openmode trunc; @@ -277,12 +278,15 @@ static const iostate goodbit = 0x0; typedef unsigned int openmode; - static const openmode app = 0x01; - static const openmode ate = 0x02; - static const openmode binary = 0x04; - static const openmode in = 0x08; - static const openmode out = 0x10; - static const openmode trunc = 0x20; + static const openmode app = 0x01; + static const openmode ate = 0x02; + static const openmode binary = 0x04; + static const openmode in = 0x08; + static const openmode out = 0x10; + static const openmode trunc = 0x20; +#if _LIBCPP_STD_VER >= 23 + static const openmode noreplace = 0x40; +#endif enum seekdir {beg, cur, end}; diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -111,6 +111,7 @@ __cpp_lib_interpolate 201902L __cpp_lib_invoke 201411L __cpp_lib_invoke_r 202106L +__cpp_lib_ios_noreplace 202207L __cpp_lib_is_aggregate 201703L __cpp_lib_is_constant_evaluated 201811L __cpp_lib_is_final 201402L @@ -428,6 +429,7 @@ // # define __cpp_lib_formatters 202302L # define __cpp_lib_forward_like 202207L # define __cpp_lib_invoke_r 202106L +# define __cpp_lib_ios_noreplace 202207L # define __cpp_lib_is_scoped_enum 202011L # define __cpp_lib_mdspan 202207L // # define __cpp_lib_move_only_function 202110L 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 --- 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 @@ -52,5 +52,57 @@ std::remove(temp.c_str()); #endif - return 0; +#if TEST_STD_VER >= 23 + // Test all the noreplace flag combinations + { + std::ios_base::openmode modes[] = { + std::ios_base::out | std::ios_base::noreplace, + std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace, + std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace, + std::ios_base::out | std::ios_base::noreplace | std::ios_base::binary, + std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | std::ios_base::binary, + std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | + std::ios_base::binary, + }; + for (auto mode : modes) { + std::string tmp = get_temp_file_name(); // also creates the file + + { + std::filebuf f; + f.open(tmp.c_str(), mode); + assert(!f.is_open()); // since it already exists + } + + { + std::remove(tmp.c_str()); + + std::filebuf f; + f.open(tmp.c_str(), mode); + assert(f.is_open()); // since it doesn't exist + } + } + +# ifndef TEST_HAS_NO_WIDE_CHARACTERS + for (auto mode : modes) { + std::string tmp = get_temp_file_name(); // also creates the file + + { + std::wfilebuf f; + f.open(tmp.c_str(), mode); + assert(!f.is_open()); // since it already exists + } + + { + std::remove(tmp.c_str()); + + std::wfilebuf f; + f.open(tmp.c_str(), mode); + assert(f.is_open()); // since it doesn't exist + } + } +# endif + } +#endif // TEST_STD_VER >= 23 + + return 0; } 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 --- 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 @@ -45,5 +45,53 @@ std::remove(temp.c_str()); #endif - return 0; +#if TEST_STD_VER >= 23 + // Test all the noreplace flag combinations + { + std::ios_base::openmode modes[] = { + std::ios_base::out | std::ios_base::noreplace, + std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace, + std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace, + std::ios_base::out | std::ios_base::noreplace | std::ios_base::binary, + std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | std::ios_base::binary, + std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | + std::ios_base::binary, + }; + for (auto mode : modes) { + std::string tmp = get_temp_file_name(); // also creates the file + + { + std::fstream f(tmp.c_str(), mode); + assert(!f.is_open()); // since it already exists + } + + { + std::remove(tmp.c_str()); + + std::fstream f(tmp.c_str(), mode); + assert(f.is_open()); // since it doesn't exist + } + } + +# ifndef TEST_HAS_NO_WIDE_CHARACTERS + for (auto mode : modes) { + std::string tmp = get_temp_file_name(); // also creates the file + + { + std::wfstream f(tmp.c_str(), mode); + assert(!f.is_open()); // since it already exists + } + + { + std::remove(tmp.c_str()); + + std::wfstream f(tmp.c_str(), mode); + assert(f.is_open()); // since it doesn't exist + } + } +# endif + } +#endif // TEST_STD_VER >= 23 + + return 0; } 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 --- 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 @@ -51,5 +51,57 @@ std::remove(temp.c_str()); #endif - return 0; +#if TEST_STD_VER >= 23 + // Test all the noreplace flag combinations + { + std::ios_base::openmode modes[] = { + std::ios_base::out | std::ios_base::noreplace, + std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace, + std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace, + std::ios_base::out | std::ios_base::noreplace | std::ios_base::binary, + std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | std::ios_base::binary, + std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | + std::ios_base::binary, + }; + for (auto mode : modes) { + std::string tmp = get_temp_file_name(); // also creates the file + + { + std::fstream f; + f.open(tmp.c_str(), mode); + assert(!f.is_open()); // since it already exists + } + + { + std::remove(tmp.c_str()); + + std::fstream f; + f.open(tmp.c_str(), mode); + assert(f.is_open()); // since it doesn't exist + } + } + +# ifndef TEST_HAS_NO_WIDE_CHARACTERS + for (auto mode : modes) { + std::string tmp = get_temp_file_name(); // also creates the file + + { + std::wfstream f; + f.open(tmp.c_str(), mode); + assert(!f.is_open()); // since it already exists + } + + { + std::remove(tmp.c_str()); + + std::wfstream f; + f.open(tmp.c_str(), mode); + assert(f.is_open()); // since it doesn't exist + } + } +# endif + } +#endif // TEST_STD_VER >= 23 + + return 0; } 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 --- 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 @@ -59,5 +59,53 @@ std::remove(temp.c_str()); #endif - return 0; +#if TEST_STD_VER >= 23 + // Test all the noreplace flag combinations + { + std::ios_base::openmode modes[] = { + std::ios_base::out | std::ios_base::noreplace, + std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace, + std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace, + std::ios_base::out | std::ios_base::noreplace | std::ios_base::binary, + std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | std::ios_base::binary, + std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | + std::ios_base::binary, + }; + for (auto mode : modes) { + std::string tmp = get_temp_file_name(); // also creates the file + + { + std::ofstream f(tmp.c_str(), mode); + assert(!f.is_open()); // since it already exists + } + + { + std::remove(tmp.c_str()); + + std::ofstream f(tmp.c_str(), mode); + assert(f.is_open()); // since it doesn't exist + } + } + +# ifndef TEST_HAS_NO_WIDE_CHARACTERS + for (auto mode : modes) { + std::string tmp = get_temp_file_name(); // also creates the file + + { + std::wofstream f(tmp.c_str(), mode); + assert(!f.is_open()); // since it already exists + } + + { + std::remove(tmp.c_str()); + + std::wofstream f(tmp.c_str(), mode); + assert(f.is_open()); // since it doesn't exist + } + } +# endif + } +#endif // TEST_STD_VER >= 23 + + return 0; } 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 --- 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 @@ -59,5 +59,56 @@ std::remove(temp.c_str()); #endif - return 0; +#if TEST_STD_VER >= 23 + // Test all the noreplace flag combinations + { + std::ios_base::openmode modes[] = { + std::ios_base::out | std::ios_base::noreplace, + std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace, + std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace, + std::ios_base::out | std::ios_base::noreplace | std::ios_base::binary, + std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | std::ios_base::binary, + std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | + std::ios_base::binary, + }; + for (auto mode : modes) { + std::string tmp = get_temp_file_name(); // also creates the file + + { + std::ofstream f; + f.open(tmp.c_str(), mode); + assert(!f.is_open()); // since it already exists + } + + { + std::remove(tmp.c_str()); + + std::ofstream f; + f.open(tmp.c_str(), mode); + assert(f.is_open()); // since it doesn't exist + } + } + +# ifndef TEST_HAS_NO_WIDE_CHARACTERS + for (auto mode : modes) { + std::string tmp = get_temp_file_name(); // also creates the file + + { + std::wofstream f; + 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); + assert(f.is_open()); // since it doesn't exist + } + } +# endif + } +#endif // TEST_STD_VER >= 23 + + return 0; } diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ios.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ios.version.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ios.version.compile.pass.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// WARNING: This test was generated by generate_feature_test_macro_components.py +// and should not be edited manually. +// +// clang-format off + +// UNSUPPORTED: no-localization + +// + +// Test the feature test macros defined by + +/* Constant Value + __cpp_lib_ios_noreplace 202207L [C++23] +*/ + +#include +#include "test_macros.h" + +#if TEST_STD_VER < 14 + +# ifdef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should not be defined before c++23" +# endif + +#elif TEST_STD_VER == 14 + +# ifdef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should not be defined before c++23" +# endif + +#elif TEST_STD_VER == 17 + +# ifdef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should not be defined before c++23" +# endif + +#elif TEST_STD_VER == 20 + +# ifdef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should not be defined before c++23" +# endif + +#elif TEST_STD_VER == 23 + +# ifndef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should be defined in c++23" +# endif +# if __cpp_lib_ios_noreplace != 202207L +# error "__cpp_lib_ios_noreplace should have the value 202207L in c++23" +# endif + +#elif TEST_STD_VER > 23 + +# ifndef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should be defined in c++26" +# endif +# if __cpp_lib_ios_noreplace != 202207L +# error "__cpp_lib_ios_noreplace should have the value 202207L in c++26" +# endif + +#endif // TEST_STD_VER > 23 + diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -104,6 +104,7 @@ __cpp_lib_interpolate 201902L [C++20] __cpp_lib_invoke 201411L [C++17] __cpp_lib_invoke_r 202106L [C++23] + __cpp_lib_ios_noreplace 202207L [C++23] __cpp_lib_is_aggregate 201703L [C++17] __cpp_lib_is_constant_evaluated 201811L [C++20] __cpp_lib_is_final 201402L [C++14] @@ -545,6 +546,10 @@ # error "__cpp_lib_invoke_r should not be defined before c++23" # endif +# ifdef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should not be defined before c++23" +# endif + # ifdef __cpp_lib_is_aggregate # error "__cpp_lib_is_aggregate should not be defined before c++17" # endif @@ -1285,6 +1290,10 @@ # error "__cpp_lib_invoke_r should not be defined before c++23" # endif +# ifdef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should not be defined before c++23" +# endif + # ifdef __cpp_lib_is_aggregate # error "__cpp_lib_is_aggregate should not be defined before c++17" # endif @@ -2154,6 +2163,10 @@ # error "__cpp_lib_invoke_r should not be defined before c++23" # endif +# ifdef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should not be defined before c++23" +# endif + # ifndef __cpp_lib_is_aggregate # error "__cpp_lib_is_aggregate should be defined in c++17" # endif @@ -3290,6 +3303,10 @@ # error "__cpp_lib_invoke_r should not be defined before c++23" # endif +# ifdef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should not be defined before c++23" +# endif + # ifndef __cpp_lib_is_aggregate # error "__cpp_lib_is_aggregate should be defined in c++20" # endif @@ -4627,6 +4644,13 @@ # error "__cpp_lib_invoke_r should have the value 202106L in c++23" # endif +# ifndef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should be defined in c++23" +# endif +# if __cpp_lib_ios_noreplace != 202207L +# error "__cpp_lib_ios_noreplace should have the value 202207L in c++23" +# endif + # ifndef __cpp_lib_is_aggregate # error "__cpp_lib_is_aggregate should be defined in c++23" # endif @@ -6162,6 +6186,13 @@ # error "__cpp_lib_invoke_r should have the value 202106L in c++26" # endif +# ifndef __cpp_lib_ios_noreplace +# error "__cpp_lib_ios_noreplace should be defined in c++26" +# endif +# if __cpp_lib_ios_noreplace != 202207L +# error "__cpp_lib_ios_noreplace should have the value 202207L in c++26" +# endif + # ifndef __cpp_lib_is_aggregate # error "__cpp_lib_is_aggregate should be defined in c++26" # endif diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -573,6 +573,11 @@ "values": {"c++23": 202106}, "headers": ["functional"], }, + { + "name": "__cpp_lib_ios_noreplace", + "values": { "c++23": 202207 }, + "headers": ["ios"], + }, { "name": "__cpp_lib_is_aggregate", "values": {"c++17": 201703},