Skip to content

Commit 3fb6845

Browse files
committed
Fix fmtlib#2818: diagnose unformattable arguments in unpacked case
1 parent bd75e24 commit 3fb6845

File tree

2 files changed

+47
-16
lines changed

2 files changed

+47
-16
lines changed

include/fmt/core.h

+21-16
Original file line numberDiff line numberDiff line change
@@ -1260,7 +1260,7 @@ template <typename Context> class value {
12601260
};
12611261

12621262
template <typename Context, typename T>
1263-
FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context>;
1263+
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
12641264

12651265
// To minimize the number of types we need to deal with, long is translated
12661266
// either to int or to long long depending on its size.
@@ -1513,7 +1513,7 @@ template <typename Context> class basic_format_arg {
15131513
detail::type type_;
15141514

15151515
template <typename ContextType, typename T>
1516-
friend FMT_CONSTEXPR auto detail::make_arg(const T& value)
1516+
friend FMT_CONSTEXPR auto detail::make_arg(T&& value)
15171517
-> basic_format_arg<ContextType>;
15181518

15191519
template <typename Visitor, typename Ctx>
@@ -1674,19 +1674,7 @@ constexpr auto encode_types() -> unsigned long long {
16741674
}
16751675

16761676
template <typename Context, typename T>
1677-
FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> {
1678-
basic_format_arg<Context> arg;
1679-
arg.type_ = mapped_type_constant<T, Context>::value;
1680-
arg.value_ = arg_mapper<Context>().map(value);
1681-
return arg;
1682-
}
1683-
1684-
// The type template parameter is there to avoid an ODR violation when using
1685-
// a fallback formatter in one translation unit and an implicit conversion in
1686-
// another (not recommended).
1687-
template <bool IS_PACKED, typename Context, type, typename T,
1688-
FMT_ENABLE_IF(IS_PACKED)>
1689-
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
1677+
FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
16901678
const auto& arg = arg_mapper<Context>().map(std::forward<T>(val));
16911679

16921680
constexpr bool formattable_char =
@@ -1715,9 +1703,26 @@ FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
17151703
return {arg};
17161704
}
17171705

1706+
template <typename Context, typename T>
1707+
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
1708+
basic_format_arg<Context> arg;
1709+
arg.type_ = mapped_type_constant<T, Context>::value;
1710+
arg.value_ = make_value<Context>(value);
1711+
return arg;
1712+
}
1713+
1714+
// The type template parameter is there to avoid an ODR violation when using
1715+
// a fallback formatter in one translation unit and an implicit conversion in
1716+
// another (not recommended).
1717+
template <bool IS_PACKED, typename Context, type, typename T,
1718+
FMT_ENABLE_IF(IS_PACKED)>
1719+
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
1720+
return make_value<Context>(val);
1721+
}
1722+
17181723
template <bool IS_PACKED, typename Context, type, typename T,
17191724
FMT_ENABLE_IF(!IS_PACKED)>
1720-
inline auto make_arg(const T& value) -> basic_format_arg<Context> {
1725+
FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
17211726
return make_arg<Context>(value);
17221727
}
17231728
FMT_END_DETAIL_NAMESPACE

test/compile-error-test/CMakeLists.txt

+26
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ project(compile-error-test CXX)
66
set(fmt_headers "
77
#include <fmt/format.h>
88
#include <fmt/xchar.h>
9+
#include <fmt/ostream.h>
10+
#include <iostream>
911
")
1012

1113
set(error_test_names "")
@@ -154,6 +156,16 @@ expect_compile(format-function-error "
154156
fmt::format(\"{}\", f);
155157
" ERROR)
156158

159+
# Formatting an unformattable argument should always be a compile time error
160+
expect_compile(format-lots-of-arguments-with-unformattable "
161+
struct E {};
162+
fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, E());
163+
" ERROR)
164+
expect_compile(format-lots-of-arguments-with-function "
165+
void (*f)();
166+
fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, f);
167+
" ERROR)
168+
157169
# Make sure that compiler features detected in the header
158170
# match the features detected in CMake.
159171
if (SUPPORTS_USER_DEFINED_LITERALS)
@@ -181,6 +193,20 @@ if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
181193
#error
182194
#endif
183195
" ERROR)
196+
expect_compile(print-string-number-spec-error "
197+
#ifdef FMT_HAS_CONSTEVAL
198+
fmt::print(\"{:d}\", \"I am not a number\");
199+
#else
200+
#error
201+
#endif
202+
" ERROR)
203+
expect_compile(print-stream-string-number-spec-error "
204+
#ifdef FMT_HAS_CONSTEVAL
205+
fmt::print(std::cout, \"{:d}\", \"I am not a number\");
206+
#else
207+
#error
208+
#endif
209+
" ERROR)
184210

185211
# Compile-time argument name check
186212
expect_compile(format-string-name "

0 commit comments

Comments
 (0)