diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -81,7 +81,7 @@ "`P2494R2 `__","LWG","Relaxing range adaptors to allow for move only types","July 2022","","" "`P2499R0 `__","LWG","``string_view`` range constructor should be ``explicit``","July 2022","","" "`P2502R2 `__","LWG","``std::generator``: Synchronous Coroutine Generator for Ranges","July 2022","","" -"`P2508R1 `__","LWG","Exposing ``std::basic-format-string``","July 2022","","" +"`P2508R1 `__","LWG","Exposing ``std::basic-format-string``","July 2022","|Complete|","15.0" "`P2513R4 `__","LWG","``char8_t`` Compatibility and Portability Fixes","July 2022","","" "`P2517R1 `__","LWG","Add a conditional ``noexcept`` specification to ``std::apply``","July 2022","","" "`P2520R0 `__","LWG","``move_iterator`` should be a random access iterator","July 2022","","" diff --git a/libcxx/docs/Status/FormatIssues.csv b/libcxx/docs/Status/FormatIssues.csv --- a/libcxx/docs/Status/FormatIssues.csv +++ b/libcxx/docs/Status/FormatIssues.csv @@ -7,7 +7,7 @@ `P2418 `__,"Add support for ``std::generator``-like types to ``std::format``","C++20",Mark de Wever,|Complete|, Clang 15 "`P2093R14 `__","Formatted output","C++23" "`P2286R8 `__","Formatting Ranges","C++23","Mark de Wever","|In Progress|" -"`P2508R1 `__","Exposing ``std::basic-format-string``","C++23","Mark de Wever","|In Progress|" +"`P2508R1 `__","Exposing ``std::basic-format-string``","C++23","Mark de Wever","|Complete|", Clang 15 "`P2585R0 `__","Improving default container formatting","C++23", `P1361 `_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|, diff --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst --- a/libcxx/docs/UsingLibcxx.rst +++ b/libcxx/docs/UsingLibcxx.rst @@ -444,3 +444,12 @@ undefined. As an extension, libc++ supports instantiating ``binomial_distribution``, ``discrete_distribution``, ``geometric_distribution``, ``negative_binomial_distribution``, ``poisson_distribution``, and ``uniform_int_distribution`` with ``int8_t``, ``__int128_t`` and their unsigned versions. + +Extensions ```` +~~~~~~~~~~~~~~~~~~~~~~~ + +The exposition only type ``basic-format-string`` and its typedefs +``format-string`` and ``wformat-string`` became ``basic_format_string``, +``format_string``, and ``wformat_string`` in C++23. As an extension libc++ +makes these types available in C++20. (The exposition only types were never +shipped.) diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -23,16 +23,23 @@ using format_args = basic_format_args; using wformat_args = basic_format_args; - // [format.fmt.string], class template basic-format-string + // [format.fmt.string], class template basic_format_string template - struct basic-format-string; // exposition only + struct basic_format_string { // since C++23, before an exposition only type + private: + basic_string_view str; // exposition only + public: + template consteval basic_format_string(const T& s); + + constexpr basic_string_view get() const noexcept { return str; } + }; template - using format-string = // exposition only - basic-format-string...>; + using format_string = // since C++23, before an exposition only type + basic_format_string...>; template - using wformat-string = // exposition only - basic-format-string...>; + using wformat_string = // since C++23, before an exposition only type + basic_format_string...>; // [format.functions], formatting functions template @@ -233,7 +240,7 @@ }; // Dummy format_context only providing the parts used during constant -// validation of the basic-format-string. +// validation of the basic_format_string. template struct _LIBCPP_TEMPLATE_VIS __compile_time_basic_format_context { public: @@ -468,17 +475,21 @@ } // namespace __format template -struct _LIBCPP_TEMPLATE_VIS __basic_format_string { - basic_string_view<_CharT> __str_; - +struct _LIBCPP_TEMPLATE_VIS basic_format_string { template requires convertible_to> - consteval __basic_format_string(const _Tp& __str) : __str_{__str} { + consteval basic_format_string(const _Tp& __str) : __str_{__str} { __format::__vformat_to(basic_format_parse_context<_CharT>{__str_, sizeof...(_Args)}, _Context{__types_.data(), __handles_.data(), sizeof...(_Args)}); } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT constexpr basic_string_view<_CharT> get() const noexcept { + return __str_; + } + private: + basic_string_view<_CharT> __str_; + using _Context = __format::__compile_time_basic_format_context<_CharT>; static constexpr array<__format::__arg_t, sizeof...(_Args)> __types_{ @@ -510,11 +521,11 @@ }; template -using __format_string_t = __basic_format_string...>; +using format_string = basic_format_string...>; #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template -using __wformat_string_t = __basic_format_string...>; +using wformat_string = basic_format_string...>; #endif template @@ -555,16 +566,16 @@ template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt -format_to(_OutIt __out_it, __format_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.__str_, +format_to(_OutIt __out_it, format_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.get(), _VSTD::make_format_args(__args...)); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt -format_to(_OutIt __out_it, __wformat_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.__str_, +format_to(_OutIt __out_it, wformat_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.get(), _VSTD::make_wformat_args(__args...)); } #endif @@ -586,16 +597,16 @@ #endif template -_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string format(__format_string_t<_Args...> __fmt, +_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string format(format_string<_Args...> __fmt, _Args&&... __args) { - return _VSTD::vformat(__fmt.__str_, _VSTD::make_format_args(__args...)); + return _VSTD::vformat(__fmt.get(), _VSTD::make_format_args(__args...)); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring -format(__wformat_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::vformat(__fmt.__str_, _VSTD::make_wformat_args(__args...)); +format(wformat_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::vformat(__fmt.get(), _VSTD::make_wformat_args(__args...)); } #endif @@ -611,16 +622,16 @@ template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> -format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, __format_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, __fmt.__str_, _VSTD::make_format_args(__args...)); +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, format_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, __fmt.get(), _VSTD::make_format_args(__args...)); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _OutIt, class... _Args> _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> -format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, __wformat_string_t<_Args...> __fmt, +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, wformat_string<_Args...> __fmt, _Args&&... __args) { - return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, __fmt.__str_, _VSTD::make_wformat_args(__args...)); + return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, __fmt.get(), _VSTD::make_wformat_args(__args...)); } #endif @@ -634,15 +645,15 @@ template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t -formatted_size(__format_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::__vformatted_size(__fmt.__str_, basic_format_args{_VSTD::make_format_args(__args...)}); +formatted_size(format_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::__vformatted_size(__fmt.get(), basic_format_args{_VSTD::make_format_args(__args...)}); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t -formatted_size(__wformat_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::__vformatted_size(__fmt.__str_, basic_format_args{_VSTD::make_wformat_args(__args...)}); +formatted_size(wformat_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::__vformatted_size(__fmt.get(), basic_format_args{_VSTD::make_wformat_args(__args...)}); } #endif @@ -686,16 +697,16 @@ template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt -format_to(_OutIt __out_it, locale __loc, __format_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.__str_, +format_to(_OutIt __out_it, locale __loc, format_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.get(), _VSTD::make_format_args(__args...)); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt -format_to(_OutIt __out_it, locale __loc, __wformat_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.__str_, +format_to(_OutIt __out_it, locale __loc, wformat_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.get(), _VSTD::make_wformat_args(__args...)); } #endif @@ -720,17 +731,17 @@ template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string format(locale __loc, - __format_string_t<_Args...> __fmt, + format_string<_Args...> __fmt, _Args&&... __args) { - return _VSTD::vformat(_VSTD::move(__loc), __fmt.__str_, + return _VSTD::vformat(_VSTD::move(__loc), __fmt.get(), _VSTD::make_format_args(__args...)); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring -format(locale __loc, __wformat_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::vformat(_VSTD::move(__loc), __fmt.__str_, +format(locale __loc, wformat_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::vformat(_VSTD::move(__loc), __fmt.get(), _VSTD::make_wformat_args(__args...)); } #endif @@ -748,18 +759,18 @@ template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> -format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, __format_string_t<_Args...> __fmt, +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, format_string<_Args...> __fmt, _Args&&... __args) { - return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_, + return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.get(), _VSTD::make_format_args(__args...)); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> -format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, __wformat_string_t<_Args...> __fmt, +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, wformat_string<_Args...> __fmt, _Args&&... __args) { - return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_, + return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.get(), _VSTD::make_wformat_args(__args...)); } #endif @@ -775,15 +786,15 @@ template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t -formatted_size(locale __loc, __format_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.__str_, basic_format_args{_VSTD::make_format_args(__args...)}); +formatted_size(locale __loc, format_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.get(), basic_format_args{_VSTD::make_format_args(__args...)}); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t -formatted_size(locale __loc, __wformat_string_t<_Args...> __fmt, _Args&&... __args) { - return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.__str_, basic_format_args{_VSTD::make_wformat_args(__args...)}); +formatted_size(locale __loc, wformat_string<_Args...> __fmt, _Args&&... __args) { + return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.get(), basic_format_args{_VSTD::make_wformat_args(__args...)}); } #endif diff --git a/libcxx/test/std/utilities/format/format.fmt.string/ctor.verify.cpp b/libcxx/test/std/utilities/format/format.fmt.string/ctor.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.fmt.string/ctor.verify.cpp @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: !stdlib=libc++ && c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// template +// class basic_format_string...> +// +// template consteval basic_format_string(const T& s); +// +// This constructor does the compile-time format string validation for the +// std::format* functions. + +#include + +#include + +#include "test_macros.h" + +void run() { + (void)std::basic_format_string{"foo"}; + (void)std::basic_format_string{"{}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}} + (void)std::basic_format_string{"{0:{0}P}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}} + (void)std::basic_format_string{"{0:{0}}"}; + (void)std::basic_format_string{"{0:{0}}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}} + (void)std::basic_format_string{"{.3}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}} +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + (void)std::basic_format_string{L"foo"}; + (void)std::basic_format_string{L"{}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}} + (void)std::basic_format_string{L"{0:{0}P}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}} + (void)std::basic_format_string{L"{0:{0}}"}; + (void)std::basic_format_string{L"{0:{0}}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}} + (void)std::basic_format_string{L"{.3}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}} +#endif +} diff --git a/libcxx/test/std/utilities/format/format.fmt.string/get.pass.cpp b/libcxx/test/std/utilities/format/format.fmt.string/get.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.fmt.string/get.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: !stdlib=libc++ && c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// template +// class basic_format_string...> +// +// constexpr basic_string_view get() const noexcept { return str; } + +#include + +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) +#define SV(S) MAKE_STRING_VIEW(CharT, S) + +template +constexpr bool test() { + assert((std::basic_format_string{CSTR("foo")}.get() == SV("foo"))); + assert((std::basic_format_string{CSTR("{}")}.get() == SV("{}"))); + assert((std::basic_format_string{CSTR("{} {:01.23L}")}.get() == SV("{} {:01.23L}"))); + + // Embedded NUL character + assert((std::basic_format_string{SV("{}\0{}")}.get() == SV("{}\0{}"))); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); + static_assert(test()); +#endif + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.fmt.string/types.compile.pass.cpp b/libcxx/test/std/utilities/format/format.fmt.string/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.fmt.string/types.compile.pass.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: !stdlib=libc++ && c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// template +// using format_string = +// basic_format_string...>; +// template +// using wformat_string = +// basic_format_string...>; + +#include + +#include + +#include "test_macros.h" + +static_assert(std::same_as, std::basic_format_string>); +static_assert(std::same_as, std::basic_format_string>); +static_assert(std::same_as, std::basic_format_string>); +static_assert(std::same_as, std::basic_format_string>); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +static_assert(std::same_as, std::basic_format_string>); +static_assert(std::same_as, std::basic_format_string>); +static_assert(std::same_as, std::basic_format_string>); +static_assert( + std::same_as, std::basic_format_string>); +#endif diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -309,6 +309,11 @@ "libcxx_guard": "!defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_filesystem)" }, { "name": "__cpp_lib_format", + # P2508, P2286, and P2419 were accepted in the same plenary and modify this + # feature-test macro. I expect to see an LWG issue soon. For now keep the + # value as is. + # TODO FMT Set P2508's feature-test macro. + #"values": { "c++20": 202106, "c++23": 202207" }, "values": { "c++20": 202106 }, "headers": ["format"], "test_suite_guard": "!defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format)",