https://gcc.gnu.org/g:341ad729c2c93b9f01854913b0404e4fc8958512
commit r17-1246-g341ad729c2c93b9f01854913b0404e4fc8958512 Author: Tomasz Kamiński <[email protected]> Date: Thu May 28 08:37:33 2026 +0200 libstdc++: Add (generic_)display_string and (generic_)native_encoded_string to filesystem::path Furthermore string and generic_string method as marked as deprecated in C++26 mode. This implements the P2319R5 "Prevent path presentation problem" paper, with the rename of system_encoded_string from LWG4512, "The system_encoded_string() and generic_system_encoded_string() member functions of std::filesystem::path are misnamed". The wording defines the display_string and generic_display_string in terms of call of std::format with format string "{}" and "{:g}" respectively. This patch inlines the effects of above, by either returning native() or generic_string<value_type>() directly (if value_type is char), or string constructed __unicode::_Utf_view of above (when value_type is wchar_t). As we currently assume that char encoding is always UTF-8, the outputs of (generic_)native_encoded string and (generic_)display_string are equivalent. However, the former may be adjusted in future. libstdc++-v3/ChangeLog: * include/bits/fs_path.h (path::string, path::generic_string): Add deprecated attribute in C++26 via _GLIBCXX26_DEPRECATED_SUGGEST. (path::display_string, path::native_encoded_string) (path::generic_display_string, path::generic_native_encoded_string) [__glibcxx_format_path >= 202506L]: Define. * include/bits/version.def (format_path): Bump to 202506. * include/bits/version.h: Regenerate. * testsuite/util/testsuite_fs.h (__gnu_test::compare_paths): Ignored deprecated declarations warning. * testsuite/27_io/filesystem/filesystem_error/cons.cc: Disable -Wdeprecated-declarations warning with pragmas. * testsuite/27_io/filesystem/operations/canonical.cc: Likewise. * testsuite/27_io/filesystem/operations/copy_file.cc: Likewise. * testsuite/27_io/filesystem/path/concat/strings.cc: Likwise. * testsuite/27_io/filesystem/path/itr/traversal.cc: Likewise. * testsuite/27_io/filesystem/operations/temp_directory_path.cc: Likewise. * testsuite/27_io/filesystem/path/decompose/root_directory.cc: Likewise. * testsuite/27_io/filesystem/path/generic/generic_string.cc: Likewise. * testsuite/27_io/print/1.cc: Likewise. * testsuite/27_io/print/2.cc: Likewise. * testsuite/27_io/print/3.cc: Likewise. * testsuite/27_io/filesystem/path/append/source.cc: Add dg-warning for deprecated functions use. * testsuite/27_io/filesystem/path/decompose/stem.cc: Likewise. * testsuite/27_io/filesystem/path/native/string-char8_t.cc: Likewise. * testsuite/27_io/filesystem/path/generic/display_native_string.cc: New test. * testsuite/27_io/filesystem/path/native/display_native_string.cc: New test. Reviewed-by: Jonathan Wakely <[email protected]> Signed-off-by: Tomasz Kamiński <[email protected]> Diff: --- libstdc++-v3/include/bits/fs_path.h | 45 +++++++++++++++ libstdc++-v3/include/bits/version.def | 3 +- libstdc++-v3/include/bits/version.h | 4 +- .../27_io/filesystem/filesystem_error/cons.cc | 2 + .../27_io/filesystem/operations/canonical.cc | 2 + .../27_io/filesystem/operations/copy_file.cc | 2 + .../filesystem/operations/temp_directory_path.cc | 2 + .../27_io/filesystem/path/append/source.cc | 2 +- .../27_io/filesystem/path/concat/strings.cc | 2 + .../filesystem/path/decompose/root_directory.cc | 2 + .../27_io/filesystem/path/decompose/stem.cc | 2 +- .../path/generic/display_native_string.cc | 66 ++++++++++++++++++++++ .../filesystem/path/generic/generic_string.cc | 2 + .../27_io/filesystem/path/itr/traversal.cc | 2 + .../path/native/display_native_string.cc | 66 ++++++++++++++++++++++ .../27_io/filesystem/path/native/string-char8_t.cc | 2 +- libstdc++-v3/testsuite/27_io/print/1.cc | 4 +- libstdc++-v3/testsuite/27_io/print/2.cc | 2 + libstdc++-v3/testsuite/27_io/print/3.cc | 2 + libstdc++-v3/testsuite/util/testsuite_fs.h | 4 +- 20 files changed, 210 insertions(+), 8 deletions(-) diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h index 8d0078000294..2f59f87fe6ed 100644 --- a/libstdc++-v3/include/bits/fs_path.h +++ b/libstdc++-v3/include/bits/fs_path.h @@ -462,7 +462,12 @@ namespace __detail std::basic_string<_CharT, _Traits, _Allocator> string(const _Allocator& __a = _Allocator()) const; + _GLIBCXX26_DEPRECATED_SUGGEST("display_string' or 'native_encoded_string") std::string string() const; +#if __glibcxx_format_path >= 202506L // C++ >= 26 && HOSTED + std::string display_string() const; + std::string native_encoded_string() const; +#endif #if _GLIBCXX_USE_WCHAR_T std::wstring wstring() const; #endif @@ -481,7 +486,12 @@ namespace __detail std::basic_string<_CharT, _Traits, _Allocator> generic_string(const _Allocator& __a = _Allocator()) const; + _GLIBCXX26_DEPRECATED_SUGGEST("generic_display_string' or 'generic_native_encoded_string") std::string generic_string() const; +#if __glibcxx_format_path >= 202506L // C++ >= 26 && HOSTED + std::string generic_display_string() const; + std::string generic_native_encoded_string() const; +#endif #if _GLIBCXX_USE_WCHAR_T std::wstring generic_wstring() const; #endif @@ -1173,6 +1183,23 @@ namespace __detail inline std::string path::string() const { return string<char>(); } +#if __glibcxx_format_path >= 202506L // C++ >= 26 && HOSTED + inline std::string + path::display_string() const + { +#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS + return std::string(from_range, + __unicode::_Utf_view<char, wstring_view>(native())); +#else + return native(); +#endif + } + + inline std::string + path::native_encoded_string() const + { return string<char>(); } +#endif + #if _GLIBCXX_USE_WCHAR_T inline std::wstring path::wstring() const { return string<wchar_t>(); } @@ -1251,6 +1278,24 @@ namespace __detail path::generic_string() const { return generic_string<char>(); } +#if __glibcxx_format_path >= 202506L // C++ >= 26 && HOSTED + inline std::string + path::generic_display_string() const + { +#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS + string_type __str = generic_string<wchar_t>(); + return std::string(from_range, + __unicode::_Utf_view<char, wstring_view>(__str)); +#else + return generic_string<char>(); +#endif + } + + inline std::string + path::generic_native_encoded_string() const + { return generic_string<char>(); } +#endif + #if _GLIBCXX_USE_WCHAR_T inline std::wstring path::generic_wstring() const diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index a94a25bc0e6c..1104e7c3c907 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1615,8 +1615,9 @@ ftms = { ftms = { name = format_path; // 202403 P2845R8 Formatting of std::filesystem::path + // 202506 P2319R5 Prevent path presentation problems values = { - v = 202403; + v = 202506; cxxmin = 26; hosted = yes; }; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 13093fdbfb71..4eff2c99e8f0 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1763,9 +1763,9 @@ #if !defined(__cpp_lib_format_path) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED -# define __glibcxx_format_path 202403L +# define __glibcxx_format_path 202506L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_format_path) -# define __cpp_lib_format_path 202403L +# define __cpp_lib_format_path 202506L # endif # endif #endif /* !defined(__cpp_lib_format_path) */ diff --git a/libstdc++-v3/testsuite/27_io/filesystem/filesystem_error/cons.cc b/libstdc++-v3/testsuite/27_io/filesystem/filesystem_error/cons.cc index 3a4be9fbd3ab..a064bc679be7 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/filesystem_error/cons.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/filesystem_error/cons.cc @@ -21,6 +21,8 @@ #include <filesystem> #include <testsuite_hooks.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + using std::filesystem::filesystem_error; bool contains(std::string_view what_str, std::string_view expected) diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc index a915b291c375..884b6da438a3 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc @@ -22,6 +22,8 @@ #include <testsuite_hooks.h> #include <testsuite_fs.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + namespace fs = std::filesystem; using __gnu_test::compare_paths; diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_file.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_file.cc index 41cd8243502c..7d54243c922b 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_file.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy_file.cc @@ -25,6 +25,8 @@ #include <testsuite_fs.h> #include <testsuite_hooks.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + void test01() { diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/temp_directory_path.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/temp_directory_path.cc index aa4ce752701f..341138549490 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/operations/temp_directory_path.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/temp_directory_path.cc @@ -24,6 +24,8 @@ #include <testsuite_hooks.h> #include <testsuite_fs.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + void clean_env() { diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc index d579c9bb840b..ba7adf6ffdc3 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc @@ -96,7 +96,7 @@ test03() { test(p, q.c_str()); if constexpr (!std::is_same_v<path::value_type, char>) - test(p, q.string().c_str()); + test(p, q.string().c_str()); // { dg-warning "deprecated" "" { target c++26 } } } } diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/concat/strings.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/concat/strings.cc index 3e1e20b48004..7501454e1551 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/concat/strings.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/concat/strings.cc @@ -24,6 +24,8 @@ #include <testsuite_iterators.h> #include <testsuite_fs.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + using std::filesystem::path; using __gnu_test::compare_paths; diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/root_directory.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/root_directory.cc index 4a1bdb091fc5..49433701c428 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/root_directory.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/root_directory.cc @@ -23,6 +23,8 @@ #include <testsuite_hooks.h> #include <testsuite_fs.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + using std::filesystem::path; void diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/stem.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/stem.cc index 0ce9dd7d6d62..2139fde734b1 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/stem.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/stem.cc @@ -33,7 +33,7 @@ test01() path p = "foo.bar.baz.tar"; std::vector<std::string> v; for (; !p.extension().empty(); p = p.stem()) - v.push_back(p.extension().string()); + v.push_back(p.extension().string()); // { dg-warning "deprecated" "" { target c++26 } } VERIFY( v.at(0) == ".tar" ); VERIFY( v.at(1) == ".baz" ); VERIFY( v.at(2) == ".bar" ); diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/display_native_string.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/display_native_string.cc new file mode 100644 index 000000000000..6652713d5804 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/display_native_string.cc @@ -0,0 +1,66 @@ +// { dg-do run { target c++26 } } + +#include <filesystem> +#include <testsuite_fs.h> +#include <testsuite_hooks.h> + +using std::filesystem::path; + +#define WIDEN(S) ::std::__format::_Widen<CharT>(S, L##S) + +template<typename CharT> +void +test_display_and_native() +{ + std::string res; + path p; + auto verify_both = [&res](const path& p, std::string_view expected) + { + res = p.generic_display_string(); + VERIFY( res == expected ); + res = p.generic_native_encoded_string(); + VERIFY( res == expected ); + }; + + verify_both(WIDEN("/usr/include"), "/usr/include"); + verify_both(WIDEN("foo///bar"), "foo/bar"); + for (path p : __gnu_test::test_paths) + verify_both(p, p.generic_string()); // { dg-warning "deprecated" } + + verify_both( + WIDEN("\u0428\u0447\u0443\u0447\u044B\u043D\u0448\u0447\u044B\u043D\u0430"), + "\u0428\u0447\u0443\u0447\u044B\u043D\u0448\u0447\u044B\u043D\u0430" ); + + if constexpr (path::preferred_separator == L'\\') + verify_both(WIDEN("C:\\foo\\bar"), "C:/foo/bar"); +} + +void +test_display_invalid() +{ + std::string res; + auto verify_native_error = [&res](const path& p, std::string_view expected) + { + res = p.generic_display_string(); + VERIFY( res == expected ); + try { p.generic_native_encoded_string(); } + catch (const std::filesystem::filesystem_error&) {} + }; + +#ifdef _GLIBCXX_USE_WCHAR_T + if constexpr (std::is_same_v<path::value_type, wchar_t>) + { + verify_native_error(L"\xd800", "\uFFFD"); + verify_native_error(L"///\xd800", "/\uFFFD"); + } +#endif +} + +int main() +{ + test_display_and_native<char>(); +#ifdef _GLIBCXX_USE_WCHAR_T + test_display_and_native<wchar_t>(); + test_display_invalid(); +#endif +} diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/generic_string.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/generic_string.cc index 77d5766089ed..3dfa866a028f 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/generic_string.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/generic_string.cc @@ -23,6 +23,8 @@ #include <testsuite_fs.h> #include <testsuite_hooks.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + using std::filesystem::path; void diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc index 9eeb829944b5..2e829eb8f822 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc @@ -25,6 +25,8 @@ #include <testsuite_hooks.h> #include <testsuite_fs.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + using std::filesystem::path; void diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/native/display_native_string.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/native/display_native_string.cc new file mode 100644 index 000000000000..149b52b05300 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/native/display_native_string.cc @@ -0,0 +1,66 @@ +// { dg-do run { target c++26 } } + +#include <filesystem> +#include <testsuite_fs.h> +#include <testsuite_hooks.h> + +using std::filesystem::path; + +#define WIDEN(S) ::std::__format::_Widen<CharT>(S, L##S) + +template<typename CharT> +void +test_display_and_native() +{ + std::string res; + path p; + auto verify_both = [&res](const path& p, std::string_view expected) + { + res = p.display_string(); + VERIFY( res == expected ); + res = p.native_encoded_string(); + VERIFY( res == expected ); + }; + + verify_both(WIDEN("/usr/include"), "/usr/include"); + verify_both(WIDEN("foo///bar"), "foo///bar"); + for (path p : __gnu_test::test_paths) + verify_both(p, p.string()); // { dg-warning "deprecated" } + + verify_both( + WIDEN("\u0428\u0447\u0443\u0447\u044B\u043D\u0448\u0447\u044B\u043D\u0430"), + "\u0428\u0447\u0443\u0447\u044B\u043D\u0448\u0447\u044B\u043D\u0430" ); + + if constexpr (path::preferred_separator == L'\\') + verify_both(WIDEN("C:\\foo\\bar"), "C:\\foo\\bar"); +} + +void +test_display_invalid() +{ + std::string res; + auto verify_native_error = [&res](const path& p, std::string_view expected) + { + res = p.display_string(); + VERIFY( res == expected ); + try { p.native_encoded_string(); } + catch (const std::filesystem::filesystem_error&) {} + }; + +#ifdef _GLIBCXX_USE_WCHAR_T + if constexpr (std::is_same_v<path::value_type, wchar_t>) + { + verify_native_error(L"\xd800", "\uFFFD"); + verify_native_error(L"///\xd800", "///\uFFFD"); + } +#endif +} + +int main() +{ + test_display_and_native<char>(); +#ifdef _GLIBCXX_USE_WCHAR_T + test_display_and_native<wchar_t>(); + test_display_invalid(); +#endif +} diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/native/string-char8_t.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/native/string-char8_t.cc index c8f2b3d2c628..996d58e7e592 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/native/string-char8_t.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/native/string-char8_t.cc @@ -48,7 +48,7 @@ test02() auto str = p.string<char>(); VERIFY( str == "abc" ); - VERIFY( str == p.string() ); + VERIFY( str == p.string() ); // { dg-warning "deprecated" "" { target c++26 } } #ifdef _GLIBCXX_USE_WCHAR_T auto strw = p.string<wchar_t>(); diff --git a/libstdc++-v3/testsuite/27_io/print/1.cc b/libstdc++-v3/testsuite/27_io/print/1.cc index 58f1eb163dfa..4fd7f5dc925a 100644 --- a/libstdc++-v3/testsuite/27_io/print/1.cc +++ b/libstdc++-v3/testsuite/27_io/print/1.cc @@ -7,6 +7,8 @@ #include <testsuite_hooks.h> #include <testsuite_fs.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + void test_print_default() { @@ -25,7 +27,7 @@ void test_print_file() { __gnu_test::scoped_file f; - FILE* strm = std::fopen(f.path.string().c_str(), "w"); + FILE* strm = std::fopen(f.path.string().c_str(), "w"); VERIFY( strm ); std::print(strm, "File under '{}' for {}", 'O', "OUT OF FILE"); std::fclose(strm); diff --git a/libstdc++-v3/testsuite/27_io/print/2.cc b/libstdc++-v3/testsuite/27_io/print/2.cc index 8aa7888e7bd4..bf7bcb1d6ee0 100644 --- a/libstdc++-v3/testsuite/27_io/print/2.cc +++ b/libstdc++-v3/testsuite/27_io/print/2.cc @@ -14,6 +14,8 @@ #include <io.h> #endif +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + namespace std { _GLIBCXX_BEGIN_NAMESPACE_VERSION diff --git a/libstdc++-v3/testsuite/27_io/print/3.cc b/libstdc++-v3/testsuite/27_io/print/3.cc index 90c4cfb0d48d..c23d2f10e4ff 100644 --- a/libstdc++-v3/testsuite/27_io/print/3.cc +++ b/libstdc++-v3/testsuite/27_io/print/3.cc @@ -7,6 +7,8 @@ #include <testsuite_hooks.h> #include <testsuite_fs.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + void test_println_blank() { diff --git a/libstdc++-v3/testsuite/util/testsuite_fs.h b/libstdc++-v3/testsuite/util/testsuite_fs.h index 6940ae51aa66..fa099b0986bc 100644 --- a/libstdc++-v3/testsuite/util/testsuite_fs.h +++ b/libstdc++-v3/testsuite/util/testsuite_fs.h @@ -67,6 +67,8 @@ namespace __gnu_test compare_paths(const test_fs::path& p1, const test_fs::path& p2) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" PATH_CHK( p1, p2, native ); PATH_CHK( p1, p2, string ); PATH_CHK( p1, p2, empty ); @@ -90,7 +92,7 @@ namespace __gnu_test throw test_fs::filesystem_error( "!equal(begin1, end1, begin2)", p1, p2, std::make_error_code(std::errc::invalid_argument) ); - +#pragma GCC diagnostic pop } const std::string test_paths[] = {
