Skip to content

Commit e5f398f

Browse files
committed
Fixes breakage with odd format functions.
1 parent da08841 commit e5f398f

File tree

3 files changed

+268
-29
lines changed

3 files changed

+268
-29
lines changed

libcxx/include/__format/format_arg_store.h

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -227,16 +227,6 @@ template <class _Context, class _Tp>
227227
{ __f.parse(__pc) } -> same_as<typename decltype(__pc)::iterator>;
228228
};
229229

230-
constexpr bool __has_format_function = requires(_Formatter& __f, _Tp&& __t, _Context __fc) {
231-
{ __f.format(__t, __fc) };
232-
};
233-
constexpr bool __is_format_function_const_qualified = requires(const _Formatter& __cf, _Tp&& __t, _Context __fc) {
234-
{ __cf.format(__t, __fc) };
235-
};
236-
constexpr bool __correct_format_function_return_type = requires(_Formatter& __f, _Tp&& __t, _Context __fc) {
237-
{ __f.format(__t, __fc)->same_as<typename _Context::iterator> };
238-
};
239-
240230
// The reason these static_asserts are placed in an if-constexpr-chain is to
241231
// only show one error. For example, when the formatter is not specialized it
242232
// would show all static_assert messages. With this chain the compiler only
@@ -253,17 +243,37 @@ template <class _Context, class _Tp>
253243
else if constexpr (!__correct_parse_function_return_type)
254244
static_assert(false, "The required formatter specialization's parse function does not return the required type.");
255245

256-
else if constexpr (!__has_format_function)
257-
static_assert(
258-
false, "The required formatter specialization does not have a format function taking the proper arguments.");
259-
else if constexpr (!__is_format_function_const_qualified)
260-
static_assert(false, "The required formatter specialization's format function is not const qualified.");
261-
else if constexpr (!__correct_format_function_return_type)
262-
static_assert(false, "The required formatter specialization's format function does not return the required type.");
246+
else {
247+
// During constant evaluation this function is called, but the format
248+
// member function has not been evaluated. This means these functions
249+
// can't be evaluated at that time.
250+
//
251+
// Note this else branch should never been taken during constant
252+
// eveluation, the static_asserts in one of the branches above should
253+
// trigger.
254+
constexpr bool __has_format_function = requires(_Formatter& __f, _Tp&& __t, _Context __fc) {
255+
{ __f.format(__t, __fc) };
256+
};
257+
constexpr bool __is_format_function_const_qualified = requires(const _Formatter& __cf, _Tp&& __t, _Context __fc) {
258+
{ __cf.format(__t, __fc) };
259+
};
260+
constexpr bool __correct_format_function_return_type = requires(_Formatter& __f, _Tp&& __t, _Context __fc) {
261+
{ __f.format(__t, __fc)->same_as<typename _Context::iterator> };
262+
};
263+
264+
if constexpr (!__has_format_function)
265+
static_assert(
266+
false, "The required formatter specialization does not have a format function taking the proper arguments.");
267+
else if constexpr (!__is_format_function_const_qualified)
268+
static_assert(false, "The required formatter specialization's format function is not const qualified.");
269+
else if constexpr (!__correct_format_function_return_type)
270+
static_assert(
271+
false, "The required formatter specialization's format function does not return the required type.");
263272

264-
else
265-
// This should not happen; it makes sure the code is ill-formed.
266-
static_assert(false, "The required formatter specialization is not formattable with its context.");
273+
else
274+
// This should not happen; it makes sure the code is ill-formed.
275+
static_assert(false, "The required formatter specialization is not formattable with its context.");
276+
}
267277
}
268278

269279
// The Psuedo constructor is constrained per [format.arg]/4.

libcxx/include/__format/format_functions.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,27 @@ template <class... _Args>
7878

7979
namespace __format {
8080

81+
/// A "watered-down" __formattable_with for usage during constant evaluation.
82+
///
83+
/// The member function "format" can have a decuded return type. When
84+
/// constraining the __compile_time_handle this leads to a nested concept
85+
/// eveluation that is always false. Instead ignore the "format" member function.
86+
///
87+
/// In theory the same could happen to the "parse" member function. However
88+
/// thisfunction does not is less likely to have odd cased, and this function
89+
/// is "required" to be called during constant evaluation. So if this
90+
/// function's return type needs to be deduced things will probably not work.
91+
///
92+
/// See https://github.com/llvm/llvm-project/issues/81590 and
93+
/// test/std/utilities/format/format.functions/bug_81590.compile.pass.cpp for
94+
/// an example of a problematic "format" member function.
95+
template <class _Tp, class _Context, class _Formatter = typename _Context::template formatter_type<remove_const_t<_Tp>>>
96+
concept __formattable_with_constexpr =
97+
semiregular<_Formatter> &&
98+
requires(_Formatter& __f, _Tp&& __t, basic_format_parse_context<typename _Context::char_type> __pc) {
99+
{ __f.parse(__pc) } -> same_as<typename decltype(__pc)::iterator>;
100+
};
101+
81102
/// Helper class parse and handle argument.
82103
///
83104
/// When parsing a handle which is not enabled the code is ill-formed.
@@ -91,7 +112,7 @@ class _LIBCPP_TEMPLATE_VIS __compile_time_handle {
91112
}
92113

93114
template <class _Tp>
94-
requires __formattable_with<_Tp, __format_context<_CharT>>
115+
requires __formattable_with_constexpr<_Tp, __format_context<_CharT>>
95116
_LIBCPP_HIDE_FROM_ABI constexpr void __enable() {
96117
__parse_ = [](basic_format_parse_context<_CharT>& __ctx) {
97118
formatter<_Tp, _CharT> __f;

libcxx/test/libcxx/utilities/format/format.functions/format.verify.cpp

Lines changed: 216 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//===----------------------------------------------------------------------===//
12
//
23
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
34
// See https://llvm.org/LICENSE.txt for license information.
@@ -15,6 +16,7 @@
1516
// diagnoses the issue.
1617

1718
#include <format>
19+
#include <variant>
1820

1921
struct no_formatter_specialization {};
2022
void test_no_formatter_specialization() {
@@ -36,7 +38,9 @@ struct std::formatter<correct_formatter_specialization, CharT> {
3638
}
3739

3840
template <class FormatContext>
39-
typename FormatContext::iterator format(correct_formatter_specialization&, FormatContext&) const;
41+
typename FormatContext::iterator format(correct_formatter_specialization&, FormatContext& ctx) const {
42+
return ctx.out();
43+
}
4044
};
4145
void test_correct_formatter_specialization() {
4246
correct_formatter_specialization t;
@@ -52,7 +56,9 @@ struct std::formatter<formatter_not_semiregular, CharT> {
5256
formatter(int);
5357

5458
template <class ParseContext>
55-
constexpr typename ParseContext::iterator parse(ParseContext&);
59+
constexpr typename ParseContext::iterator parse(ParseContext& ctx) {
60+
return ctx.begin();
61+
}
5662

5763
template <class FormatContext>
5864
typename FormatContext::iterator format(formatter_not_semiregular&, FormatContext&) const;
@@ -87,7 +93,9 @@ struct parse_function_invalid_arguments {};
8793
template <class CharT>
8894
struct std::formatter<parse_function_invalid_arguments, CharT> {
8995
template <class ParseContext>
90-
constexpr typename ParseContext::iterator parse(ParseContext&, int);
96+
constexpr typename ParseContext::iterator parse(ParseContext& ctx, int) {
97+
return ctx.begin();
98+
}
9199

92100
template <class FormatContext>
93101
typename FormatContext::iterator format(parse_function_invalid_arguments&, FormatContext&) const;
@@ -106,7 +114,9 @@ struct parse_function_invalid_return_type {};
106114
template <class CharT>
107115
struct std::formatter<parse_function_invalid_return_type, CharT> {
108116
template <class ParseContext>
109-
constexpr int parse(ParseContext&);
117+
constexpr int parse(ParseContext&) {
118+
return 42;
119+
}
110120

111121
template <class FormatContext>
112122
typename FormatContext::iterator format(parse_function_invalid_return_type&, FormatContext&) const;
@@ -125,7 +135,9 @@ struct no_format_function {};
125135
template <class CharT>
126136
struct std::formatter<no_format_function, CharT> {
127137
template <class ParseContext>
128-
constexpr typename ParseContext::iterator parse(ParseContext&);
138+
constexpr typename ParseContext::iterator parse(ParseContext& ctx) {
139+
return ctx.begin();
140+
}
129141
};
130142
void test_no_format_function() {
131143
no_format_function t;
@@ -141,7 +153,9 @@ struct format_function_invalid_arguments {};
141153
template <class CharT>
142154
struct std::formatter<format_function_invalid_arguments, CharT> {
143155
template <class ParseContext>
144-
constexpr typename ParseContext::iterator parse(ParseContext&);
156+
constexpr typename ParseContext::iterator parse(ParseContext& ctx) {
157+
return ctx.begin();
158+
}
145159

146160
template <class FormatContext>
147161
typename FormatContext::iterator format(format_function_invalid_arguments&) const;
@@ -160,7 +174,9 @@ struct format_function_invalid_return_type {};
160174
template <class CharT>
161175
struct std::formatter<format_function_invalid_return_type, CharT> {
162176
template <class ParseContext>
163-
constexpr typename ParseContext::iterator parse(ParseContext&);
177+
constexpr typename ParseContext::iterator parse(ParseContext& ctx) {
178+
return ctx.begin();
179+
}
164180

165181
template <class FormatContext>
166182
int format(format_function_invalid_return_type&, FormatContext&) const;
@@ -179,7 +195,9 @@ struct format_function_not_const_qualified {};
179195
template <class CharT>
180196
struct std::formatter<format_function_not_const_qualified, CharT> {
181197
template <class ParseContext>
182-
constexpr typename ParseContext::iterator parse(ParseContext&);
198+
constexpr typename ParseContext::iterator parse(ParseContext& ctx) {
199+
return ctx.begin();
200+
}
183201

184202
template <class FormatContext>
185203
typename FormatContext::iterator format(format_function_not_const_qualified&, FormatContext&);
@@ -193,3 +211,193 @@ void test_format_function_not_const_qualified() {
193211
(void)std::format(L"{}", t);
194212
#endif
195213
}
214+
215+
struct auto_deduction_correct_formatter_specialization
216+
: std::variant<auto_deduction_correct_formatter_specialization*> {
217+
auto_deduction_correct_formatter_specialization* p = nullptr;
218+
constexpr const std::variant<auto_deduction_correct_formatter_specialization*>& decay() const noexcept {
219+
return *this;
220+
}
221+
};
222+
223+
template <>
224+
struct std::formatter<auto_deduction_correct_formatter_specialization, char> {
225+
template <class ParseContext>
226+
static constexpr auto parse(ParseContext& ctx) {
227+
return ctx.begin();
228+
}
229+
static constexpr auto format(const auto_deduction_correct_formatter_specialization& x, auto& ctx) {
230+
if (!x.p)
231+
return ctx.out();
232+
auto m = [&](const auto_deduction_correct_formatter_specialization* t) {
233+
return std::format_to(ctx.out(), "{}", *t);
234+
};
235+
return std::visit(m, x.decay());
236+
}
237+
};
238+
239+
void test_auto_deduction_correct_formatter_specialization() {
240+
auto_deduction_correct_formatter_specialization t;
241+
(void)std::format("{}", t);
242+
}
243+
244+
struct auto_deduction_no_parse_function : std::variant<auto_deduction_no_parse_function*> {
245+
auto_deduction_no_parse_function* p = nullptr;
246+
constexpr const std::variant<auto_deduction_no_parse_function*>& decay() const noexcept { return *this; }
247+
};
248+
249+
template <>
250+
struct std::formatter<auto_deduction_no_parse_function, char> {
251+
static constexpr auto format(const auto_deduction_no_parse_function& x, auto& ctx) {
252+
if (!x.p)
253+
return ctx.out();
254+
auto m = [&](const auto_deduction_no_parse_function* t) { return std::format_to(ctx.out(), "{}", *t); };
255+
return std::visit(m, x.decay());
256+
}
257+
};
258+
259+
void test_auto_deduction_no_parse_function() {
260+
auto_deduction_no_parse_function t;
261+
// expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}}
262+
(void)std::format("{}", t);
263+
}
264+
265+
struct auto_deduction_parse_function_invalid_arguments
266+
: std::variant<auto_deduction_parse_function_invalid_arguments*> {
267+
auto_deduction_parse_function_invalid_arguments* p = nullptr;
268+
constexpr const std::variant<auto_deduction_parse_function_invalid_arguments*>& decay() const noexcept {
269+
return *this;
270+
}
271+
};
272+
273+
template <>
274+
struct std::formatter<auto_deduction_parse_function_invalid_arguments, char> {
275+
template <class ParseContext>
276+
static constexpr auto parse(ParseContext& ctx, int) {
277+
return ctx.begin();
278+
}
279+
static constexpr auto format(const auto_deduction_parse_function_invalid_arguments& x, auto& ctx) {
280+
if (!x.p)
281+
return ctx.out();
282+
auto m = [&](const auto_deduction_parse_function_invalid_arguments* t) {
283+
return std::format_to(ctx.out(), "{}", *t);
284+
};
285+
return std::visit(m, x.decay());
286+
}
287+
};
288+
289+
void test_auto_deduction_parse_function_invalid_arguments() {
290+
auto_deduction_parse_function_invalid_arguments t;
291+
// expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}}
292+
(void)std::format("{}", t);
293+
}
294+
295+
struct auto_deduction_parse_function_invalid_return_types
296+
: std::variant<auto_deduction_parse_function_invalid_return_types*> {
297+
auto_deduction_parse_function_invalid_return_types* p = nullptr;
298+
constexpr const std::variant<auto_deduction_parse_function_invalid_return_types*>& decay() const noexcept {
299+
return *this;
300+
}
301+
};
302+
303+
template <>
304+
struct std::formatter<auto_deduction_parse_function_invalid_return_types, char> {
305+
template <class ParseContext>
306+
static constexpr auto parse(ParseContext&) {
307+
return 42;
308+
}
309+
static constexpr auto format(const auto_deduction_parse_function_invalid_return_types& x, auto& ctx) {
310+
if (!x.p)
311+
return ctx.out();
312+
auto m = [&](const auto_deduction_parse_function_invalid_return_types* t) {
313+
return std::format_to(ctx.out(), "{}", *t);
314+
};
315+
return std::visit(m, x.decay());
316+
}
317+
};
318+
319+
void test_auto_deduction_parse_function_invalid_return_types() {
320+
// expected-error@*:* {{static assertion failed: The required formatter specialization's parse function does not return the required type.}}
321+
auto_deduction_parse_function_invalid_return_types t;
322+
(void)std::format("{}", t);
323+
}
324+
325+
struct auto_deduction_no_format_function : std::variant<auto_deduction_no_format_function*> {
326+
auto_deduction_no_format_function* p = nullptr;
327+
// expected-error@*:* 2 {{static assertion failed: The required formatter specialization's format function does not return the required type.}}
328+
constexpr const std::variant<auto_deduction_no_format_function*>& decay() const noexcept { return *this; }
329+
};
330+
331+
template <>
332+
struct std::formatter<auto_deduction_no_format_function, char> {
333+
template <class ParseContext>
334+
static constexpr auto parse(ParseContext& ctx) {
335+
return ctx.begin();
336+
}
337+
};
338+
339+
void test_auto_deduction_no_format_function() {
340+
// expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}}
341+
auto_deduction_no_format_function t;
342+
(void)std::format("{}", t);
343+
}
344+
345+
struct auto_deduction_format_function_invalid_arguments
346+
: std::variant<auto_deduction_format_function_invalid_arguments*> {
347+
auto_deduction_format_function_invalid_arguments* p = nullptr;
348+
constexpr const std::variant<auto_deduction_format_function_invalid_arguments*>& decay() const noexcept {
349+
return *this;
350+
}
351+
};
352+
353+
template <>
354+
struct std::formatter<auto_deduction_format_function_invalid_arguments, char> {
355+
template <class ParseContext>
356+
static constexpr auto parse(ParseContext& ctx) {
357+
return ctx.begin();
358+
}
359+
static constexpr auto format(const auto_deduction_format_function_invalid_arguments& x, auto& ctx, int) {
360+
if (!x.p)
361+
return ctx.out();
362+
auto m = [&](const auto_deduction_format_function_invalid_arguments* t) {
363+
return std::format_to(ctx.out(), "{}", *t);
364+
};
365+
return std::visit(m, x.decay());
366+
}
367+
};
368+
369+
void test_auto_deduction_format_function_invalid_arguments() {
370+
// expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}}
371+
auto_deduction_format_function_invalid_arguments t;
372+
(void)std::format("{}", t);
373+
}
374+
375+
struct auto_deduction_format_function_invalid_return_types
376+
: std::variant<auto_deduction_format_function_invalid_return_types*> {
377+
auto_deduction_format_function_invalid_return_types* p = nullptr;
378+
constexpr const std::variant<auto_deduction_format_function_invalid_return_types*>& decay() const noexcept {
379+
return *this;
380+
}
381+
};
382+
383+
template <>
384+
struct std::formatter<auto_deduction_format_function_invalid_return_types, char> {
385+
template <class ParseContext>
386+
static constexpr auto parse(ParseContext& ctx) {
387+
return ctx.begin();
388+
}
389+
static constexpr auto format(const auto_deduction_format_function_invalid_return_types& x, auto& ctx) {
390+
if (!x.p)
391+
return 42;
392+
auto m = [&](const auto_deduction_format_function_invalid_return_types* t) {
393+
std::format_to(ctx.out(), "{}", *t);
394+
return 42;
395+
};
396+
return std::visit(m, x.decay());
397+
}
398+
};
399+
400+
void test_auto_deduction_format_function_invalid_return_types() {
401+
auto_deduction_format_function_invalid_return_types t;
402+
(void)std::format("{}", t);
403+
}

0 commit comments

Comments
 (0)