[libcxx-commits] [libcxx] f712775 - [libc++][format] Exposes basic-format-string

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Tue Aug 2 11:33:28 PDT 2022


Author: Mark de Wever
Date: 2022-08-02T20:33:17+02:00
New Revision: f712775dafdb221fdf73f38819fa9e618aec6b67

URL: https://github.com/llvm/llvm-project/commit/f712775dafdb221fdf73f38819fa9e618aec6b67
DIFF: https://github.com/llvm/llvm-project/commit/f712775dafdb221fdf73f38819fa9e618aec6b67.diff

LOG: [libc++][format] Exposes basic-format-string

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

Added: 
    libcxx/test/std/utilities/format/format.fmt.string/ctor.verify.cpp
    libcxx/test/std/utilities/format/format.fmt.string/get.pass.cpp
    libcxx/test/std/utilities/format/format.fmt.string/types.compile.pass.cpp

Modified: 
    libcxx/docs/Status/Cxx2bPapers.csv
    libcxx/docs/Status/FormatIssues.csv
    libcxx/docs/UsingLibcxx.rst
    libcxx/include/format
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv
index c3a63311d03d5..e9b10554da4fe 100644
--- a/libcxx/docs/Status/Cxx2bPapers.csv
+++ b/libcxx/docs/Status/Cxx2bPapers.csv
@@ -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","",""

diff  --git a/libcxx/docs/Status/FormatIssues.csv b/libcxx/docs/Status/FormatIssues.csv
index 01b5818f9d15b..1cf3829f65c0d 100644
--- a/libcxx/docs/Status/FormatIssues.csv
+++ b/libcxx/docs/Status/FormatIssues.csv
@@ -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|,

diff  --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst
index d4f86ff397af5..c0d4cee0b6c32 100644
--- a/libcxx/docs/UsingLibcxx.rst
+++ b/libcxx/docs/UsingLibcxx.rst
@@ -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.

diff  --git a/libcxx/include/format b/libcxx/include/format
index d2ec8fc233634..6b14570bc6272 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -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_
diff erence_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_
diff erence_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_
diff erence_t<_OutIt> __n, __wformat_string_t<_Args...> __fmt,
+format_to_n(_OutIt __out_it, iter_
diff erence_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_
diff erence_t<_OutIt> __n, locale __loc, __format_string_t<_Args...> __fmt,
+format_to_n(_OutIt __out_it, iter_
diff erence_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_
diff erence_t<_OutIt> __n, locale __loc, __wformat_string_t<_Args...> __fmt,
+format_to_n(_OutIt __out_it, iter_
diff erence_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
index 0000000000000..8f5404daaf396
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.fmt.string/ctor.verify.cpp
@@ -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
index 0000000000000..bf7e2add0e8f5
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.fmt.string/get.pass.cpp
@@ -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
index 0000000000000..3ebd2bfc4fbd5
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.fmt.string/types.compile.pass.cpp
@@ -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

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 6b8db98242f29..f9bfa13cc2991 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -309,6 +309,11 @@ def add_version_header(tc):
     "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)",


        


More information about the libcxx-commits mailing list