https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121254

            Bug ID: 121254
           Summary: [modules] Specialization for std::formatter in a
                    module causes std::format to throw error during parse
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: omerfaruko at gmail dot com
  Target Milestone: ---

This was the underlying issue I was trying to debug in Bug 121175.

std::format throws during parsing if a module with a specialized std::formatter
is imported.

Below example demonstrates throwing import version vs working include version.

==> CMakeLists.txt <==
cmake_minimum_required(VERSION 4.0)
project(reprod)

set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

add_library(reprod)
target_sources(reprod
  PUBLIC
    FILE_SET CXX_MODULES
    FILES
      reprod.ccm
)

add_executable(reprod_main main.cc)
target_link_libraries(reprod_main PRIVATE reprod)

==> main.cc <==
#include <format>
#include <iostream>

// throws "format error: invalid width or precision in format-spec"
import reprod;
// prints "1.000" and exits normally
// #include "reprod.h"

int main() {
  std::cout << std::format("{:1.3f}", 1.0) << std::endl;
  return 0;
}

==> reprod.ccm <==
module;

#include <format>

export module reprod;

export struct Reprod {};

// Commenting this specialization out prevents the throw.
template <> struct std::formatter<Reprod> {
  auto parse(std::format_parse_context &ctx) { return ctx.begin(); }

  template <typename FormatContext>
  auto format(Reprod value, FormatContext &ctx) const {
    return std::format_to(ctx.out(), "Reprod");
  }
};

==> reprod.h <==
#pragma once

#include <format>

struct Reprod {};

template <> struct std::formatter<Reprod> {
  auto parse(std::format_parse_context &ctx) { return ctx.begin(); }

  template <typename FormatContext>
  auto format(Reprod value, FormatContext &ctx) const {
    return std::format_to(ctx.out(), "Reprod");
  }
};



Happens with 15.1. Also tested with gcc-16-20250720 snapshot:

$ export CC=/opt/gcc-16-20250720/bin/gcc
$ export CXX=/opt/gcc-16-20250720/bin/g++
$ mkdir build && cd build && cmake .. && ninja --verbose && ./reprod_main
-- The C compiler identification is GNU 16.0.0
-- The CXX compiler identification is GNU 16.0.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /opt/gcc-16-20250720/bin/gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /opt/gcc-16-20250720/bin/g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.3s)
-- Generating done (0.0s)
-- Build files have been written to: /home/omer/src/reprod/build
[1/8] /opt/gcc-16-20250720/bin/g++   -g -std=c++23 -E -x c++
/home/omer/src/reprod/reprod.ccm -MT CMakeFiles/reprod.dir/reprod.ccm.o.ddi -MD
-MF CMakeFiles/reprod.dir/reprod.ccm.o.ddi.d -fmodules-ts
-fdeps-file=CMakeFiles/reprod.dir/reprod.ccm.o.ddi
-fdeps-target=CMakeFiles/reprod.dir/reprod.ccm.o -fdeps-format=p1689r5 -o
CMakeFiles/reprod.dir/reprod.ccm.o.ddi.i
[2/8] /opt/gcc-16-20250720/bin/g++   -g -std=c++23 -E -x c++
/home/omer/src/reprod/main.cc -MT CMakeFiles/reprod_main.dir/main.cc.o.ddi -MD
-MF CMakeFiles/reprod_main.dir/main.cc.o.ddi.d -fmodules-ts
-fdeps-file=CMakeFiles/reprod_main.dir/main.cc.o.ddi
-fdeps-target=CMakeFiles/reprod_main.dir/main.cc.o -fdeps-format=p1689r5 -o
CMakeFiles/reprod_main.dir/main.cc.o.ddi.i
[3/8] /usr/bin/cmake -E cmake_ninja_dyndep
--tdi=CMakeFiles/reprod.dir/CXXDependInfo.json --lang=CXX --modmapfmt=gcc
--dd=CMakeFiles/reprod.dir/CXX.dd @CMakeFiles/reprod.dir/CXX.dd.rsp
[4/8] /usr/bin/cmake -E cmake_ninja_dyndep
--tdi=CMakeFiles/reprod_main.dir/CXXDependInfo.json --lang=CXX --modmapfmt=gcc
--dd=CMakeFiles/reprod_main.dir/CXX.dd @CMakeFiles/reprod_main.dir/CXX.dd.rsp
[5/8] /opt/gcc-16-20250720/bin/g++   -g -std=c++23 -MD -MT
CMakeFiles/reprod.dir/reprod.ccm.o -MF CMakeFiles/reprod.dir/reprod.ccm.o.d
-fmodules-ts -fmodule-mapper=CMakeFiles/reprod.dir/reprod.ccm.o.modmap -MD
-fdeps-format=p1689r5 -x c++ -o CMakeFiles/reprod.dir/reprod.ccm.o -c
/home/omer/src/reprod/reprod.ccm
[6/8] : && /usr/bin/cmake -E rm -f libreprod.a && /usr/bin/ar qc libreprod.a 
CMakeFiles/reprod.dir/reprod.ccm.o && /usr/bin/ranlib libreprod.a && :
[7/8] /opt/gcc-16-20250720/bin/g++   -g -std=c++23 -MD -MT
CMakeFiles/reprod_main.dir/main.cc.o -MF CMakeFiles/reprod_main.dir/main.cc.o.d
-fmodules-ts -fmodule-mapper=CMakeFiles/reprod_main.dir/main.cc.o.modmap -MD
-fdeps-format=p1689r5 -x c++ -o CMakeFiles/reprod_main.dir/main.cc.o -c
/home/omer/src/reprod/main.cc
[8/8] : && /opt/gcc-16-20250720/bin/g++ -g
-Wl,--dependency-file=CMakeFiles/reprod_main.dir/link.d
CMakeFiles/reprod_main.dir/main.cc.o -o reprod_main  libreprod.a && :
terminate called after throwing an instance of 'std::format_error'
  what():  format error: invalid width or precision in format-spec
[1]    132427 IOT instruction (core dumped)  ./reprod_main


$ gdb reprod_main
GNU gdb (GDB) 16.3
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from reprod_main...
(gdb) r
Starting program: /home/omer/src/reprod/build/reprod_main

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.archlinux.org>
Enable debuginfod for this session? (y or [n])
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Function(s) ^std::(move|forward|as_const|(__)?addressof) will be skipped when
stepping.
Function(s) ^std::(shared|unique)_ptr<.*>::(get|operator) will be skipped when
stepping.
Function(s)
^std::(basic_string|vector|array|deque|(forward_)?list|(unordered_|flat_)?(multi)?(map|set)|span)<.*>::(c?r?(begin|end)|front|back|data|size|empty)
will be skipped when stepping.
Function(s) ^std::(basic_string|vector|array|deque|span)<.*>::operator.] will
be skipped when stepping.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
terminate called after throwing an instance of 'std::format_error'
  what():  format error: invalid width or precision in format-spec

Program received signal SIGABRT, Aborted.
0x00007ffff7a7a74c in ?? () from /usr/lib/libc.so.6
(gdb) where
#0  0x00007ffff7a7a74c in ?? () from /usr/lib/libc.so.6
#1  0x00007ffff7a20dc0 in raise () from /usr/lib/libc.so.6
#2  0x00007ffff7a0857a in abort () from /usr/lib/libc.so.6
#3  0x00007ffff7c97bf8 in __gnu_cxx::__verbose_terminate_handler () at
/usr/src/debug/gcc/gcc/libstdc++-v3/libsupc++/vterminate.cc:95
#4  0x00007ffff7cb1c1a in __cxxabiv1::__terminate (handler=<optimized out>) at
/usr/src/debug/gcc/gcc/libstdc++-v3/libsupc++/eh_terminate.cc:48
#5  0x00007ffff7c975db in std::terminate () at
/usr/src/debug/gcc/gcc/libstdc++-v3/libsupc++/eh_terminate.cc:58
#6  0x00007ffff7cb1ed6 in __cxxabiv1::__cxa_throw (obj=<optimized out>,
tinfo=0x421cb8 <typeinfo for std::format_error>, dest=0x404f14
<std::format_error::~format_error()>) at
/usr/src/debug/gcc/gcc/libstdc++-v3/libsupc++/eh_throw.cc:98
#7  0x0000000000404fa4 in std::__throw_format_error (__what=0x41cf38 "format
error: invalid width or precision in format-spec") at
/opt/gcc-16-20250720/include/c++/16.0.0/format:229
#8  0x000000000040b2af in
std::__format::_Spec<char>::_S_parse_width_or_precision (__first=0x41cf74
"1.3f}", __last=0x41cf79 "", __val=@0x7fffffffd514: 0,
__arg_id=@0x7fffffffd4a7: false, __pc=...)
    at /opt/gcc-16-20250720/include/c++/16.0.0/format:658
#9  0x000000000040b1ec in std::__format::_Spec<char>::_M_parse_width
(this=0x7fffffffd514, __first=0x41cf74 "1.3f}", __last=0x41cf79 "", __pc=...)
at /opt/gcc-16-20250720/include/c++/16.0.0/format:697
#10 0x000000000040c5a0 in std::__format::__formatter_fp<char>::parse
(this=0x7fffffffd54c, __pc=...) at
/opt/gcc-16-20250720/include/c++/16.0.0/format:2001
#11 0x000000000040ea36 in std::formatter<double, char>::parse (this=<optimized
out>, __pc=...) at /opt/gcc-16-20250720/include/c++/16.0.0/format:2866
#12 std::__format::_Formatting_scanner<std::__format::_Sink_iter<char>,
char>::_M_format_arg(unsigned
long)::{lambda(auto:1&)#1}::operator()<double>(double&) const
(__closure=0x7fffffffd6e8, __arg=@0x7fffffffd690: 1)
    at /opt/gcc-16-20250720/include/c++/16.0.0/format:4956
#13 0x000000000040f581 in
std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>,
char>
>::_M_visit<std::__format::_Formatting_scanner<std::__format::_Sink_iter<char>,
char>::_M_format_arg(unsigned
long)::{lambda(auto:1&)#1}>(std::__format::_Formatting_scanner<std::__format::_Sink_iter<char>,
char>::_M_format_arg(unsigned long)::{lambda(auto:1&)#1}&&,
std::__format::_Arg_t) (this=0x7fffffffd690, __vis=...,
    __type=std::__format::_Arg_t::_Arg_dbl) at
/opt/gcc-16-20250720/include/c++/16.0.0/format:4368
#14 0x000000000040f79e in
std::__format::__visit_format_arg<std::__format::_Formatting_scanner<std::__format::_Sink_iter<char>,
char>::_M_format_arg(unsigned long)::{lambda(auto:1&)#1},
std::basic_format_context<std::__format::_Sink_iter<char>, char>
>(std::__format::_Formatting_scanner<std::__format::_Sink_iter<char>,
char>::_M_format_arg(unsigned long)::{lambda(auto:1&)#1}&&,
std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>,
char> >) (
    __vis=..., __arg=...) at
/opt/gcc-16-20250720/include/c++/16.0.0/format:4459
#15 0x000000000040f807 in
std::__format::_Formatting_scanner<std::__format::_Sink_iter<char>,
char>::_M_format_arg (this=0x7fffffffd840, __id=0) at
/opt/gcc-16-20250720/include/c++/16.0.0/format:4946
#16 0x000000000040c337 in
std::__format::_Scanner<char>::_M_on_replacement_field (this=0x7fffffffd840) at
/opt/gcc-16-20250720/include/c++/16.0.0/format:4909
#17 0x000000000040bf8c in std::__format::_Scanner<char>::_M_scan
(this=0x7fffffffd840) at /opt/gcc-16-20250720/include/c++/16.0.0/format:4857
#18 0x000000000040deb9 in
std::__format::__do_vformat_to<std::__format::_Sink_iter<char>, char,
std::basic_format_context<std::__format::_Sink_iter<char>, char> > (__out=...,
__fmt="{:1.3f}", __args=std::format_args with 1 argument,
    __loc=0x0) at /opt/gcc-16-20250720/include/c++/16.0.0/format:5114
#19 0x00000000004052e1 in std::vformat_to<std::__format::_Sink_iter<char>
>(std::__format::_Sink_iter<char>, std::basic_string_view<char,
std::char_traits<char> >,
std::basic_format_args<std::basic_format_context<std::__format::_Sink_iter<char>,
char> >) requires output_iterator<std::__format::_Sink_iter<char>, char const&>
(__out=..., __fmt="{:1.3f}", __args=std::format_args with 1 argument) at
/opt/gcc-16-20250720/include/c++/16.0.0/format:5172
#20 std::vformat[abi:cxx11](std::basic_string_view<char, std::char_traits<char>
>,
std::basic_format_args<std::basic_format_context<std::__format::_Sink_iter<char>,
char> >) (__fmt="{:1.3f}", __args=std::format_args with 1 argument)
    at /opt/gcc-16-20250720/include/c++/16.0.0/format:5207
#21 0x000000000040cc30 in std::format<double> (__fmt=...) at
/opt/gcc-16-20250720/include/c++/16.0.0/format:5246
#22 0x00000000004044f4 in main () at /home/omer/src/reprod/main.cc:10



Include version produces correct output "1.000" and exits normally.

Commenting out the std::formatter specialization in reprod.ccm also leads to
the correct result.

Reply via email to