From bde72ecf152059e454f82f9c4c1a5d846d3f5a53 Mon Sep 17 00:00:00 2001 From: Mark de Wever Date: Sat, 25 Nov 2023 16:53:54 +0100 Subject: [PATCH 1/3] [libc++][format] Fixes formatting code units as integers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This paper was voted in as a DR, so it's retroactively enabled back to C++20; the C++ version that introduced std::format. Implements: - P2909R4 Fix formatting of code units as integers (Dude, where’s my ``char``?) --- libcxx/docs/FeatureTestMacroTable.rst | 2 +- libcxx/docs/ReleaseNotes/18.rst | 1 + libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/docs/Status/FormatIssues.csv | 2 +- libcxx/include/__format/format_arg_store.h | 10 +- libcxx/include/__format/formatter_char.h | 19 ++- libcxx/include/version | 2 +- .../.version.compile.pass.cpp | 48 ++----- .../version.version.compile.pass.cpp | 48 ++----- .../formatter.char.fsigned-char.pass.cpp | 131 ++++++++++++++++++ .../formatter.char.funsigned-char.pass.cpp | 131 ++++++++++++++++++ .../generate_feature_test_macro_components.py | 1 - 12 files changed, 312 insertions(+), 85 deletions(-) create mode 100644 libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.fsigned-char.pass.cpp create mode 100644 libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.funsigned-char.pass.cpp diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 3b2dff3108b0f..d09f65b7cadc0 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -236,7 +236,7 @@ Status --------------------------------------------------- ----------------- ``__cpp_lib_format`` *unimplemented* --------------------------------------------------- ----------------- - ``__cpp_lib_format_uchar`` *unimplemented* + ``__cpp_lib_format_uchar`` ``202311L`` --------------------------------------------------- ----------------- ``__cpp_lib_generic_unordered_lookup`` ``201811L`` --------------------------------------------------- ----------------- diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst index 7c4b578bd6fa3..36ce4a46f76e4 100644 --- a/libcxx/docs/ReleaseNotes/18.rst +++ b/libcxx/docs/ReleaseNotes/18.rst @@ -53,6 +53,7 @@ Implemented Papers - P2918R2 - Runtime format strings II - P2871R3 - Remove Deprecated Unicode Conversion Facets from C++26 - P2870R3 - Remove basic_string::reserve() +- P2909R4 - Fix formatting of code units as integers (Dude, where’s my ``char``?) Improvements and New Features diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 65c3391526596..1d071b7ebcb4a 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -32,7 +32,7 @@ "`P2546R5 `__","LWG","Debugging Support","Kona November 2023","","","" "`P2905R2 `__","LWG","Runtime format strings","Kona November 2023","","","|format| |DR|" "`P2918R2 `__","LWG","Runtime format strings II","Kona November 2023","|Complete|","18.0","|format|" -"`P2909R4 `__","LWG","Fix formatting of code units as integers (Dude, where’s my ``char``?)","Kona November 2023","","","|format| |DR|" +"`P2909R4 `__","LWG","Fix formatting of code units as integers (Dude, where’s my ``char``?)","Kona November 2023","|Complete|","18.0","|format| |DR|" "`P0952R2 `__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","","" "`P2447R6 `__","LWG","``std::span`` over an initializer list","Kona November 2023","","","" "`P2821R5 `__","LWG","``span.at()``","Kona November 2023","","","" diff --git a/libcxx/docs/Status/FormatIssues.csv b/libcxx/docs/Status/FormatIssues.csv index 14ea7c4360a37..005de97405f7c 100644 --- a/libcxx/docs/Status/FormatIssues.csv +++ b/libcxx/docs/Status/FormatIssues.csv @@ -19,7 +19,7 @@ Number,Name,Standard,Assignee,Status,First released version "`P2637R3 `__","Member ``visit``","C++26","","", "`P2905R2 `__","Runtime format strings","C++26 DR","Mark de Wever","|In Progress|" "`P2918R2 `__","Runtime format strings II","C++26","Mark de Wever","|Complete|",18.0 -"`P2909R4 `__","Fix formatting of code units as integers (Dude, where’s my ``char``?)","C++26 DR","Mark de Wever","|In Progress|" +"`P2909R4 `__","Fix formatting of code units as integers (Dude, where’s my ``char``?)","C++26 DR","Mark de Wever","|Complete|",18.0 `P1361 `_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|, `P2372 `__,"Fixing locale handling in chrono formatters","C++20",Mark de Wever,|In Progress|, "`P2419R2 `__","Clarify handling of encodings in localized formatting of chrono types","C++23", diff --git a/libcxx/include/__format/format_arg_store.h b/libcxx/include/__format/format_arg_store.h index 15ec8eb0a7d83..2962962ab5d1c 100644 --- a/libcxx/include/__format/format_arg_store.h +++ b/libcxx/include/__format/format_arg_store.h @@ -172,9 +172,13 @@ _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp& __valu // __basic_format_arg_value. First handle all types needing adjustment, the // final else requires no adjustment. if constexpr (__arg == __arg_t::__char_type) - // On some platforms initializing a wchar_t from a char is a narrowing - // conversion. - return basic_format_arg<_Context>{__arg, static_cast(__value)}; + +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + if constexpr (same_as && same_as<_Dp, char>) + return basic_format_arg<_Context>{__arg, static_cast(static_cast(__value))}; + else +# endif + return basic_format_arg<_Context>{__arg, __value}; else if constexpr (__arg == __arg_t::__int) return basic_format_arg<_Context>{__arg, static_cast(__value)}; else if constexpr (__arg == __arg_t::__long_long) diff --git a/libcxx/include/__format/formatter_char.h b/libcxx/include/__format/formatter_char.h index d6e61e8654493..bc3962c876071 100644 --- a/libcxx/include/__format/formatter_char.h +++ b/libcxx/include/__format/formatter_char.h @@ -21,7 +21,7 @@ #include <__format/parser_std_format_spec.h> #include <__format/write_escaped.h> #include <__type_traits/conditional.h> -#include <__type_traits/is_signed.h> +#include <__type_traits/make_unsigned.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -51,22 +51,20 @@ struct _LIBCPP_TEMPLATE_VIS __formatter_char { return __formatter::__format_escaped_char(__value, __ctx.out(), __parser_.__get_parsed_std_specifications(__ctx)); # endif - if constexpr (sizeof(_CharT) <= sizeof(int)) - // Promotes _CharT to an integral type. This reduces the number of - // instantiations of __format_integer reducing code size. + if constexpr (sizeof(_CharT) <= sizeof(unsigned)) return __formatter::__format_integer( - static_cast, int, unsigned>>(__value), + static_cast(static_cast>(__value)), __ctx, __parser_.__get_parsed_std_specifications(__ctx)); else - return __formatter::__format_integer(__value, __ctx, __parser_.__get_parsed_std_specifications(__ctx)); + return __formatter::__format_integer( + static_cast>(__value), __ctx, __parser_.__get_parsed_std_specifications(__ctx)); } template _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(char __value, _FormatContext& __ctx) const - requires(same_as<_CharT, wchar_t>) - { - return format(static_cast(__value), __ctx); + requires(same_as<_CharT, wchar_t>) { + return format(static_cast(static_cast(__value)), __ctx); } # if _LIBCPP_STD_VER >= 23 @@ -84,8 +82,7 @@ template <> struct _LIBCPP_TEMPLATE_VIS formatter : public __formatter_char {}; template <> -struct _LIBCPP_TEMPLATE_VIS formatter : public __formatter_char { -}; +struct _LIBCPP_TEMPLATE_VIS formatter : public __formatter_char {}; # endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS diff --git a/libcxx/include/version b/libcxx/include/version index ef01af7532982..e84790b888d33 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -384,7 +384,7 @@ __cpp_lib_within_lifetime 202306L # undef __cpp_lib_execution // # define __cpp_lib_execution 201902L // # define __cpp_lib_format 202106L -// # define __cpp_lib_format_uchar 202311L +# define __cpp_lib_format_uchar 202311L # define __cpp_lib_generic_unordered_lookup 201811L # define __cpp_lib_int_pow2 202002L # define __cpp_lib_integer_comparison_functions 202002L diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/.version.compile.pass.cpp index 6aa31c60e8630..2486985cefaca 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/.version.compile.pass.cpp @@ -55,17 +55,11 @@ #elif TEST_STD_VER == 20 -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should be defined in c++20" -# endif -# if __cpp_lib_format_uchar != 202311L -# error "__cpp_lib_format_uchar should have the value 202311L in c++20" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_format_uchar +# error "__cpp_lib_format_uchar should be defined in c++20" +# endif +# if __cpp_lib_format_uchar != 202311L +# error "__cpp_lib_format_uchar should have the value 202311L in c++20" # endif # ifdef __cpp_lib_saturation_arithmetic @@ -74,17 +68,11 @@ #elif TEST_STD_VER == 23 -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should be defined in c++23" -# endif -# if __cpp_lib_format_uchar != 202311L -# error "__cpp_lib_format_uchar should have the value 202311L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_format_uchar +# error "__cpp_lib_format_uchar should be defined in c++23" +# endif +# if __cpp_lib_format_uchar != 202311L +# error "__cpp_lib_format_uchar should have the value 202311L in c++23" # endif # ifdef __cpp_lib_saturation_arithmetic @@ -93,17 +81,11 @@ #elif TEST_STD_VER > 23 -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should be defined in c++26" -# endif -# if __cpp_lib_format_uchar != 202311L -# error "__cpp_lib_format_uchar should have the value 202311L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_format_uchar +# error "__cpp_lib_format_uchar should be defined in c++26" +# endif +# if __cpp_lib_format_uchar != 202311L +# error "__cpp_lib_format_uchar should have the value 202311L in c++26" # endif # if !defined(_LIBCPP_VERSION) diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index d7adf2941b62c..a995795e305c4 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -3383,17 +3383,11 @@ # error "__cpp_lib_format_ranges should not be defined before c++23" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should be defined in c++20" -# endif -# if __cpp_lib_format_uchar != 202311L -# error "__cpp_lib_format_uchar should have the value 202311L in c++20" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_format_uchar +# error "__cpp_lib_format_uchar should be defined in c++20" +# endif +# if __cpp_lib_format_uchar != 202311L +# error "__cpp_lib_format_uchar should have the value 202311L in c++20" # endif # ifdef __cpp_lib_formatters @@ -4778,17 +4772,11 @@ # error "__cpp_lib_format_ranges should have the value 202207L in c++23" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should be defined in c++23" -# endif -# if __cpp_lib_format_uchar != 202311L -# error "__cpp_lib_format_uchar should have the value 202311L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_format_uchar +# error "__cpp_lib_format_uchar should be defined in c++23" +# endif +# if __cpp_lib_format_uchar != 202311L +# error "__cpp_lib_format_uchar should have the value 202311L in c++23" # endif # if !defined(_LIBCPP_VERSION) @@ -6389,17 +6377,11 @@ # error "__cpp_lib_format_ranges should have the value 202207L in c++26" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should be defined in c++26" -# endif -# if __cpp_lib_format_uchar != 202311L -# error "__cpp_lib_format_uchar should have the value 202311L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_format_uchar -# error "__cpp_lib_format_uchar should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_format_uchar +# error "__cpp_lib_format_uchar should be defined in c++26" +# endif +# if __cpp_lib_format_uchar != 202311L +# error "__cpp_lib_format_uchar should have the value 202311L in c++26" # endif # if !defined(_LIBCPP_VERSION) diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.fsigned-char.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.fsigned-char.pass.cpp new file mode 100644 index 0000000000000..933cb1ff962bb --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.fsigned-char.pass.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// ADDITIONAL_COMPILE_FLAGS: -fsigned-char + +// + +// C++23 the formatter is a debug-enabled specialization. +// [format.formatter.spec]: +// Each header that declares the template `formatter` provides the following +// enabled specializations: +// The specializations +// template<> struct formatter; +// template<> struct formatter; +// template<> struct formatter; + +// P2909R4 "Fix formatting of code units as integers (Dude, where’s my char?)" +// changed the behaviour of char (and wchar_t) when their underlying type is signed. + +#include +#include +#include +#include +#include + +#include "test_format_context.h" +#include "test_macros.h" +#include "make_string.h" +#include "assert_macros.h" +#include "concat_macros.h" + +#define STR(S) MAKE_STRING(CharT, S) +#define SV(S) MAKE_STRING_VIEW(CharT, S) + +template +void test(StringT expected, StringViewT fmt, ArgumentT arg) { + using CharT = typename StringT::value_type; + auto parse_ctx = std::basic_format_parse_context(fmt); + std::formatter formatter; + static_assert(std::semiregular); + + formatter.parse(parse_ctx); + + StringT result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + FormatCtxT format_ctx = test_format_context_create(out, std::make_format_args(arg)); + formatter.format(arg, format_ctx); + TEST_REQUIRE(result == expected, + TEST_WRITE_CONCATENATED( + "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", result, '\n')); +} + +template +void test() { + test(STR("\x00"), STR("}"), '\x00'); + test(STR("a"), STR("}"), 'a'); + test(STR("\x80"), STR("}"), '\x80'); + test(STR("\xff"), STR("}"), '\xff'); + + test(STR("\x00"), STR("c}"), '\x00'); + test(STR("a"), STR("c}"), 'a'); + test(STR("\x80"), STR("c}"), '\x80'); + test(STR("\xff"), STR("c}"), '\xff'); + +#if TEST_STD_VER > 20 + test(STR(R"('\u{0}')"), STR("?}"), '\x00'); + test(STR("'a'"), STR("?}"), 'a'); + if constexpr (std::same_as) { + test(STR(R"('\x{80}')"), STR("?}"), '\x80'); + test(STR(R"('\x{ff}')"), STR("?}"), '\xff'); + } +# ifndef TEST_HAS_NO_WIDE_CHARACTERS + else { + test(STR(R"('\u{80}')"), STR("?}"), '\x80'); + test(STR("'\u00ff'"), STR("?}"), '\xff'); + } +# endif // TEST_HAS_NO_WIDE_CHARACTERS +#endif // TEST_STD_VER > 20 + + test(STR("10000000"), STR("b}"), char(-128)); + test(STR("11111111"), STR("b}"), char(-1)); + test(STR("0"), STR("b}"), char(0)); + test(STR("1"), STR("b}"), char(1)); + test(STR("1111111"), STR("b}"), char(127)); + + test(STR("10000000"), STR("B}"), char(-128)); + test(STR("11111111"), STR("B}"), char(-1)); + test(STR("0"), STR("B}"), char(0)); + test(STR("1"), STR("B}"), char(1)); + test(STR("1111111"), STR("B}"), char(127)); + + test(STR("128"), STR("d}"), char(-128)); + test(STR("255"), STR("d}"), char(-1)); + test(STR("0"), STR("d}"), char(0)); + test(STR("1"), STR("d}"), char(1)); + test(STR("127"), STR("d}"), char(127)); + + test(STR("200"), STR("o}"), char(-128)); + test(STR("377"), STR("o}"), char(-1)); + test(STR("0"), STR("o}"), char(0)); + test(STR("1"), STR("o}"), char(1)); + test(STR("177"), STR("o}"), char(127)); + + test(STR("80"), STR("x}"), char(-128)); + test(STR("ff"), STR("x}"), char(-1)); + test(STR("0"), STR("x}"), char(0)); + test(STR("1"), STR("x}"), char(1)); + test(STR("7f"), STR("x}"), char(127)); + + test(STR("80"), STR("X}"), char(-128)); + test(STR("FF"), STR("X}"), char(-1)); + test(STR("0"), STR("X}"), char(0)); + test(STR("1"), STR("X}"), char(1)); + test(STR("7F"), STR("X}"), char(127)); +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.funsigned-char.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.funsigned-char.pass.cpp new file mode 100644 index 0000000000000..c0044cd1f615f --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.funsigned-char.pass.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// ADDITIONAL_COMPILE_FLAGS: -funsigned-char + +// + +// C++23 the formatter is a debug-enabled specialization. +// [format.formatter.spec]: +// Each header that declares the template `formatter` provides the following +// enabled specializations: +// The specializations +// template<> struct formatter; +// template<> struct formatter; +// template<> struct formatter; + +// P2909R4 "Fix formatting of code units as integers (Dude, where’s my char?)" +// changed the behaviour of char (and wchar_t) when their underlying type is signed. + +#include +#include +#include +#include +#include + +#include "test_format_context.h" +#include "test_macros.h" +#include "make_string.h" +#include "assert_macros.h" +#include "concat_macros.h" + +#define STR(S) MAKE_STRING(CharT, S) +#define SV(S) MAKE_STRING_VIEW(CharT, S) + +template +void test(StringT expected, StringViewT fmt, ArgumentT arg) { + using CharT = typename StringT::value_type; + auto parse_ctx = std::basic_format_parse_context(fmt); + std::formatter formatter; + static_assert(std::semiregular); + + formatter.parse(parse_ctx); + + StringT result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + FormatCtxT format_ctx = test_format_context_create(out, std::make_format_args(arg)); + formatter.format(arg, format_ctx); + TEST_REQUIRE(result == expected, + TEST_WRITE_CONCATENATED( + "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", result, '\n')); +} + +template +void test() { + test(STR("\x00"), STR("}"), '\x00'); + test(STR("a"), STR("}"), 'a'); + test(STR("\x80"), STR("}"), '\x80'); + test(STR("\xff"), STR("}"), '\xff'); + + test(STR("\x00"), STR("c}"), '\x00'); + test(STR("a"), STR("c}"), 'a'); + test(STR("\x80"), STR("c}"), '\x80'); + test(STR("\xff"), STR("c}"), '\xff'); + +#if TEST_STD_VER > 20 + test(STR(R"('\u{0}')"), STR("?}"), '\x00'); + test(STR("'a'"), STR("?}"), 'a'); + if constexpr (std::same_as) { + test(STR(R"('\x{80}')"), STR("?}"), '\x80'); + test(STR(R"('\x{ff}')"), STR("?}"), '\xff'); + } +# ifndef TEST_HAS_NO_WIDE_CHARACTERS + else { + test(STR(R"('\u{80}')"), STR("?}"), '\x80'); + test(STR("'\u00ff'"), STR("?}"), '\xff'); + } +# endif // TEST_HAS_NO_WIDE_CHARACTERS +#endif // TEST_STD_VER > 20 + + test(STR("10000000"), STR("b}"), char(128)); + test(STR("11111111"), STR("b}"), char(255)); + test(STR("0"), STR("b}"), char(0)); + test(STR("1"), STR("b}"), char(1)); + test(STR("1111111"), STR("b}"), char(127)); + + test(STR("10000000"), STR("B}"), char(128)); + test(STR("11111111"), STR("B}"), char(255)); + test(STR("0"), STR("B}"), char(0)); + test(STR("1"), STR("B}"), char(1)); + test(STR("1111111"), STR("B}"), char(127)); + + test(STR("128"), STR("d}"), char(128)); + test(STR("255"), STR("d}"), char(255)); + test(STR("0"), STR("d}"), char(0)); + test(STR("1"), STR("d}"), char(1)); + test(STR("127"), STR("d}"), char(127)); + + test(STR("200"), STR("o}"), char(128)); + test(STR("377"), STR("o}"), char(255)); + test(STR("0"), STR("o}"), char(0)); + test(STR("1"), STR("o}"), char(1)); + test(STR("177"), STR("o}"), char(127)); + + test(STR("80"), STR("x}"), char(128)); + test(STR("ff"), STR("x}"), char(255)); + test(STR("0"), STR("x}"), char(0)); + test(STR("1"), STR("x}"), char(1)); + test(STR("7f"), STR("x}"), char(127)); + + test(STR("80"), STR("X}"), char(128)); + test(STR("FF"), STR("X}"), char(255)); + test(STR("0"), STR("X}"), char(0)); + test(STR("1"), STR("X}"), char(1)); + test(STR("7F"), STR("X}"), char(127)); +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 3a068ab120b15..47ee20de0fdc2 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -487,7 +487,6 @@ def add_version_header(tc): "c++20": 202311 # DR P2909R4 Fix formatting of code units as integers }, "headers": [""], # Note not in format - "unimplemented": True, }, { "name": "__cpp_lib_formatters", From cba5ac688d1f1a1e24648eba5f0268ba33bf82b3 Mon Sep 17 00:00:00 2001 From: Mark de Wever Date: Tue, 28 Nov 2023 19:28:58 +0100 Subject: [PATCH 2/3] Addresses review comments. --- .../format.formatter.spec/formatter.char.fsigned-char.pass.cpp | 1 - .../format.formatter.spec/formatter.char.funsigned-char.pass.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.fsigned-char.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.fsigned-char.pass.cpp index 933cb1ff962bb..d0da8d69f3f99 100644 --- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.fsigned-char.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.fsigned-char.pass.cpp @@ -14,7 +14,6 @@ // [format.formatter.spec]: // Each header that declares the template `formatter` provides the following // enabled specializations: -// The specializations // template<> struct formatter; // template<> struct formatter; // template<> struct formatter; diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.funsigned-char.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.funsigned-char.pass.cpp index c0044cd1f615f..9c31ecad85eac 100644 --- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.funsigned-char.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.funsigned-char.pass.cpp @@ -14,7 +14,6 @@ // [format.formatter.spec]: // Each header that declares the template `formatter` provides the following // enabled specializations: -// The specializations // template<> struct formatter; // template<> struct formatter; // template<> struct formatter; From 9f10eec6c09f011a7c4b5e4129a48ef60ba07536 Mon Sep 17 00:00:00 2001 From: Mark de Wever Date: Wed, 29 Nov 2023 17:54:48 +0100 Subject: [PATCH 3/3] Formatting fixes. --- libcxx/include/__format/formatter_char.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libcxx/include/__format/formatter_char.h b/libcxx/include/__format/formatter_char.h index bc3962c876071..3358d422252f4 100644 --- a/libcxx/include/__format/formatter_char.h +++ b/libcxx/include/__format/formatter_char.h @@ -63,7 +63,8 @@ struct _LIBCPP_TEMPLATE_VIS __formatter_char { template _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(char __value, _FormatContext& __ctx) const - requires(same_as<_CharT, wchar_t>) { + requires(same_as<_CharT, wchar_t>) + { return format(static_cast(static_cast(__value)), __ctx); }