It's not possible to construct a path::string_type from an allocator of a different type. Create the correct specialization of basic_string, and adjust path::_S_str_convert to use a basic_string_view so that it is independent of the allocator type.
PR libstdc++/94242 * include/bits/fs_path.h (path::_S_str_convert): Replace first parameter with basic_string_view so that strings with different allocators can be accepted. (path::generic_string<C,T,A>()): Use basic_string object that uses the right allocator type. * testsuite/27_io/filesystem/path/generic/94242.cc: New test. * testsuite/27_io/filesystem/path/generic/generic_string.cc: Improve test coverage. Tested x86_64-linux, committed to master. I plan to backport this too.
commit 9fc985118d9f5014afc1caf32a411ee5803fba61 Author: Jonathan Wakely <jwak...@redhat.com> Date: Sat Mar 21 21:51:07 2020 +0000 libstdc++: Fix path::generic_string allocator handling (PR 94242) It's not possible to construct a path::string_type from an allocator of a different type. Create the correct specialization of basic_string, and adjust path::_S_str_convert to use a basic_string_view so that it is independent of the allocator type. PR libstdc++/94242 * include/bits/fs_path.h (path::_S_str_convert): Replace first parameter with basic_string_view so that strings with different allocators can be accepted. (path::generic_string<C,T,A>()): Use basic_string object that uses the right allocator type. * testsuite/27_io/filesystem/path/generic/94242.cc: New test. * testsuite/27_io/filesystem/path/generic/generic_string.cc: Improve test coverage. diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h index bf1f09929c3..fb6e8a5247f 100644 --- a/libstdc++-v3/include/bits/fs_path.h +++ b/libstdc++-v3/include/bits/fs_path.h @@ -579,7 +579,7 @@ namespace __detail template<typename _CharT, typename _Traits, typename _Allocator> static basic_string<_CharT, _Traits, _Allocator> - _S_str_convert(const string_type&, const _Allocator& __a); + _S_str_convert(basic_string_view<value_type>, const _Allocator&); void _M_split_cmpts(); @@ -1015,7 +1015,8 @@ namespace __detail /// @cond undocumented template<typename _CharT, typename _Traits, typename _Allocator> std::basic_string<_CharT, _Traits, _Allocator> - path::_S_str_convert(const string_type& __str, const _Allocator& __a) + path::_S_str_convert(basic_string_view<value_type> __str, + const _Allocator& __a) { static_assert(!is_same_v<_CharT, value_type>); @@ -1126,7 +1127,9 @@ namespace __detail #else const value_type __slash = '/'; #endif - string_type __str(__a); + using _Alloc2 = typename allocator_traits<_Allocator>::template + rebind_alloc<value_type>; + basic_string<value_type, char_traits<value_type>, _Alloc2> __str(__a); if (_M_type() == _Type::_Root_dir) __str.assign(1, __slash); @@ -1145,7 +1148,7 @@ namespace __detail #endif if (__add_slash) __str += __slash; - __str += __elem._M_pathname; + __str += basic_string_view<value_type>(__elem._M_pathname); __add_slash = __elem._M_type() == _Type::_Filename; } } diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/94242.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/94242.cc new file mode 100644 index 00000000000..c917daed94e --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/94242.cc @@ -0,0 +1,52 @@ +// { dg-options "-std=gnu++17" } +// { dg-do run { target c++17 } } + +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// C++17 30.10.7.4.7 path generic format observers [fs.path.generic.obs] + +#include <filesystem> +#include <testsuite_allocator.h> + +using std::filesystem::path; +using __gnu_test::SimpleAllocator; + +void +test01() +{ + path p = "//foo//bar//."; + using C = path::value_type; + auto g = p.generic_string<C, std::char_traits<C>, SimpleAllocator<C>>(); + VERIFY( g == path("/foo/bar/.").c_str() ); +} + +void +test02() +{ + path p = "//foo//bar//."; + using C = char16_t; + auto g = p.generic_string<C, std::char_traits<C>, SimpleAllocator<C>>(); + VERIFY( g == u"/foo/bar/." ); +} + +int +main() +{ + test01(); + test02(); +} 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 5caf079ed9b..8554e9f8f63 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 @@ -21,6 +21,7 @@ // C++17 30.10.7.4.7 path generic format observers [fs.path.generic.obs] #include <filesystem> +#include <testsuite_fs.h> #include <testsuite_hooks.h> using std::filesystem::path; @@ -34,11 +35,15 @@ test01() #ifdef __CYGWIN__ VERIFY( path("//a").generic_string() == "//a" ); VERIFY( path("//a/").generic_string() == "//a/" ); + VERIFY( path("//a//").generic_string() == "//a/" ); VERIFY( path("//a/b").generic_string() == "//a/b" ); + VERIFY( path("//a//b").generic_string() == "//a/b" ); #else VERIFY( path("//a").generic_string() == "/a" ); VERIFY( path("//a/").generic_string() == "/a/" ); + VERIFY( path("//a//").generic_string() == "/a/" ); VERIFY( path("//a/b").generic_string() == "/a/b" ); + VERIFY( path("//a//b").generic_string() == "/a/b" ); #endif VERIFY( path("/a//b").generic_string() == "/a/b" ); VERIFY( path("/a//b/").generic_string() == "/a/b/" ); @@ -57,9 +62,36 @@ test02() } } +void +test03() +{ + for (path p : __gnu_test::test_paths) + { + // A path constructed from the generic format string should compare equal + // to the original, because they represent the same path. + VERIFY( path(p.generic_string()) == p ); + VERIFY( path(p.generic_wstring()) == p ); + VERIFY( path(p.generic_u8string()) == p ); + VERIFY( path(p.generic_u16string()) == p ); + VERIFY( path(p.generic_u32string()) == p ); + } + + for (path p : { "a///b//c", "///a//b//c", "a:b//c", "a://b///c" }) + { + // A path constructed from the generic format string should compare equal + // to the original, because they represent the same path. + VERIFY( path(p.generic_string()) == p ); + VERIFY( path(p.generic_wstring()) == p ); + VERIFY( path(p.generic_u8string()) == p ); + VERIFY( path(p.generic_u16string()) == p ); + VERIFY( path(p.generic_u32string()) == p ); + } +} + int main() { test01(); test02(); + test03(); }