[libcxx-commits] [libcxx] aed5ddf - [libc++][format] Implement format-string.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Sat Jun 11 06:26:01 PDT 2022


Author: Mark de Wever
Date: 2022-06-11T15:25:56+02:00
New Revision: aed5ddf8d09762f3adb80dc842239ad67ed2e301

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

LOG: [libc++][format] Implement format-string.

Implements the compile-time checking of the formatting arguments.

Completes:
- P2216 std::format improvements

Reviewed By: #libc, ldionne

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

Added: 
    libcxx/test/std/utilities/format/format.functions/format.locale.verify.cpp
    libcxx/test/std/utilities/format/format.functions/format.verify.cpp
    libcxx/test/std/utilities/format/format.functions/format_to.locale.verify.cpp
    libcxx/test/std/utilities/format/format.functions/format_to.verify.cpp
    libcxx/test/std/utilities/format/format.functions/format_to_n.locale.verify.cpp
    libcxx/test/std/utilities/format/format.functions/format_to_n.verify.cpp
    libcxx/test/std/utilities/format/format.functions/formatted_size.locale.verify.cpp
    libcxx/test/std/utilities/format/format.functions/formatted_size.verify.cpp

Modified: 
    libcxx/benchmarks/formatter_float.bench.cpp
    libcxx/docs/ReleaseNotes.rst
    libcxx/docs/Status/Cxx20Papers.csv
    libcxx/include/__format/parser_std_format_spec.h
    libcxx/include/format
    libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp
    libcxx/test/std/utilities/format/format.functions/format.pass.cpp
    libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
    libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp
    libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp
    libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp
    libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp
    libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/benchmarks/formatter_float.bench.cpp b/libcxx/benchmarks/formatter_float.bench.cpp
index 3190b3779c5d9..ee04647aa955a 100644
--- a/libcxx/benchmarks/formatter_float.bench.cpp
+++ b/libcxx/benchmarks/formatter_float.bench.cpp
@@ -216,17 +216,27 @@ struct FloatingPoint {
   void run(benchmark::State& state) const {
     std::array<F, 1000> data{Value<V::value>::template make_data<F>()};
     std::array<char, 20'000> output;
-    std::string fmt{std::string("{:") + Alignment<A::value>::fmt + Precision<P::value>::fmt +
-                    Localization<L::value>::fmt + DisplayType<DT::value>::fmt + "}"};
 
     while (state.KeepRunningBatch(1000))
       for (F value : data)
-        benchmark::DoNotOptimize(std::format_to(output.begin(), fmt, value));
+        benchmark::DoNotOptimize(std::format_to(output.begin(), std::string_view{fmt.data(), fmt.size()}, value));
   }
 
   std::string name() const {
     return "FloatingPoint" + L::name() + DT::name() + T::name() + V::name() + A::name() + P::name();
   }
+
+  static constexpr std::string make_fmt() {
+    return std::string("{:") + Alignment<A::value>::fmt + Precision<P::value>::fmt + Localization<L::value>::fmt +
+           DisplayType<DT::value>::fmt + "}";
+  }
+
+  static constexpr auto fmt = []() {
+    constexpr size_t s = make_fmt().size();
+    std::array<char, s> r;
+    std::ranges::copy(make_fmt(), r.begin());
+    return r;
+  }();
 };
 
 int main(int argc, char** argv) {

diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index 8dc8733dd048d..8c77f2e3cff6a 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -73,6 +73,14 @@ New Features
   moved from the dylib to the header. This means the function no longer has a
   minimum deployment target.
 
+- Implemented P2216R3 (std::format improvements). The format functions
+  (``std::format``, ``std::format_to``, ``std::format_to_n``, and
+  ``std::formatted_size``) now validate the format string at compile time.
+  When the format string is invalid this will make the code ill-formed instead
+  of throwing an exception at run-time.  (This does not affect the ``v``
+  functions.)
+
+
 API Changes
 -----------
 

diff  --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 2a00accf35907..7ed0c7b825c49 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -196,7 +196,7 @@
 "`P2231R1 <https://wg21.link/P2231R1>`__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Partial| [#note-P2231]_","13.0"
 "`P2325R3 <https://wg21.link/P2325R3>`__","LWG","Views should not be required to be default constructible","June 2021","|In progress|",""
 "`P2210R2 <https://wg21.link/P2210R2>`__","LWG",Superior String Splitting,"June 2021","",""
-"`P2216R3 <https://wg21.link/P2216R3>`__","LWG",std::format improvements,"June 2021","|Partial|",""
+"`P2216R3 <https://wg21.link/P2216R3>`__","LWG",std::format improvements,"June 2021","|Complete|","15.0"
 "`P2281R1 <https://wg21.link/P2281R1>`__","LWG",Clarifying range adaptor objects,"June 2021","|Complete|","14.0"
 "`P2328R1 <https://wg21.link/P2328R1>`__","LWG",join_view should join all views of ranges,"June 2021","",""
 "`P2367R0 <https://wg21.link/P2367R0>`__","LWG",Remove misuses of list-initialization from Clause 24,"June 2021","",""

diff  --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h
index d58ad11ea0416..7a0efe10ff376 100644
--- a/libcxx/include/__format/parser_std_format_spec.h
+++ b/libcxx/include/__format/parser_std_format_spec.h
@@ -252,25 +252,21 @@ class _LIBCPP_TYPE_VIS __parser_width {
   /** Determines whether the value stored is a width or an arg-id. */
   uint32_t __width_as_arg : 1 {0};
 
-protected:
   /**
-   * Does the supplied std-format-spec contain a width field?
+   * Does the supplied width field contain an arg-id?
    *
-   * When the field isn't present there's no padding required. This can be used
-   * to optimize the formatting.
+   * If @c true the formatter needs to call @ref __substitute_width_arg_id.
    */
-  constexpr bool __has_width_field() const noexcept {
-    return __width_as_arg || __width;
-  }
+  constexpr bool __width_needs_substitution() const noexcept { return __width_as_arg; }
 
+protected:
   /**
-   * Does the supplied width field contain an arg-id?
+   * Does the supplied std-format-spec contain a width field?
    *
-   * If @c true the formatter needs to call @ref __substitute_width_arg_id.
+   * When the field isn't present there's no padding required. This can be used
+   * to optimize the formatting.
    */
-  constexpr bool __width_needs_substitution() const noexcept {
-    return __width_as_arg;
-  }
+  constexpr bool __has_width_field() const noexcept { return __width_as_arg || __width; }
 
   template <class _CharT>
   _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
@@ -327,6 +323,15 @@ class _LIBCPP_TYPE_VIS __parser_precision {
    */
   uint32_t __precision_as_arg : 1 {1};
 
+  /**
+   * Does the supplied precision field contain an arg-id?
+   *
+   * If @c true the formatter needs to call @ref __substitute_precision_arg_id.
+   */
+  constexpr bool __precision_needs_substitution() const noexcept {
+    return __precision_as_arg && __precision != __format::__number_max;
+  }
+
 protected:
   /**
    * Does the supplied std-format-spec contain a precision field?
@@ -340,15 +345,6 @@ class _LIBCPP_TYPE_VIS __parser_precision {
            __precision != __format::__number_max; // The arg-id is valid?
   }
 
-  /**
-   * Does the supplied precision field contain an arg-id?
-   *
-   * If @c true the formatter needs to call @ref __substitute_precision_arg_id.
-   */
-  constexpr bool __precision_needs_substitution() const noexcept {
-    return __precision_as_arg && __precision != __format::__number_max;
-  }
-
   template <class _CharT>
   _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
   __parse(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) {

diff  --git a/libcxx/include/format b/libcxx/include/format
index 95d85127a1967..b60a6aaa68157 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -23,15 +23,26 @@ 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
+  template<class charT, class... Args>
+    struct basic-format-string;                       // exposition only
+
+  template<class... Args>
+    using format-string =                             // exposition only
+      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>...>;
+
   // [format.functions], formatting functions
   template<class... Args>
-    string format(string_view fmt, const Args&... args);
+    string format(format-string<Args...> fmt, const Args&... args);
   template<class... Args>
-    wstring format(wstring_view fmt, const Args&... args);
+    wstring format(wformat-string<Args...> fmt, const Args&... args);
   template<class... Args>
-    string format(const locale& loc, string_view fmt, const Args&... args);
+    string format(const locale& loc, format-string<Args...> fmt, const Args&... args);
   template<class... Args>
-    wstring format(const locale& loc, wstring_view fmt, const Args&... args);
+    wstring format(const locale& loc, wformat-string<Args...> fmt, const Args&... args);
 
   string vformat(string_view fmt, format_args args);
   wstring vformat(wstring_view fmt, wformat_args args);
@@ -39,13 +50,13 @@ namespace std {
   wstring vformat(const locale& loc, wstring_view fmt, wformat_args args);
 
   template<class Out, class... Args>
-    Out format_to(Out out, string_view fmt, const Args&... args);
+    Out format_to(Out out, format-string<Args...> fmt, const Args&... args);
   template<class Out, class... Args>
-    Out format_to(Out out, wstring_view fmt, const Args&... args);
+    Out format_to(Out out, wformat-string<Args...> fmt, const Args&... args);
   template<class Out, class... Args>
-    Out format_to(Out out, const locale& loc, string_view fmt, const Args&... args);
+    Out format_to(Out out, const locale& loc, format-string<Args...> fmt, const Args&... args);
   template<class Out, class... Args>
-    Out format_to(Out out, const locale& loc, wstring_view fmt, const Args&... args);
+    Out format_to(Out out, const locale& loc, wformat-string<Args...> fmt, const Args&... args);
 
   template<class Out>
     Out vformat_to(Out out, string_view fmt, format_args args);
@@ -64,27 +75,27 @@ namespace std {
   };
   template<class Out, class... Args>
     format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
-                                        string_view fmt, const Args&... args);
+                                        format-string<Args...> fmt, const Args&... args);
   template<class Out, class... Args>
     format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
-                                        wstring_view fmt, const Args&... args);
+                                        wformat-string<Args...> fmt, const Args&... args);
   template<class Out, class... Args>
     format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
-                                        const locale& loc, string_view fmt,
+                                        const locale& loc, format-string<Args...> fmt,
                                         const Args&... args);
   template<class Out, class... Args>
     format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
-                                        const locale& loc, wstring_view fmt,
+                                        const locale& loc, wformat-string<Args...> fmt,
                                         const Args&... args);
 
   template<class... Args>
-    size_t formatted_size(string_view fmt, const Args&... args);
+    size_t formatted_size(format-string<Args...> fmt, const Args&... args);
   template<class... Args>
-    size_t formatted_size(wstring_view fmt, const Args&... args);
+    size_t formatted_size(wformat-string<Args...> fmt, const Args&... args);
   template<class... Args>
-    size_t formatted_size(const locale& loc, string_view fmt, const Args&... args);
+    size_t formatted_size(const locale& loc, format-string<Args...> fmt, const Args&... args);
   template<class... Args>
-    size_t formatted_size(const locale& loc, wstring_view fmt, const Args&... args);
+    size_t formatted_size(const locale& loc, wformat-string<Args...> fmt, const Args&... args);
 
   // [format.formatter], formatter
   template<class T, class charT = char> struct formatter;
@@ -203,8 +214,163 @@ _LIBCPP_HIDE_FROM_ABI auto make_wformat_args(const _Args&... __args) {
 
 namespace __format {
 
+/// Helper class parse and handle argument.
+///
+/// When parsing a handle which is not enabled the code is ill-formed.
+/// This helper uses the parser of the appropriate formatter for the stored type.
+template <class _CharT>
+class _LIBCPP_TEMPLATE_VIS __compile_time_handle {
+public:
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr void __parse(basic_format_parse_context<_CharT>& __parse_ctx) const { __parse_(__parse_ctx); }
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __enable() {
+    __parse_ = [](basic_format_parse_context<_CharT>& __parse_ctx) {
+      formatter<_Tp, _CharT> __f;
+      __parse_ctx.advance_to(__f.parse(__parse_ctx));
+    };
+  }
+
+  // Before calling __parse the proper handler needs to be set with __enable.
+  // The default handler isn't a core constant expression.
+  _LIBCPP_HIDE_FROM_ABI constexpr __compile_time_handle()
+      : __parse_([](basic_format_parse_context<_CharT>&) { __throw_format_error("Not a handle"); }) {}
+
+private:
+  void (*__parse_)(basic_format_parse_context<_CharT>&);
+};
+
+// Dummy format_context only providing the parts used during constant
+// validation of the basic-format-string.
+template <class _CharT>
+struct _LIBCPP_TEMPLATE_VIS __compile_time_basic_format_context {
+public:
+  using char_type = _CharT;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __compile_time_basic_format_context(
+      const __arg_t* __args, const __compile_time_handle<_CharT>* __handles, size_t __size)
+      : __args_(__args), __handles_(__handles), __size_(__size) {}
+
+  // During the compile-time validation nothing needs to be written.
+  // Therefore all operations of this iterator are a NOP.
+  struct iterator {
+    _LIBCPP_HIDE_FROM_ABI constexpr iterator& operator=(_CharT) { return *this; }
+    _LIBCPP_HIDE_FROM_ABI constexpr iterator& operator*() { return *this; }
+    _LIBCPP_HIDE_FROM_ABI constexpr iterator operator++(int) { return *this; }
+  };
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __arg_t arg(size_t __id) const {
+    if (__id >= __size_)
+      __throw_format_error("Argument index out of bounds");
+    return __args_[__id];
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const __compile_time_handle<_CharT>& __handle(size_t __id) const {
+    if (__id >= __size_)
+      __throw_format_error("Argument index out of bounds");
+    return __handles_[__id];
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr iterator out() { return {}; }
+  _LIBCPP_HIDE_FROM_ABI constexpr void advance_to(iterator) {}
+
+private:
+  const __arg_t* __args_;
+  const __compile_time_handle<_CharT>* __handles_;
+  size_t __size_;
+};
+
+_LIBCPP_HIDE_FROM_ABI
+constexpr void __compile_time_validate_integral(__arg_t __type) {
+  switch (__type) {
+  case __arg_t::__int:
+  case __arg_t::__long_long:
+  case __arg_t::__i128:
+  case __arg_t::__unsigned:
+  case __arg_t::__unsigned_long_long:
+  case __arg_t::__u128:
+    return;
+
+  default:
+    __throw_format_error("Argument isn't an integral type");
+  }
+}
+
+// _HasPrecision does the formatter have a precision?
+template <class _CharT, class _Tp, bool _HasPrecision = false>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__compile_time_validate_argument(basic_format_parse_context<_CharT>& __parse_ctx,
+                                 __compile_time_basic_format_context<_CharT>& __ctx) {
+  formatter<_Tp, _CharT> __formatter;
+  __parse_ctx.advance_to(__formatter.parse(__parse_ctx));
+  // [format.string.std]/7
+  // ... If the corresponding formatting argument is not of integral type, or
+  // its value is negative for precision or non-positive for width, an
+  // exception of type format_error is thrown.
+  //
+  // Validate whether the arguments are integrals.
+  if (__formatter.__width_needs_substitution())
+    __format::__compile_time_validate_integral(__ctx.arg(__formatter.__width));
+
+  if constexpr (_HasPrecision)
+    if (__formatter.__precision_needs_substitution())
+      __format::__compile_time_validate_integral(__ctx.arg(__formatter.__precision));
+}
+
+template <class _CharT>
+_LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_visit_format_arg(basic_format_parse_context<_CharT>& __parse_ctx,
+                                                                     __compile_time_basic_format_context<_CharT>& __ctx,
+                                                                     __arg_t __type) {
+  switch (__type) {
+  case __arg_t::__none:
+    __throw_format_error("Invalid argument");
+  case __arg_t::__boolean:
+    return __format::__compile_time_validate_argument<_CharT, bool>(__parse_ctx, __ctx);
+  case __arg_t::__char_type:
+    return __format::__compile_time_validate_argument<_CharT, _CharT>(__parse_ctx, __ctx);
+  case __arg_t::__int:
+    return __format::__compile_time_validate_argument<_CharT, int>(__parse_ctx, __ctx);
+  case __arg_t::__long_long:
+    return __format::__compile_time_validate_argument<_CharT, long long>(__parse_ctx, __ctx);
+  case __arg_t::__i128:
+#      ifndef _LIBCPP_HAS_NO_INT128
+    return __format::__compile_time_validate_argument<_CharT, __int128_t>(__parse_ctx, __ctx);
+#      else
+    __throw_format_error("Invalid argument");
+#      endif
+    return;
+  case __arg_t::__unsigned:
+    return __format::__compile_time_validate_argument<_CharT, unsigned>(__parse_ctx, __ctx);
+  case __arg_t::__unsigned_long_long:
+    return __format::__compile_time_validate_argument<_CharT, unsigned long long>(__parse_ctx, __ctx);
+  case __arg_t::__u128:
+#      ifndef _LIBCPP_HAS_NO_INT128
+    return __format::__compile_time_validate_argument<_CharT, __uint128_t>(__parse_ctx, __ctx);
+#      else
+    __throw_format_error("Invalid argument");
+#      endif
+    return;
+  case __arg_t::__float:
+    return __format::__compile_time_validate_argument<_CharT, float, true>(__parse_ctx, __ctx);
+  case __arg_t::__double:
+    return __format::__compile_time_validate_argument<_CharT, double, true>(__parse_ctx, __ctx);
+  case __arg_t::__long_double:
+    return __format::__compile_time_validate_argument<_CharT, long double, true>(__parse_ctx, __ctx);
+  case __arg_t::__const_char_type_ptr:
+    return __format::__compile_time_validate_argument<_CharT, const _CharT*, true>(__parse_ctx, __ctx);
+  case __arg_t::__string_view:
+    return __format::__compile_time_validate_argument<_CharT, basic_string_view<_CharT>, true>(__parse_ctx, __ctx);
+  case __arg_t::__ptr:
+    return __format::__compile_time_validate_argument<_CharT, const void*>(__parse_ctx, __ctx);
+  case __arg_t::__handle:
+    __throw_format_error("Handle should use __compile_time_validate_handle_argument");
+  }
+  __throw_format_error("Invalid argument");
+}
+
 template <class _CharT, class _ParseCtx, class _Ctx>
-_LIBCPP_HIDE_FROM_ABI const _CharT*
+_LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
 __handle_replacement_field(const _CharT* __begin, const _CharT* __end,
                            _ParseCtx& __parse_ctx, _Ctx& __ctx) {
   __format::__parse_number_result __r =
@@ -224,19 +390,26 @@ __handle_replacement_field(const _CharT* __begin, const _CharT* __end,
         "The replacement field arg-id should terminate at a ':' or '}'");
   }
 
-  _VSTD::visit_format_arg(
-      [&](auto __arg) {
-        if constexpr (same_as<decltype(__arg), monostate>)
-          __throw_format_error("Argument index out of bounds");
-        else if constexpr (same_as<decltype(__arg), typename basic_format_arg<_Ctx>::handle>)
-          __arg.format(__parse_ctx, __ctx);
-        else {
-          formatter<decltype(__arg), _CharT> __formatter;
-          __parse_ctx.advance_to(__formatter.parse(__parse_ctx));
-          __ctx.advance_to(__formatter.format(__arg, __ctx));
-        }
-      },
-      __ctx.arg(__r.__value));
+  if constexpr (same_as<_Ctx, __compile_time_basic_format_context<_CharT>>) {
+    __arg_t __type = __ctx.arg(__r.__value);
+    if (__type == __arg_t::__handle)
+      __ctx.__handle(__r.__value).__parse(__parse_ctx);
+    else
+      __format::__compile_time_visit_format_arg(__parse_ctx, __ctx, __type);
+  } else
+    _VSTD::visit_format_arg(
+        [&](auto __arg) {
+          if constexpr (same_as<decltype(__arg), monostate>)
+            __throw_format_error("Argument index out of bounds");
+          else if constexpr (same_as<decltype(__arg), typename basic_format_arg<_Ctx>::handle>)
+            __arg.format(__parse_ctx, __ctx);
+          else {
+            formatter<decltype(__arg), _CharT> __formatter;
+            __parse_ctx.advance_to(__formatter.parse(__parse_ctx));
+            __ctx.advance_to(__formatter.format(__arg, __ctx));
+          }
+        },
+        __ctx.arg(__r.__value));
 
   __begin = __parse_ctx.begin();
   if (__begin == __end || *__begin != _CharT('}'))
@@ -246,7 +419,7 @@ __handle_replacement_field(const _CharT* __begin, const _CharT* __end,
 }
 
 template <class _ParseCtx, class _Ctx>
-_LIBCPP_HIDE_FROM_ABI typename _Ctx::iterator
+_LIBCPP_HIDE_FROM_ABI constexpr typename _Ctx::iterator
 __vformat_to(_ParseCtx&& __parse_ctx, _Ctx&& __ctx) {
   using _CharT = typename _ParseCtx::char_type;
   static_assert(same_as<typename _Ctx::char_type, _CharT>);
@@ -291,6 +464,56 @@ __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_;
+
+  template <class _Tp>
+    requires convertible_to<const _Tp&, basic_string_view<_CharT>>
+  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)});
+  }
+
+private:
+  using _Context = __format::__compile_time_basic_format_context<_CharT>;
+
+  static constexpr array<__format::__arg_t, sizeof...(_Args)> __types_{
+      __format::__determine_arg_t<_Context, remove_cvref_t<_Args>>()...};
+
+  // TODO FMT remove this work-around when the AIX ICE has been resolved.
+#    if defined(_AIX) && defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 1400
+  template <class _Tp>
+  static constexpr __format::__compile_time_handle<_CharT> __get_handle() {
+    __format::__compile_time_handle<_CharT> __handle;
+    if (__format::__determine_arg_t<_Context, _Tp>() == __format::__arg_t::__handle)
+      __handle.template __enable<_Tp>();
+
+    return __handle;
+  }
+
+  static constexpr array<__format::__compile_time_handle<_CharT>, sizeof...(_Args)> __handles_{
+      __get_handle<_Args>()...};
+#    else
+  static constexpr array<__format::__compile_time_handle<_CharT>, sizeof...(_Args)> __handles_{[] {
+    using _Tp = remove_cvref_t<_Args>;
+    __format::__compile_time_handle<_CharT> __handle;
+    if (__format::__determine_arg_t<_Context, _Tp>() == __format::__arg_t::__handle)
+      __handle.template __enable<_Tp>();
+
+    return __handle;
+  }()...};
+#    endif
+};
+
+template <class... _Args>
+using __format_string_t = __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>...>;
+#endif
+
 template <class _OutIt, class _CharT, class _FormatOutIt>
 requires(output_iterator<_OutIt, const _CharT&>) _LIBCPP_HIDE_FROM_ABI _OutIt
     __vformat_to(
@@ -329,16 +552,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, string_view __fmt, const _Args&... __args) {
-  return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt,
+format_to(_OutIt __out_it, __format_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.__str_,
                            _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, wstring_view __fmt, const _Args&... __args) {
-  return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt,
+format_to(_OutIt __out_it, __wformat_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.__str_,
                            _VSTD::make_wformat_args(__args...));
 }
 #endif
@@ -360,16 +583,16 @@ vformat(wstring_view __fmt, wformat_args __args) {
 #endif
 
 template <class... _Args>
-_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string
-format(string_view __fmt, const _Args&... __args) {
-  return _VSTD::vformat(__fmt, _VSTD::make_format_args(__args...));
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string format(__format_string_t<_Args...> __fmt,
+                                                                                      const _Args&... __args) {
+  return _VSTD::vformat(__fmt.__str_, _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(wstring_view __fmt, const _Args&... __args) {
-  return _VSTD::vformat(__fmt, _VSTD::make_wformat_args(__args...));
+format(__wformat_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::vformat(__fmt.__str_, _VSTD::make_wformat_args(__args...));
 }
 #endif
 
@@ -385,15 +608,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, string_view __fmt, const _Args&... __args) {
-  return _VSTD::__vformat_to_n<format_context>(_VSTD::move(__out_it), __n, __fmt, _VSTD::make_format_args(__args...));
+format_to_n(_OutIt __out_it, iter_
diff erence_t<_OutIt> __n, __format_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::__vformat_to_n<format_context>(_VSTD::move(__out_it), __n, __fmt.__str_, _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, wstring_view __fmt, const _Args&... __args) {
-  return _VSTD::__vformat_to_n<wformat_context>(_VSTD::move(__out_it), __n, __fmt, _VSTD::make_wformat_args(__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,
+            const _Args&... __args) {
+  return _VSTD::__vformat_to_n<wformat_context>(_VSTD::move(__out_it), __n, __fmt.__str_, _VSTD::make_wformat_args(__args...));
 }
 #endif
 
@@ -406,16 +630,16 @@ _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(string_view __fmt,
-                                                                                              const _Args&... __args) {
-  return _VSTD::__vformatted_size(__fmt, basic_format_args{_VSTD::make_format_args(__args...)});
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
+formatted_size(__format_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::__vformatted_size(__fmt.__str_, 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(wstring_view __fmt,
-                                                                                              const _Args&... __args) {
-  return _VSTD::__vformatted_size(__fmt, basic_format_args{_VSTD::make_wformat_args(__args...)});
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
+formatted_size(__wformat_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::__vformatted_size(__fmt.__str_, basic_format_args{_VSTD::make_wformat_args(__args...)});
 }
 #endif
 
@@ -458,17 +682,17 @@ _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt v
 #endif
 
 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, string_view __fmt, const _Args&... __args) {
-  return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt,
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
+format_to(_OutIt __out_it, locale __loc, __format_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.__str_,
                            _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, wstring_view __fmt, const _Args&... __args) {
-  return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt,
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
+format_to(_OutIt __out_it, locale __loc, __wformat_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.__str_,
                            _VSTD::make_wformat_args(__args...));
 }
 #endif
@@ -492,17 +716,18 @@ vformat(locale __loc, wstring_view __fmt, wformat_args __args) {
 #endif
 
 template <class... _Args>
-_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string
-format(locale __loc, string_view __fmt, const _Args&... __args) {
-  return _VSTD::vformat(_VSTD::move(__loc), __fmt,
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string format(locale __loc,
+                                                                                      __format_string_t<_Args...> __fmt,
+                                                                                      const _Args&... __args) {
+  return _VSTD::vformat(_VSTD::move(__loc), __fmt.__str_,
                         _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, wstring_view __fmt, const _Args&... __args) {
-  return _VSTD::vformat(_VSTD::move(__loc), __fmt,
+format(locale __loc, __wformat_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::vformat(_VSTD::move(__loc), __fmt.__str_,
                         _VSTD::make_wformat_args(__args...));
 }
 #endif
@@ -520,16 +745,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, string_view __fmt, const _Args&... __args) {
-  return _VSTD::__vformat_to_n<format_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt,
+format_to_n(_OutIt __out_it, iter_
diff erence_t<_OutIt> __n, locale __loc, __format_string_t<_Args...> __fmt,
+            const _Args&... __args) {
+  return _VSTD::__vformat_to_n<format_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_,
                                                _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, wstring_view __fmt, const _Args&... __args) {
-  return _VSTD::__vformat_to_n<wformat_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt,
+format_to_n(_OutIt __out_it, iter_
diff erence_t<_OutIt> __n, locale __loc, __wformat_string_t<_Args...> __fmt,
+            const _Args&... __args) {
+  return _VSTD::__vformat_to_n<wformat_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_,
                                                 _VSTD::make_wformat_args(__args...));
 }
 #endif
@@ -544,18 +771,16 @@ _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,
-                                                                                              string_view __fmt,
-                                                                                              const _Args&... __args) {
-  return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt, basic_format_args{_VSTD::make_format_args(__args...)});
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
+formatted_size(locale __loc, __format_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.__str_, 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,
-                                                                                              wstring_view __fmt,
-                                                                                              const _Args&... __args) {
-  return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt, basic_format_args{_VSTD::make_wformat_args(__args...)});
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
+formatted_size(locale __loc, __wformat_string_t<_Args...> __fmt, const _Args&... __args) {
+  return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.__str_, basic_format_args{_VSTD::make_wformat_args(__args...)});
 }
 #endif
 

diff  --git a/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp
index 92f1f4b122a54..f377096c94c4d 100644
--- a/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp
@@ -16,9 +16,9 @@
 // <format>
 
 // template<class... Args>
-//   string format(const locale& loc, string_view fmt, const Args&... args);
+//   string format(const locale& loc, format-string<Args...> fmt, const Args&... args);
 // template<class... Args>
-//   wstring format(const locale& loc, wstring_view fmt, const Args&... args);
+//   wstring format(const locale& loc, wformat-string<Args...> fmt, const Args&... args);
 
 #include <format>
 #include <cassert>
@@ -39,30 +39,11 @@ auto test = []<string_literal fmt, class CharT, class... Args>(std::basic_string
   assert(out == expected);
 };
 
-auto test_exception = []<class CharT, class... Args>(std::string_view what, std::basic_string_view<CharT> fmt,
-                                                     const Args&... args) {
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    std::format(std::locale(), fmt, args...);
-    if constexpr (std::same_as<CharT, char>)
-      std::cerr << "\nFormat string   " << fmt << "\nDidn't throw an exception.\n";
-    assert(false);
-  } catch (const std::format_error& e) {
-#  ifdef _LIBCPP_VERSION
-    if constexpr (std::same_as<CharT, char>)
-      if (e.what() != what)
-        std::cerr << "\nFormat string   " << fmt << "\nExpected exception " << what << "\nActual exception   "
-                  << e.what() << '\n';
-#  endif
-    LIBCPP_ASSERT(e.what() == what);
-    return;
-  }
-  assert(false);
-#else
-  (void)what;
-  (void)fmt;
-  (void)sizeof...(args);
-#endif
+auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, const Args&...) {
+  // After P2216 most exceptions thrown by std::format become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in format.locale.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
 };
 
 int main(int, char**) {

diff  --git a/libcxx/test/std/utilities/format/format.functions/format.locale.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format.locale.verify.cpp
new file mode 100644
index 0000000000000..5e52e41298199
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/format.locale.verify.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+// 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: no-localization
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
+
+// Basic test to validate ill-formed code is properly detected.
+
+// <format>
+
+// template<class... Args>
+//   string format(const locale& loc, format-string<Args...> fmt, const Args&... args);
+// template<class... Args>
+//   wstring format(const locale& loc, wformat-string<Args...> fmt, const Args&... args);
+
+#include <format>
+#include <locale>
+
+#include "test_macros.h"
+
+// clang-format off
+
+void f() {
+  std::format(std::locale(), "{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), "}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), "{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), "{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), "{:-}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), "{:#}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), "{:L}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), "{0:{0}}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), "{:.42d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), "{:d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::format(std::locale(), L"{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), L"}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), L"{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), L"{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), L"{:-}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), L"{:#}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), L"{:L}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), L"{0:{0}}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), L"{:.42d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(std::locale(), L"{:d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+#endif
+}

diff  --git a/libcxx/test/std/utilities/format/format.functions/format.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format.pass.cpp
index ed837f22be9e7..77e086fcb54d0 100644
--- a/libcxx/test/std/utilities/format/format.functions/format.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format.pass.cpp
@@ -19,9 +19,9 @@
 // <format>
 
 // template<class... Args>
-//   string format(string_view fmt, const Args&... args);
+//   string format(format-string<Args...> fmt, const Args&... args);
 // template<class... Args>
-//   wstring format(wstring_view fmt, const Args&... args);
+//   wstring format(wformat-string<Args...> fmt, const Args&... args);
 
 #include <format>
 #include <cassert>
@@ -48,31 +48,11 @@ auto test = []<string_literal fmt, class CharT, class... Args>(std::basic_string
   assert(out == expected);
 };
 
-auto test_exception = []<class CharT, class... Args>(std::string_view what, std::basic_string_view<CharT> fmt,
-                                                     const Args&... args) {
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    std::format(fmt, args...);
-#  ifndef TEST_HAS_NO_LOCALIZATION
-    if constexpr (std::same_as<CharT, char>)
-      std::cerr << "\nFormat string   " << fmt << "\nDidn't throw an exception.\n";
-#  endif
-    assert(false);
-  } catch (const std::format_error& e) {
-    if constexpr (std::same_as<CharT, char>)
-#  if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_LOCALIZATION)
-      if (e.what() != what)
-        std::cerr << "\nFormat string   " << fmt << "\nExpected exception " << what << "\nActual exception   "
-                  << e.what() << '\n';
-#  endif
-    LIBCPP_ASSERT(e.what() == what);
-    return;
-  }
-  assert(false);
-#endif
-  (void)what;
-  (void)fmt;
-  (void)sizeof...(args);
+auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, const Args&...) {
+  // After P2216 most exceptions thrown by std::format become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in format.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
 };
 
 int main(int, char**) {

diff  --git a/libcxx/test/std/utilities/format/format.functions/format.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format.verify.cpp
new file mode 100644
index 0000000000000..0febf0e073f4c
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/format.verify.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+// 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
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
+
+// Basic test to validate ill-formed code is properly detected.
+
+// <format>
+
+// template<class... Args>
+//   string format(format-string<Args...> fmt, const Args&... args);
+// template<class... Args>
+//   wstring format(wformat-string<Args...> fmt, const Args&... args);
+
+#include <format>
+
+#include "test_macros.h"
+
+// clang-format off
+
+void f() {
+  std::format("{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format("}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format("{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format("{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format("{:-}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format("{:#}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format("{:L}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format("{0:{0}}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format("{:.42d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format("{:d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::format(L"{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(L"}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(L"{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(L"{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(L"{:-}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(L"{:#}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(L"{:L}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(L"{0:{0}}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(L"{:.42d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format(L"{:d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+#endif
+}

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
index e45331b1dd186..3f435cb95789a 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
@@ -17,10 +17,10 @@
 
 // template<class Out, class... Args>
 //   Out format_to(Out out, const locale& loc,
-//                 string_view fmt, const Args&... args);
+//                 format-string<Args...> fmt, const Args&... args);
 // template<class Out, class... Args>
 //   Out format_to(Out out, const locale& loc,
-//                 wstring_view fmt, const Args&... args);
+//                 wformat-string<Args...> fmt, const Args&... args);
 
 #include <format>
 #include <algorithm>
@@ -60,23 +60,11 @@ auto test = []<string_literal fmt, class CharT, class... Args>(std::basic_string
   }
 };
 
-auto test_exception = []<class CharT, class... Args>(std::string_view what, std::basic_string_view<CharT> fmt,
-                                                     const Args&... args) {
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    std::basic_string<CharT> out;
-    std::format_to(std::back_inserter(out), std::locale(), fmt, args...);
-    assert(false);
-  } catch (const std::format_error& e) {
-    LIBCPP_ASSERT(e.what() == what);
-    return;
-  }
-  assert(false);
-#else
-  (void)what;
-  (void)fmt;
-  (void)sizeof...(args);
-#endif
+auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, const Args&...) {
+  // After P2216 most exceptions thrown by std::format_to become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in format_to.locale.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
 };
 
 int main(int, char**) {

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_to.locale.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.locale.verify.cpp
new file mode 100644
index 0000000000000..ce57fdc83f5ba
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/format_to.locale.verify.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+// 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: no-localization
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
+
+// Basic test to validate ill-formed code is properly detected.
+
+// <format>
+
+// template<class Out, class... Args>
+//   Out format_to(Out out, const locale& loc,
+//                 format-string<Args...> fmt, const Args&... args);
+// template<class Out, class... Args>
+//   Out format_to(Out out, const locale& loc,
+//                 wformat-string<Args...> fmt, const Args&... args);
+
+#include <format>
+#include <locale>
+
+#include "test_macros.h"
+
+extern char* out;
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+extern wchar_t* wout;
+#endif
+
+// clang-format off
+
+void f() {
+  std::format_to(out, std::locale(), "{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, std::locale(), "}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, std::locale(), "{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, std::locale(), "{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, std::locale(), "{:-}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, std::locale(), "{:#}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, std::locale(), "{:L}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, std::locale(), "{0:{0}}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, std::locale(), "{:.42d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, std::locale(), "{:d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::format_to(wout, std::locale(), L"{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, std::locale(), L"}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, std::locale(), L"{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, std::locale(), L"{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, std::locale(), L"{:-}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, std::locale(), L"{:#}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, std::locale(), L"{:L}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, std::locale(), L"{0:{0}}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, std::locale(), L"{:.42d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, std::locale(), L"{:d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+#endif
+}

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp
index 9392844b283a1..3aaf287830efa 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp
@@ -15,9 +15,9 @@
 // <format>
 
 // template<class Out, class... Args>
-//   Out format_to(Out out, string_view fmt, const Args&... args);
+//   Out format_to(Out out, format-string<Args...> fmt, const Args&... args);
 // template<class Out, class... Args>
-//   Out format_to(Out out, wstring_view fmt, const Args&... args);
+//   Out format_to(Out out, wformat-string<Args...> fmt, const Args&... args);
 
 #include <format>
 #include <algorithm>
@@ -57,23 +57,11 @@ auto test = []<string_literal fmt, class CharT, class... Args>(std::basic_string
   }
 };
 
-auto test_exception = []<class CharT, class... Args>(std::string_view what, std::basic_string_view<CharT> fmt,
-                                                     const Args&... args) {
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    std::basic_string<CharT> out;
-    std::format_to(std::back_inserter(out), fmt, args...);
-    assert(false);
-  } catch (const std::format_error& e) {
-    LIBCPP_ASSERT(e.what() == what);
-    return;
-  }
-  assert(false);
-#else
-  (void)what;
-  (void)fmt;
-  (void)sizeof...(args);
-#endif
+auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, const Args&...) {
+  // After P2216 most exceptions thrown by std::format become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in format.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
 };
 
 int main(int, char**) {

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_to.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.verify.cpp
new file mode 100644
index 0000000000000..3bffcce2a7610
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/format_to.verify.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+// 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
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
+
+// <format>
+
+// Basic test to validate ill-formed code is properly detected.
+
+// template<class Out, class... Args>
+//   Out format_to(Out out, format-string<Args...> fmt, const Args&... args);
+// template<class Out, class... Args>
+//   Out format_to(Out out, wformat-string<Args...> fmt, const Args&... args);
+
+#include <format>
+
+#include "test_macros.h"
+
+extern char* out;
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+extern wchar_t* wout;
+#endif
+
+// clang-format off
+
+void f() {
+  std::format_to(out, "{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, "}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, "{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, "{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, "{:-}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, "{:#}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, "{:L}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, "{0:{0}}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, "{:.42d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(out, "{:d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::format_to(wout, L"{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, L"}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, L"{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, L"{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, L"{:-}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, L"{:#}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, L"{:L}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, L"{0:{0}}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, L"{:.42d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to(wout, L"{:d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+#endif
+}

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp
index 6c2f2c530f1cb..f3ae11b4a6ca1 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp
@@ -17,11 +17,11 @@
 
 // template<class Out, class... Args>
 //   format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
-//                                       const locale& loc, string_view fmt,
+//                                       const locale& loc, format-string<Args...> fmt,
 //                                       const Args&... args);
 // template<class Out, class... Args>
 //   format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
-//                                       const locale& loc, wstring_view fmt,
+//                                       const locale& loc, wformat-string<Args...> fmt,
 //                                       const Args&... args);
 
 #include <format>
@@ -98,23 +98,11 @@ auto test = []<string_literal fmt, class CharT, class... Args>(std::basic_string
   }
 };
 
-auto test_exception = []<class CharT, class... Args>(std::string_view what, std::basic_string_view<CharT> fmt,
-                                                     const Args&... args) {
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    std::basic_string<CharT> out;
-    std::format_to_n(std::back_inserter(out), 0, std::locale(), fmt, args...);
-    assert(false);
-  } catch (const std::format_error& e) {
-    LIBCPP_ASSERT(e.what() == what);
-    return;
-  }
-  assert(false);
-#else
-  (void)what;
-  (void)fmt;
-  (void)sizeof...(args);
-#endif
+auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, const Args&...) {
+  // After P2216 most exceptions thrown by std::format_to_n become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in format_to_n.locale.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
 };
 
 int main(int, char**) {

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.verify.cpp
new file mode 100644
index 0000000000000..0302d2496c9e6
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.verify.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+// 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: no-localization
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
+
+// Basic test to validate ill-formed code is properly detected.
+
+// <format>
+
+// template<class Out, class... Args>
+//   format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
+//                                       const locale& loc, format-string<Args...> fmt,
+//                                       const Args&... args);
+// template<class Out, class... Args>
+//   format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
+//                                       const locale& loc, wformat-string<Args...> fmt,
+//                                       const Args&... args);
+
+#include <format>
+#include <locale>
+
+#include "test_macros.h"
+
+extern char* out;
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+extern wchar_t* wout;
+#endif
+
+// clang-format off
+
+void f() {
+  std::format_to_n(out, 42, std::locale(), "{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, std::locale(), "}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, std::locale(), "{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, std::locale(), "{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, std::locale(), "{:-}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, std::locale(), "{:#}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, std::locale(), "{:L}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, std::locale(), "{0:{0}}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, std::locale(), "{:.42d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, std::locale(), "{:d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::format_to_n(wout, 42, std::locale(), L"{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, std::locale(), L"}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, std::locale(), L"{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, std::locale(), L"{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, std::locale(), L"{:-}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, std::locale(), L"{:#}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, std::locale(), L"{:L}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, std::locale(), L"{0:{0}}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, std::locale(), L"{:.42d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, std::locale(), L"{:d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+#endif
+}

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp
index dd60b047ca21e..24d2466f60d78 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp
@@ -16,10 +16,10 @@
 
 // template<class Out, class... Args>
 //   format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
-//                                       string_view fmt, const Args&... args);
+//                                       format-string<Args...> fmt, const Args&... args);
 // template<class Out, class... Args>
 //   format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
-//                                       wstring_view fmt, const Args&... args);
+//                                       wformat-string<Args...> fmt, const Args&... args);
 
 #include <format>
 #include <algorithm>
@@ -91,23 +91,11 @@ auto test = []<string_literal fmt, class CharT, class... Args>(std::basic_string
   }
 };
 
-auto test_exception = []<class CharT, class... Args>(std::string_view what, std::basic_string_view<CharT> fmt,
-                                                     const Args&... args) {
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    std::basic_string<CharT> out;
-    std::format_to_n(std::back_inserter(out), 0, fmt, args...);
-    assert(false);
-  } catch (const std::format_error& e) {
-    LIBCPP_ASSERT(e.what() == what);
-    return;
-  }
-  assert(false);
-#else
-  (void)what;
-  (void)fmt;
-  (void)sizeof...(args);
-#endif
+auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, const Args&...) {
+  // After P2216 most exceptions thrown by std::format_to_n become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in format_to_n.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
 };
 
 int main(int, char**) {

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_to_n.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format_to_n.verify.cpp
new file mode 100644
index 0000000000000..0150e199f762d
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/format_to_n.verify.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+// 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
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
+
+// Basic test to validate ill-formed code is properly detected.
+
+// <format>
+
+// template<class Out, class... Args>
+//   format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
+//                                       format-string<Args...> fmt, const Args&... args);
+// template<class Out, class... Args>
+//   format_to_n_result<Out> format_to_n(Out out, iter_
diff erence_t<Out> n,
+//                                       wformat-string<Args...> fmt, const Args&... args);
+
+#include <format>
+
+#include "test_macros.h"
+
+extern char* out;
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+extern wchar_t* wout;
+#endif
+
+// clang-format off
+
+void f() {
+  std::format_to_n(out, 42, "{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, "}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, "{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, "{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, "{:-}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, "{:#}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, "{:L}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, "{0:{0}}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, "{:.42d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(out, 42, "{:d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::format_to_n(wout, 42, L"{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, L"}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, L"{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, L"{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, L"{:-}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, L"{:#}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, L"{:L}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, L"{0:{0}}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, L"{:.42d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::format_to_n(wout, 42, L"{:d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+#endif
+}

diff  --git a/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp
index a487d558ebf44..094d1011face7 100644
--- a/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp
@@ -17,10 +17,10 @@
 
 // template<class... Args>
 //   size_t formatted_size(const locale& loc,
-//                         string_view fmt, const Args&... args);
+//                         format-string<Args...> fmt, const Args&... args);
 // template<class... Args>
 //   size_t formatted_size(const locale& loc,
-//                         wstring_view fmt, const Args&... args);
+//                         wformat-string<Args...> fmt, const Args&... args);
 
 #include <format>
 #include <cassert>
@@ -36,22 +36,11 @@ auto test = []<string_literal fmt, class CharT, class... Args>(std::basic_string
   assert(size == expected.size());
 };
 
-auto test_exception = []<class CharT, class... Args>(std::string_view what, std::basic_string_view<CharT> fmt,
-                                                     const Args&... args) {
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    std::formatted_size(std::locale(), fmt, args...);
-    assert(false);
-  } catch (const std::format_error& e) {
-    LIBCPP_ASSERT(e.what() == what);
-    return;
-  }
-  assert(false);
-#else
-  (void)what;
-  (void)fmt;
-  (void)sizeof...(args);
-#endif
+auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, const Args&...) {
+  // After P2216 most exceptions thrown by std::formatted_siz3 become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in formatted_size.locale.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
 };
 
 int main(int, char**) {

diff  --git a/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.verify.cpp b/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.verify.cpp
new file mode 100644
index 0000000000000..6aaffa4d8d926
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.verify.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+// 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: no-localization
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
+
+// Basic test to validate ill-formed code is properly detected.
+
+// <format>
+
+// template<class... Args>
+//   size_t formatted_size(const locale& loc,
+//                         format-string<Args...> fmt, const Args&... args);
+// template<class... Args>
+//   size_t formatted_size(const locale& loc,
+//                         wformat-string<Args...> fmt, const Args&... args);
+
+#include <format>
+#include <locale>
+
+#include "test_macros.h"
+
+// clang-format off
+
+void f() {
+  std::formatted_size(std::locale(), "{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), "}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), "{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), "{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), "{:-}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), "{:#}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), "{:L}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), "{0:{0}}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), "{:.42d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), "{:d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::formatted_size(std::locale(), L"{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), L"}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), L"{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), L"{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), L"{:-}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), L"{:#}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), L"{:L}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), L"{0:{0}}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), L"{:.42d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(std::locale(), L"{:d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+#endif
+}

diff  --git a/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp b/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp
index 032b9b6b9ddab..250f24e657f05 100644
--- a/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp
@@ -15,9 +15,9 @@
 // <format>
 
 // template<class... Args>
-//   size_t formatted_size(string_view fmt, const Args&... args);
+//   size_t formatted_size(format-string<Args...> fmt, const Args&... args);
 // template<class... Args>
-//   size_t formatted_size(wstring_view fmt, const Args&... args);
+//   size_t formatted_size(wformat-string<Args...> fmt, const Args&... args);
 
 #include <format>
 #include <cassert>
@@ -33,22 +33,11 @@ auto test = []<string_literal fmt, class CharT, class... Args>(std::basic_string
   assert(size == expected.size());
 };
 
-auto test_exception = []<class CharT, class... Args>(std::string_view what, std::basic_string_view<CharT> fmt,
-                                                     const Args&... args) {
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    std::formatted_size(fmt, args...);
-    assert(false);
-  } catch (const std::format_error& e) {
-    LIBCPP_ASSERT(e.what() == what);
-    return;
-  }
-  assert(false);
-#else
-  (void)what;
-  (void)fmt;
-  (void)sizeof...(args);
-#endif
+auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, const Args&...) {
+  // After P2216 most exceptions thrown by std::formatted_siz3 become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in formatted_size.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
 };
 
 int main(int, char**) {

diff  --git a/libcxx/test/std/utilities/format/format.functions/formatted_size.verify.cpp b/libcxx/test/std/utilities/format/format.functions/formatted_size.verify.cpp
new file mode 100644
index 0000000000000..1f5451cc3dad3
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/formatted_size.verify.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+// 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
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
+
+// Basic test to validate ill-formed code is properly detected.
+
+// <format>
+
+// template<class... Args>
+//   size_t formatted_size(format-string<Args...> fmt, const Args&... args);
+// template<class... Args>
+//   size_t formatted_size(wformat-string<Args...> fmt, const Args&... args);
+
+#include <format>
+
+#include "test_macros.h"
+
+// clang-format off
+
+void f() {
+  std::formatted_size("{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size("}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size("{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size("{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size("{:-}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size("{:#}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size("{:L}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size("{0:{0}}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size("{:.42d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size("{:d}", "Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::formatted_size(L"{"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(L"}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(L"{}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(L"{0}"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(L"{:-}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(L"{:#}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(L"{:L}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(L"{0:{0}}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(L"{:.42d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+
+  std::formatted_size(L"{:d}", L"Forty-two"); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
+  // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
+#endif
+}


        


More information about the libcxx-commits mailing list