Skip to content

Compilation error when formatting a chrono duration when using fmtlib as a module #4611

@friedkeenan

Description

@friedkeenan

Hi, I'm currently testing whether I'd be able to use fmtlib as a module in my project, and upon trying the following code, I get a hefty compilation error:

import std;
import fmt;

int main() {
    fmt::println("Test: {}", std::chrono::seconds(600));
}

This is my CMakeLists.txt:

cmake_minimum_required(VERSION 4.1)

set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")

project(modules LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
set(CMAKE_CXX_MODULE_STD ON)

set(FMT_MODULE ON)
add_subdirectory(fmt)

add_executable(main
    source/main.cpp
)

target_link_libraries(main fmt::fmt)

This is my GCC version:

gcc (GCC) 15.2.1 20251112

If I try to format say, an int or a std::vector<int>, then everything compiles correctly. However upon trying to format std::chrono::seconds(600), I get a fairly long compilation error:

Error Output
/usr/bin/c++  -I/home/keenan/Documents/test/modules/fmt/include -std=gnu++26 -fmodules-ts -MD -MT CMakeFiles/main.dir/source/main.cpp.o -MF CMakeFiles/main.dir/source/main.cpp.o.d -fmodules-ts -fmodule-mapper=CMakeFiles/main.dir/source/main.cpp.o.modmap -MD -fdeps-format=p1689r5 -x c++ -o CMakeFiles/main.dir/source/main.cpp.o -c /home/keenan/Documents/test/modules/source/main.cpp
In file included from /home/keenan/Documents/test/modules/fmt/include/fmt/format.h:41,
                 from /home/keenan/Documents/test/modules/fmt/include/fmt/args.h:17,
                 from /home/keenan/Documents/test/modules/fmt/src/fmt.cc:119,
of module fmt, imported at /home/keenan/Documents/test/modules/source/main.cpp:2:
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h: In instantiation of ‘fmt::v12::fstring@fmt<T>::fstring(const S&) [with S = fmt::v12::detail::write_floating_seconds@fmt<std::chrono::duration<long unsigned int, std::ratio<1> > >(fmt::v12::memory_buffer@fmt&, std::chrono::duration<long unsigned int, std::ratio<1> >, int)::<lambda()>::FMT_COMPILE_STRING; typename std::enable_if<(std::is_base_of<fmt::v12::detail::compile_string@fmt, S>::value && std::is_same<typename S::char_type, char>::value), int>::type <anonymous> = 0; T = {double, int&}]’:
/home/keenan/Documents/test/modules/fmt/include/fmt/chrono.h:1038:43:   required from ‘void fmt::v12::detail::write_floating_seconds@fmt(fmt::v12::memory_buffer@fmt&, Duration, int) [with Duration = std::chrono::duration<long unsigned int, std::ratio<1> >; fmt::v12::memory_buffer@fmt = fmt::v12::basic_memory_buffer@fmt<char>]’
 4238 |     [] {                                                                     \
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4239 |       /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4240 |       /* Use a macro-like name to avoid shadowing warnings. */               \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4241 |       struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base {            \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4242 |         using char_type = fmt::remove_cvref_t<decltype(s[0])>;               \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4243 |         constexpr explicit operator fmt::basic_string_view<char_type>()      \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4244 |             const {                                                          \
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4245 |           return fmt::detail::compile_string_to_view<char_type>(s);          \
      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4246 |         }                                                                    \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4247 |       };                                                                     \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4248 |       using FMT_STRING_VIEW =                                                \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4249 |           fmt::basic_string_view<typename FMT_COMPILE_STRING::char_type>;    \
      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4250 |       fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING()));     \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4251 |       return FMT_COMPILE_STRING();                                           \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4252 |     }()
      |     ~^~
/home/keenan/Documents/test/modules/fmt/include/fmt/chrono.h:1781:31:   required from ‘void fmt::v12::detail::duration_formatter@fmt<Char, Rep, Period>::on_second(fmt::v12::detail::numeric_system@fmt, fmt::v12::detail::pad_type@fmt) [with Char = char; Rep = long int; Period = std::ratio<1>]’
 1781 |         write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
      |         ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1782 |                                precision);
      |                                ~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/chrono.h:706:32:   required from ‘constexpr const Char* fmt::v12::detail::parse_chrono_format@fmt(const Char*, const Char*, Handler&&) [with Char = char; Handler = duration_formatter@fmt<char, long int, std::ratio<1> >&]’
  706 |     case 'S': handler.on_second(numeric_system::standard, pad); break;
      |               ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/chrono.h:2096:34:   required from ‘decltype (ctx.out()) fmt::v12::formatter@fmt<std::chrono::duration<_Rep1, _Period1>, Char>::format(std::chrono::duration<_Rep1, _Period1>, FormatContext&) const [with FormatContext = fmt::v12::context@fmt; Rep = long int; Period = std::ratio<1>; Char = char; decltype (ctx.out()) = fmt::v12::basic_appender@fmt<char>]’
 2096 |       detail::parse_chrono_format(begin, end, f);
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2322:29:   required from ‘static void fmt::v12::detail::value@fmt<Context>::format_custom(void*, fmt::v12::parse_context@fmt<typename Context::char_type>&, Context&) [with T = std::chrono::duration<long int>; Context = fmt::v12::context@fmt; typename Context::char_type = char]’
 2322 |     ctx.advance_to(cf.format(*static_cast<qualified_type*>(arg), ctx));
      |                    ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2302:19:   required from ‘constexpr fmt::v12::detail::value@fmt<Context>::value(T&, fmt::v12::detail::custom_tag@fmt) [with T = std::chrono::duration<long int>; typename std::enable_if<has_formatter<T, typename Context::char_type>(), int>::type <anonymous> = 0; Context = fmt::v12::context@fmt]’
 2302 |     custom.format = format_custom<value_type>;
      |     ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2282:65:   required from ‘constexpr fmt::v12::detail::value@fmt<Context>::value(T&) [with T = std::chrono::duration<long int>; typename std::enable_if<(std::integral_constant<bool, (((((((std::is_class<T>::value || std::is_enum<T>::value) || std::is_union<T>::value) || std::is_array<_Up>::value) && (! fmt::v12::detail::has_to_string_view@fmt<T, void>::value)) && (! fmt::v12::detail::is_named_arg@fmt<T>::value)) && (! fmt::v12::detail::use_format_as@fmt<T>::value)) && (! fmt::v12::detail::use_format_as_member@fmt<typename std::remove_const<_Tp>::type, std::integral_constant<bool, true> >::value))>::value || (!1)), int>::type <anonymous> = 0; Context = fmt::v12::context@fmt]’
 2282 |   FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {}
      |                                                                 ^
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2989:15:   required from ‘void fmt::v12::println@fmt(FILE*, format_string<T ...>, T&& ...) [with T = {std::chrono::duration<long int, std::ratio<1, 1> >}; FILE = FILE; format_string<T ...> = fstring@fmt<std::chrono::duration<long int, std::ratio<1, 1> > >]’
 2989 |   vargs<T...> va = {{args...}};
      |               ^~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2999:22:   required from ‘void fmt::v12::println@fmt(format_string<T ...>, T&& ...) [with T = {std::chrono::duration<long int, std::ratio<1, 1> >}; format_string<T ...> = fstring@fmt<std::chrono::duration<long int, std::ratio<1, 1> > >]’
 2999 |   return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
      |          ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/source/main.cpp:5:17:   required from here
    5 |     fmt::println("Test: {}", std::chrono::seconds(600));
      |     ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2785:29: error: ‘parse_format_string’ was not declared in this scope
 2785 |         (parse_format_string(sv, checker(sv, arg_pack())), 0);
      |          ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h: In instantiation of ‘fmt::v12::fstring@fmt<T>::fstring(const S&) [with S = fmt::v12::detail::write_floating_seconds@fmt<std::chrono::duration<long int> >(fmt::v12::memory_buffer@fmt&, std::chrono::duration<long int>, int)::<lambda()>::FMT_COMPILE_STRING; typename std::enable_if<(std::is_base_of<fmt::v12::detail::compile_string@fmt, S>::value && std::is_same<typename S::char_type, char>::value), int>::type <anonymous> = 0; T = {double, int&}]’:
/home/keenan/Documents/test/modules/fmt/include/fmt/chrono.h:1038:43:   required from ‘void fmt::v12::detail::write_floating_seconds@fmt(fmt::v12::memory_buffer@fmt&, Duration, int) [with Duration = std::chrono::duration<long int>; fmt::v12::memory_buffer@fmt = fmt::v12::basic_memory_buffer@fmt<char>]’
 4238 |     [] {                                                                     \
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4239 |       /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4240 |       /* Use a macro-like name to avoid shadowing warnings. */               \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4241 |       struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base {            \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4242 |         using char_type = fmt::remove_cvref_t<decltype(s[0])>;               \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4243 |         constexpr explicit operator fmt::basic_string_view<char_type>()      \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4244 |             const {                                                          \
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4245 |           return fmt::detail::compile_string_to_view<char_type>(s);          \
      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4246 |         }                                                                    \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4247 |       };                                                                     \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4248 |       using FMT_STRING_VIEW =                                                \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4249 |           fmt::basic_string_view<typename FMT_COMPILE_STRING::char_type>;    \
      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4250 |       fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING()));     \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4251 |       return FMT_COMPILE_STRING();                                           \
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 4252 |     }()
      |     ~^~
/home/keenan/Documents/test/modules/fmt/include/fmt/chrono.h:1422:33:   required from ‘void fmt::v12::detail::tm_writer@fmt<OutputIt, Char, Duration>::on_second(fmt::v12::detail::numeric_system@fmt, fmt::v12::detail::pad_type@fmt) [with OutputIt = fmt::v12::basic_appender@fmt<char>; Char = char; Duration = std::chrono::duration<long int>]’
 1422 |           write_floating_seconds(buf, *subsecs_);
      |           ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/chrono.h:1796:21:   required from ‘void fmt::v12::detail::duration_formatter@fmt<Char, Rep, Period>::on_second(fmt::v12::detail::numeric_system@fmt, fmt::v12::detail::pad_type@fmt) [with Char = char; Rep = long int; Period = std::ratio<1>]’
 1796 |     format_tm(time, &tm_writer_type::on_second, ns, pad);
      |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/chrono.h:706:32:   required from ‘constexpr const Char* fmt::v12::detail::parse_chrono_format@fmt(const Char*, const Char*, Handler&&) [with Char = char; Handler = duration_formatter@fmt<char, long int, std::ratio<1> >&]’
  706 |     case 'S': handler.on_second(numeric_system::standard, pad); break;
      |               ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/chrono.h:2096:34:   required from ‘decltype (ctx.out()) fmt::v12::formatter@fmt<std::chrono::duration<_Rep1, _Period1>, Char>::format(std::chrono::duration<_Rep1, _Period1>, FormatContext&) const [with FormatContext = fmt::v12::context@fmt; Rep = long int; Period = std::ratio<1>; Char = char; decltype (ctx.out()) = fmt::v12::basic_appender@fmt<char>]’
 2096 |       detail::parse_chrono_format(begin, end, f);
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2322:29:   required from ‘static void fmt::v12::detail::value@fmt<Context>::format_custom(void*, fmt::v12::parse_context@fmt<typename Context::char_type>&, Context&) [with T = std::chrono::duration<long int>; Context = fmt::v12::context@fmt; typename Context::char_type = char]’
 2322 |     ctx.advance_to(cf.format(*static_cast<qualified_type*>(arg), ctx));
      |                    ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2302:19:   required from ‘constexpr fmt::v12::detail::value@fmt<Context>::value(T&, fmt::v12::detail::custom_tag@fmt) [with T = std::chrono::duration<long int>; typename std::enable_if<has_formatter<T, typename Context::char_type>(), int>::type <anonymous> = 0; Context = fmt::v12::context@fmt]’
 2302 |     custom.format = format_custom<value_type>;
      |     ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2282:65:   required from ‘constexpr fmt::v12::detail::value@fmt<Context>::value(T&) [with T = std::chrono::duration<long int>; typename std::enable_if<(std::integral_constant<bool, (((((((std::is_class<T>::value || std::is_enum<T>::value) || std::is_union<T>::value) || std::is_array<_Up>::value) && (! fmt::v12::detail::has_to_string_view@fmt<T, void>::value)) && (! fmt::v12::detail::is_named_arg@fmt<T>::value)) && (! fmt::v12::detail::use_format_as@fmt<T>::value)) && (! fmt::v12::detail::use_format_as_member@fmt<typename std::remove_const<_Tp>::type, std::integral_constant<bool, true> >::value))>::value || (!1)), int>::type <anonymous> = 0; Context = fmt::v12::context@fmt]’
 2282 |   FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {}
      |                                                                 ^
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2989:15:   required from ‘void fmt::v12::println@fmt(FILE*, format_string<T ...>, T&& ...) [with T = {std::chrono::duration<long int, std::ratio<1, 1> >}; FILE = FILE; format_string<T ...> = fstring@fmt<std::chrono::duration<long int, std::ratio<1, 1> > >]’
 2989 |   vargs<T...> va = {{args...}};
      |               ^~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2999:22:   required from ‘void fmt::v12::println@fmt(format_string<T ...>, T&& ...) [with T = {std::chrono::duration<long int, std::ratio<1, 1> >}; format_string<T ...> = fstring@fmt<std::chrono::duration<long int, std::ratio<1, 1> > >]’
 2999 |   return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
      |          ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/source/main.cpp:5:17:   required from here
    5 |     fmt::println("Test: {}", std::chrono::seconds(600));
      |     ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/keenan/Documents/test/modules/fmt/include/fmt/base.h:2785:29: error: ‘parse_format_string’ was not declared in this scope
 2785 |         (parse_format_string(sv, checker(sv, arg_pack())), 0);
      |          ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The error no longer occurs if I remove the use of FMT_STRING here, though I suspect that the underlying issue is that FMT_STRING yields an object of a type defined inside an immediately-invoked lambda. Removing the FMT_VISIBILITY("hidden") did not seem to change anything, though that was just a blind attempt to see if that could be the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions