diff options
author | Tomasz Kamiński <tkaminsk@redhat.com> | 2025-04-16 13:39:04 +0200 |
---|---|---|
committer | Tomasz Kamiński <tkaminsk@redhat.com> | 2025-04-16 14:32:57 +0200 |
commit | aef87975224b0a4b5b103f241fd366b0e3a21360 (patch) | |
tree | 62e40c32b04285e548f944f40adf1f63f8ae2c2b | |
parent | fa99720e9f3447565d274baaa81e23c2ddab4a67 (diff) | |
download | gcc-aef87975224b0a4b5b103f241fd366b0e3a21360.zip gcc-aef87975224b0a4b5b103f241fd366b0e3a21360.tar.gz gcc-aef87975224b0a4b5b103f241fd366b0e3a21360.tar.bz2 |
libstdc++: Fix constification in range_formatter::format [PR109162]
The _Rg is deduced to lvalue reference for the lvalue arguments,
and in such case __format::__maybe_const_range<_Rg, _CharT> is always _Rg
(adding const to reference does not change behavior).
Now we correctly check if _Range = remove_reference_t<_Rg> is const
formattable range, furthermore as range_formatter<T> can only format
ranges of values of type (possibly const) _Tp, we additional check if the
remove_cvref_t<range_reference_t<const _Range>> is _Tp.
The range_reference_t<R> and range_reference_t<const R> have different
types (modulo remove_cvref_t) for std::vector<bool> (::reference and bool)
or flat_map<T, U> (pair<const T&, U&> and pair<const T&, const U&>).
PR libstdc++/109162
libstdc++-v3/ChangeLog:
* include/std/format (range_formatter::format): Format const range,
only if reference type is not changed.
* testsuite/std/format/ranges/formatter.cc: New tests.
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
-rw-r--r-- | libstdc++-v3/include/std/format | 11 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/format/ranges/formatter.cc | 22 |
2 files changed, 30 insertions, 3 deletions
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index b145597..27253f5 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -5634,9 +5634,14 @@ namespace __format typename basic_format_context<_Out, _CharT>::iterator format(_Rg&& __rg, basic_format_context<_Out, _CharT>& __fc) const { - using __maybe_const_range - = __format::__maybe_const_range<_Rg, _CharT>; - return _M_format<__maybe_const_range>(__rg, __fc); + using _Range = remove_reference_t<_Rg>; + if constexpr (__format::__const_formattable_range<_Range, _CharT>) + { + using _CRef = ranges::range_reference_t<const _Range>; + if constexpr (same_as<remove_cvref_t<_CRef>, _Tp>) + return _M_format<const _Range>(__rg, __fc); + } + return _M_format(__rg, __fc); } private: diff --git a/libstdc++-v3/testsuite/std/format/ranges/formatter.cc b/libstdc++-v3/testsuite/std/format/ranges/formatter.cc index a4f5d92..00ce9f6 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/formatter.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/formatter.cc @@ -1,5 +1,6 @@ // { dg-do run { target c++23 } } +#include <flat_map> #include <format> #include <testsuite_hooks.h> #include <vector> @@ -138,6 +139,26 @@ test_nested() VERIFY( res == "+<01; 02; 11; 12>+" ); } +struct MyFlatMap : std::flat_map<int, int> +{ + using std::flat_map<int, int>::flat_map; +}; + +template<typename CharT> +struct std::formatter<MyFlatMap, CharT> + // This cannot apply format BitVector const&, because formatted type would + // be std::pair<int const&, int const&>, and formatter for + // pair<int const&, int> cannot format it. + : std::range_formatter<MyFlatMap::reference> +{}; + +void test_const_ref_type_mismatch() +{ + MyFlatMap m{{1, 11}, {2, 22}}; + std::string res = std::format("{:m}", m); + VERIFY( res == "{1: 11, 2: 22}" ); +} + template<typename T, typename CharT> using VectorFormatter = std::formatter<std::vector<T>, CharT>; @@ -146,4 +167,5 @@ int main() test_outputs<std::range_formatter>(); test_outputs<VectorFormatter>(); test_nested(); + test_const_ref_type_mismatch(); } |