[libc++] Implement P2467R1: Support exclusive mode for fstreams

This patch brings std::ios_base::noreplace from P2467R1 to libc++.
This requires compiling the shared library in C++23 mode since otherwise
fstream::open(...) doesn't know about the new flag.

Differential Revision: https://reviews.llvm.org/D137640
Co-authored-by: Louis Dionne <ldionne.2@gmail.com>
This commit is contained in:
PragmaTwice 2023-09-18 16:32:02 -04:00 committed by Louis Dionne
parent 341ca1ad0c
commit a3f17ba3fe
14 changed files with 416 additions and 12 deletions

View File

@ -332,6 +332,8 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_invoke_r`` ``202106L``
--------------------------------------------------- -----------------
``__cpp_lib_ios_noreplace`` ``202207L``
--------------------------------------------------- -----------------
``__cpp_lib_is_scoped_enum`` ``202011L``
--------------------------------------------------- -----------------
``__cpp_lib_mdspan`` ``202207L``

View File

@ -48,6 +48,7 @@ Implemented Papers
- 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

View File

@ -76,7 +76,7 @@
"`P2446R2 <https://wg21.link/P2446R2>`__","LWG","``views::as_rvalue``","July 2022","|Complete|","16.0","|ranges|"
"`P2460R2 <https://wg21.link/P2460R2>`__","LWG","Relax requirements on ``wchar_t`` to match existing practices","July 2022","",""
"`P2465R3 <https://wg21.link/P2465R3>`__","LWG","Standard Library Modules ``std`` and ``std.compat``","July 2022","",""
"`P2467R1 <https://wg21.link/P2467R1>`__","LWG","Support exclusive mode for ``fstreams``","July 2022","",""
"`P2467R1 <https://wg21.link/P2467R1>`__","LWG","Support exclusive mode for ``fstreams``","July 2022","|Complete|","18.0",""
"`P2474R2 <https://wg21.link/P2474R2>`__","LWG","``views::repeat``","July 2022","|Complete|","17.0","|ranges|"
"`P2494R2 <https://wg21.link/P2494R2>`__","LWG","Relaxing range adaptors to allow for move only types","July 2022","|Complete|","17.0","|ranges|"
"`P2499R0 <https://wg21.link/P2499R0>`__","LWG","``string_view`` range constructor should be ``explicit``","July 2022","|Complete|","16.0","|ranges|"

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -552,6 +552,18 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(
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 @@ basic_filebuf<_CharT, _Traits>::open(const wchar_t* __s, ios_base::openmode __mo
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;

View File

@ -58,6 +58,7 @@ public:
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 @@ public:
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};

View File

@ -121,6 +121,7 @@ __cpp_lib_integral_constant_callable 201304L <type_traits>
__cpp_lib_interpolate 201902L <cmath> <numeric>
__cpp_lib_invoke 201411L <functional>
__cpp_lib_invoke_r 202106L <functional>
__cpp_lib_ios_noreplace 202207L <ios>
__cpp_lib_is_aggregate 201703L <type_traits>
__cpp_lib_is_constant_evaluated 201811L <type_traits>
__cpp_lib_is_final 201402L <type_traits>
@ -447,6 +448,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
// # 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

View File

@ -10,6 +10,8 @@
// basic_filebuf<charT,traits>* open(const char* s, ios_base::openmode mode);
// XFAIL: LIBCXX-AIX-FIXME
#include <fstream>
#include <cassert>
#include "test_macros.h"
@ -52,5 +54,57 @@ int main(int, char**)
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;
}

View File

@ -13,6 +13,8 @@
// explicit basic_fstream(const char* s, ios_base::openmode mode = ios_base::in | ios_base::out);
// XFAIL: LIBCXX-AIX-FIXME
#include <fstream>
#include <cassert>
#include "test_macros.h"
@ -45,5 +47,53 @@ int main(int, char**)
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;
}

View File

@ -13,6 +13,8 @@
// void open(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out);
// XFAIL: LIBCXX-AIX-FIXME
#include <fstream>
#include <cassert>
#include "test_macros.h"
@ -51,5 +53,57 @@ int main(int, char**)
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;
}

View File

@ -13,6 +13,8 @@
// explicit basic_ofstream(const char* s, ios_base::openmode mode = ios_base::out);
// XFAIL: LIBCXX-AIX-FIXME
#include <fstream>
#include <cassert>
#include "test_macros.h"
@ -59,5 +61,53 @@ int main(int, char**)
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;
}

View File

@ -13,6 +13,8 @@
// void open(const char* s, ios_base::openmode mode = ios_base::out);
// XFAIL: LIBCXX-AIX-FIXME
#include <fstream>
#include <cassert>
#include "test_macros.h"
@ -59,5 +61,56 @@ int main(int, char**)
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;
}

View File

@ -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
// <ios>
// Test the feature test macros defined by <ios>
/* Constant Value
__cpp_lib_ios_noreplace 202207L [C++23]
*/
#include <ios>
#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

View File

@ -114,6 +114,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]
@ -602,6 +603,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
@ -1402,6 +1407,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
@ -2331,6 +2340,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
@ -3536,6 +3549,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
@ -4942,6 +4959,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
@ -6645,6 +6669,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

View File

@ -659,6 +659,11 @@ feature_test_macros = [
"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},