Skip to content

Commit 96f2f4c

Browse files
[libc++][format] Decay character arrays in formatting
Currently, built-in `char`/`wchar_t` arrays are assumed to be null-terminated sequence with the terminator being the last element in formatting. This doesn't conform to [format.arg]/6.9. > otherwise, if `decay_t<TD>` is `char_type*` or `const char_type*`, > initializes value with `static_cast<const char_type*>(v)`; The standard wording specifies that character arrays are decayed to pointers. When the null terminator is not the last element or there's no null terminator (the latter case is UB), libc++ currently produces different results.
1 parent 1d46020 commit 96f2f4c

File tree

2 files changed

+9
-18
lines changed

2 files changed

+9
-18
lines changed

libcxx/include/__format/format_arg_store.h

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,14 @@ consteval __arg_t __determine_arg_t() {
101101
return __arg_t::__long_double;
102102
}
103103

104-
// Char pointer
104+
// Char pointer or array
105105
template <class _Context, class _Tp>
106-
requires(same_as<typename _Context::char_type*, _Tp> || same_as<const typename _Context::char_type*, _Tp>)
106+
requires(same_as<typename _Context::char_type*, _Tp> || same_as<const typename _Context::char_type*, _Tp>) ||
107+
(is_array_v<_Tp> && same_as<_Tp, typename _Context::char_type[extent_v<_Tp>]>)
107108
consteval __arg_t __determine_arg_t() {
108109
return __arg_t::__const_char_type_ptr;
109110
}
110111

111-
// Char array
112-
template <class _Context, class _Tp>
113-
requires(is_array_v<_Tp> && same_as<_Tp, typename _Context::char_type[extent_v<_Tp>]>)
114-
consteval __arg_t __determine_arg_t() {
115-
return __arg_t::__string_view;
116-
}
117-
118112
// String view
119113
template <class _Context, class _Tp>
120114
requires(same_as<typename _Context::char_type, typename _Tp::value_type> &&
@@ -188,15 +182,8 @@ _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp& __valu
188182
else if constexpr (__arg == __arg_t::__unsigned_long_long)
189183
return basic_format_arg<_Context>{__arg, static_cast<unsigned long long>(__value)};
190184
else if constexpr (__arg == __arg_t::__string_view)
191-
// Using std::size on a character array will add the NUL-terminator to the size.
192-
if constexpr (is_array_v<_Dp>)
193-
return basic_format_arg<_Context>{
194-
__arg, basic_string_view<typename _Context::char_type>{__value, extent_v<_Dp> - 1}};
195-
else
196-
// When the _Traits or _Allocator are different an implicit conversion will
197-
// fail.
198-
return basic_format_arg<_Context>{
199-
__arg, basic_string_view<typename _Context::char_type>{__value.data(), __value.size()}};
185+
return basic_format_arg<_Context>{
186+
__arg, basic_string_view<typename _Context::char_type>{__value.data(), __value.size()}};
200187
else if constexpr (__arg == __arg_t::__ptr)
201188
return basic_format_arg<_Context>{__arg, static_cast<const void*>(__value)};
202189
else if constexpr (__arg == __arg_t::__handle)

libcxx/test/std/utilities/format/format.functions/format_tests.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3189,6 +3189,10 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
31893189
const CharT* data = buffer;
31903190
check(SV("hello 09azAZ!"), SV("hello {}"), data);
31913191
}
3192+
{
3193+
CharT buffer[] = {CharT('a'), CharT('b'), CharT('c'), 0, CharT('d'), CharT('e'), CharT('f'), 0};
3194+
check(SV("hello abc"), SV("hello {}"), buffer);
3195+
}
31923196
{
31933197
std::basic_string<CharT> data = STR("world");
31943198
check(SV("hello world"), SV("hello {}"), data);

0 commit comments

Comments
 (0)