[libc++][format] Exposes basic-format-string
authorMark de Wever <koraq@xs4all.nl>
Fri, 15 Jul 2022 05:42:17 +0000 (07:42 +0200)
committerMark de Wever <koraq@xs4all.nl>
Tue, 2 Aug 2022 18:33:17 +0000 (20:33 +0200)
This paper was accepted during the last plenary and is intended to be
backported to LLVM 15. When backporting the release notes in the branch
should be updated too.

Note the feature-test macro isn't updated since this will change; three
papers have updated the same macro in the same plenary.

Implements:
- P2508R1 Exposing std::basic-format-string

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D130643

libcxx/docs/Status/Cxx2bPapers.csv
libcxx/docs/Status/FormatIssues.csv
libcxx/docs/UsingLibcxx.rst
libcxx/include/format
libcxx/test/std/utilities/format/format.fmt.string/ctor.verify.cpp [new file with mode: 0644]
libcxx/test/std/utilities/format/format.fmt.string/get.pass.cpp [new file with mode: 0644]
libcxx/test/std/utilities/format/format.fmt.string/types.compile.pass.cpp [new file with mode: 0644]
libcxx/utils/generate_feature_test_macro_components.py

index c3a6331..e9b1055 100644 (file)
@@ -81,7 +81,7 @@
 "`P2494R2 <https://wg21.link/P2494R2>`__","LWG","Relaxing range adaptors to allow for move only types","July 2022","",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","LWG","``string_view`` range constructor should be ``explicit``","July 2022","|Complete|","16.0"
 "`P2502R2 <https://wg21.link/P2502R2>`__","LWG","``std::generator``: Synchronous Coroutine Generator for Ranges","July 2022","",""
-"`P2508R1 <https://wg21.link/P2508R1>`__","LWG","Exposing ``std::basic-format-string``","July 2022","",""
+"`P2508R1 <https://wg21.link/P2508R1>`__","LWG","Exposing ``std::basic-format-string``","July 2022","|Complete|","15.0"
 "`P2513R4 <https://wg21.link/P2513R4>`__","LWG","``char8_t`` Compatibility and Portability Fixes","July 2022","",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","LWG","Add a conditional ``noexcept`` specification to ``std::apply``","July 2022","",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","LWG","``move_iterator`` should be a random access iterator","July 2022","",""
index 01b5818..1cf3829 100644 (file)
@@ -7,7 +7,7 @@ Number,Name,Standard,Assignee,Status,First released version
 `P2418 <https://wg21.link/P2418>`__,"Add support for ``std::generator``-like types to ``std::format``","C++20",Mark de Wever,|Complete|, Clang 15
 "`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","C++23"
 "`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","C++23","Mark de Wever","|In Progress|"
-"`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","C++23","Mark de Wever","|In Progress|"
+"`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","C++23","Mark de Wever","|Complete|", Clang 15
 "`P2585R0 <https://wg21.link/P2585R0>`__","Improving default container formatting","C++23",
 
 `P1361 <https://wg21.link/P1361>`_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|,
index d4f86ff..c0d4cee 100644 (file)
@@ -460,3 +460,11 @@ As an extension these types can be used in the following headers:
 
 * ``<format>``
 * ``<random>``
+
+Extensions to ``<format>``
+--------------------------
+
+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. Libc++ makes these types
+available in C++20 as an extension.
index d2ec8fc..6b14570 100644 (file)
@@ -23,16 +23,23 @@ namespace std {
   using format_args = basic_format_args<format_context>;
   using wformat_args = basic_format_args<wformat_context>;
 
-  // [format.fmt.string], class template basic-format-string
+  // [format.fmt.string], class template basic_format_string
   template<class charT, class... Args>
-    struct basic-format-string;                       // exposition only
+    struct basic_format_string {                                // since C++23, exposition only before C++23
+    private:
+      basic_string_view<charT> str;                             // exposition only
 
+    public:
+      template<class T> consteval basic_format_string(const T& s);
+
+      constexpr basic_string_view<charT> get() const noexcept { return str; }
+    };
   template<class... Args>
-    using format-string =                             // exposition only
-      basic-format-string<char, type_identity_t<Args>...>;
+    using format_string =                                       // since C++23, exposition only before C++23
+      basic_format_string<char, type_identity_t<Args>...>;
   template<class... Args>
-    using wformat-string =                            // exposition only
-      basic-format-string<wchar_t, type_identity_t<Args>...>;
+    using wformat_string =                                      // since C++23, exposition only before C++23
+      basic_format_string<wchar_t, type_identity_t<Args>...>;
 
   // [format.functions], formatting functions
   template<class... Args>
@@ -233,7 +240,7 @@ private:
 };
 
 // Dummy format_context only providing the parts used during constant
-// validation of the basic-format-string.
+// validation of the basic_format_string.
 template <class _CharT>
 struct _LIBCPP_TEMPLATE_VIS __compile_time_basic_format_context {
 public:
@@ -468,17 +475,21 @@ __vformat_to(_ParseCtx&& __parse_ctx, _Ctx&& __ctx) {
 } // namespace __format
 
 template <class _CharT, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS __basic_format_string {
-  basic_string_view<_CharT> __str_;
-
+struct _LIBCPP_TEMPLATE_VIS basic_format_string {
   template <class _Tp>
     requires convertible_to<const _Tp&, basic_string_view<_CharT>>
-  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 @@ private:
 };
 
 template <class... _Args>
-using __format_string_t = __basic_format_string<char, type_identity_t<_Args>...>;
+using format_string = basic_format_string<char, type_identity_t<_Args>...>;
 
 #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
 template <class... _Args>
-using __wformat_string_t = __basic_format_string<wchar_t, type_identity_t<_Args>...>;
+using wformat_string = basic_format_string<wchar_t, type_identity_t<_Args>...>;
 #endif
 
 template <class _OutIt, class _CharT, class _FormatOutIt>
@@ -555,16 +566,16 @@ vformat_to(_OutIt __out_it, wstring_view __fmt, wformat_args __args) {
 
 template <output_iterator<const char&> _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 <output_iterator<const wchar_t&> _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 @@ vformat(wstring_view __fmt, wformat_args __args) {
 #endif
 
 template <class... _Args>
-_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 <class... _Args>
 _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 @@ _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __vformat_to_n(_OutIt __out_it,
 
 template <output_iterator<const char&> _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<format_context>(_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<format_context>(_VSTD::move(__out_it), __n, __fmt.get(), _VSTD::make_format_args(__args...));
 }
 
 #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
 template <output_iterator<const wchar_t&> _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<wformat_context>(_VSTD::move(__out_it), __n, __fmt.__str_, _VSTD::make_wformat_args(__args...));
+  return _VSTD::__vformat_to_n<wformat_context>(_VSTD::move(__out_it), __n, __fmt.get(), _VSTD::make_wformat_args(__args...));
 }
 #endif
 
@@ -634,15 +645,15 @@ _LIBCPP_HIDE_FROM_ABI size_t __vformatted_size(basic_string_view<_CharT> __fmt,
 
 template <class... _Args>
 _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 <class... _Args>
 _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 @@ _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt v
 
 template <output_iterator<const char&> _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 <output_iterator<const wchar_t&> _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 @@ vformat(locale __loc, wstring_view __fmt, wformat_args __args) {
 
 template <class... _Args>
 _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 <class... _Args>
 _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 @@ _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __vformat_to_n(_OutIt __out_it,
 
 template <output_iterator<const char&> _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<format_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_,
+  return _VSTD::__vformat_to_n<format_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.get(),
                                                _VSTD::make_format_args(__args...));
 }
 
 #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
 template <output_iterator<const wchar_t&> _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<wformat_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_,
+  return _VSTD::__vformat_to_n<wformat_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.get(),
                                                 _VSTD::make_wformat_args(__args...));
 }
 #endif
@@ -775,15 +786,15 @@ _LIBCPP_HIDE_FROM_ABI size_t __vformatted_size(locale __loc, basic_string_view<_
 
 template <class... _Args>
 _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 <class... _Args>
 _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 (file)
index 0000000..8f5404d
--- /dev/null
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-format
+
+// libc++ supports basic_format_string in C++20 as an extension
+// UNSUPPORTED: !stdlib=libc++ && c++20
+
+// <format>
+
+// template<class charT, class... Args>
+// class basic_format_string<charT, type_identity_t<Args>...>
+//
+// template<class T> consteval basic_format_string(const T& s);
+//
+// This constructor does the compile-time format string validation for the
+// std::format* functions.
+
+#include <format>
+
+#include <string_view>
+
+#include "test_macros.h"
+
+void run() {
+  (void)std::basic_format_string<char>{"foo"};
+  (void)std::basic_format_string<char>{"{}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
+  (void)std::basic_format_string<char, int>{"{0:{0}P}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
+  (void)std::basic_format_string<char, int>{"{0:{0}}"};
+  (void)std::basic_format_string<char, float>{"{0:{0}}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
+  (void)std::basic_format_string<char, int>{"{.3}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  (void)std::basic_format_string<wchar_t>{L"foo"};
+  (void)std::basic_format_string<wchar_t>{L"{}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
+  (void)std::basic_format_string<wchar_t, int>{L"{0:{0}P}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
+  (void)std::basic_format_string<wchar_t, int>{L"{0:{0}}"};
+  (void)std::basic_format_string<wchar_t, float>{L"{0:{0}}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
+  (void)std::basic_format_string<wchar_t, int>{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 (file)
index 0000000..bf7e2ad
--- /dev/null
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-format
+
+// libc++ supports basic_format_string in C++20 as an extension
+// UNSUPPORTED: !stdlib=libc++ && c++20
+
+// <format>
+
+// template<class charT, class... Args>
+// class basic_format_string<charT, type_identity_t<Args>...>
+//
+// constexpr basic_string_view<charT> get() const noexcept { return str; }
+
+#include <format>
+
+#include <cassert>
+#include <concepts>
+#include <string_view>
+
+#include "test_macros.h"
+#include "make_string.h"
+
+#define CSTR(S) MAKE_CSTRING(CharT, S)
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+
+template <class CharT>
+constexpr bool test() {
+  assert((std::basic_format_string<CharT>{CSTR("foo")}.get() == SV("foo")));
+  assert((std::basic_format_string<CharT, int>{CSTR("{}")}.get() == SV("{}")));
+  assert((std::basic_format_string<CharT, int, float>{CSTR("{} {:01.23L}")}.get() == SV("{} {:01.23L}")));
+
+  // Embedded NUL character
+  assert((std::basic_format_string<CharT, void*, double>{SV("{}\0{}")}.get() == SV("{}\0{}")));
+  return true;
+}
+
+int main(int, char**) {
+  test<char>();
+  static_assert(test<char>());
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+  static_assert(test<wchar_t>());
+#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 (file)
index 0000000..3ebd2bf
--- /dev/null
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-format
+
+// libc++ supports basic_format_string in C++20 as an extension
+// UNSUPPORTED: !stdlib=libc++ && c++20
+
+// <format>
+
+//  template<class... Args>
+//    using format_string =
+//      basic_format_string<char, type_identity_t<Args>...>;
+//  template<class... Args>
+//    using wformat_string =
+//      basic_format_string<wchar_t, type_identity_t<Args>...>;
+
+#include <format>
+
+#include <concepts>
+
+#include "test_macros.h"
+
+static_assert(std::same_as<std::format_string<>, std::basic_format_string<char>>);
+static_assert(std::same_as<std::format_string<int>, std::basic_format_string<char, int>>);
+static_assert(std::same_as<std::format_string<int, float>, std::basic_format_string<char, int, float>>);
+static_assert(std::same_as<std::format_string<int, float, void*>, std::basic_format_string<char, int, float, void*>>);
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::same_as<std::wformat_string<>, std::basic_format_string<wchar_t>>);
+static_assert(std::same_as<std::wformat_string<int>, std::basic_format_string<wchar_t, int>>);
+static_assert(std::same_as<std::wformat_string<int, float>, std::basic_format_string<wchar_t, int, float>>);
+static_assert(
+    std::same_as<std::wformat_string<int, float, void*>, std::basic_format_string<wchar_t, int, float, void*>>);
+#endif
index 6b8db98..f9bfa13 100755 (executable)
@@ -309,6 +309,11 @@ feature_test_macros = [ add_version_header(x) for x in [
     "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. We 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) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)",