https://gcc.gnu.org/g:aef87975224b0a4b5b103f241fd366b0e3a21360

commit r15-9520-gaef87975224b0a4b5b103f241fd366b0e3a21360
Author: Tomasz Kamiński <tkami...@redhat.com>
Date:   Wed Apr 16 13:39:04 2025 +0200

    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 <jwak...@redhat.com>
    Signed-off-by: Tomasz Kamiński <tkami...@redhat.com>

Diff:
---
 libstdc++-v3/include/std/format                    | 11 ++++++++---
 .../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 b1455977c65f..27253f50ea8d 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 a4f5d9210ddf..00ce9f6dd0cd 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();
 }

Reply via email to