[libcxx-commits] [libcxx] [libc++][format] Improves diagnostics. (PR #127234)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Thu Mar 27 10:09:47 PDT 2025


================
@@ -205,6 +208,149 @@ _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp& __valu
     return basic_format_arg<_Context>{__arg, __value};
 }
 
+// Helper function that issues a diagnostic when __formattable_with<_Tp, _Context> is false.
+//
+// Since it's quite easy to make a mistake writing a formatter specialization
+// this function tries to give a better explanation. This should improve the
+// diagnostics when trying to format type that has no properly specialized
+// formatter.
+template <class _Context, class _Tp>
+[[noreturn]] _LIBCPP_HIDE_FROM_ABI constexpr void __diagnose_invalid_formatter() {
+  using _Formatter = typename _Context::template formatter_type<remove_const_t<_Tp>>;
+  constexpr bool __is_disabled =
+      !is_default_constructible_v<_Formatter> && !is_copy_constructible_v<_Formatter> &&
+      !is_move_constructible_v<_Formatter> && !is_copy_assignable_v<_Formatter> && !is_move_assignable_v<_Formatter>;
+  constexpr bool __is_semiregular = semiregular<_Formatter>;
+
+  constexpr bool __has_parse_function =
+      requires(_Formatter& __f, basic_format_parse_context<typename _Context::char_type> __pc) {
+        { __f.parse(__pc) };
+      };
+  constexpr bool __correct_parse_function_return_type =
+      requires(_Formatter& __f, basic_format_parse_context<typename _Context::char_type> __pc) {
+        { __f.parse(__pc) } -> same_as<typename decltype(__pc)::iterator>;
+      };
+
+  // The reason these static_asserts are placed in an if-constexpr-chain is to
+  // only show one error. For example, when the formatter is not specialized it
+  // would show all static_assert messages. With this chain the compiler only
+  // evaluates one static_assert.
+
+  if constexpr (__is_disabled)
+
+    static_assert(
+#  if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+        sizeof(_Tp) == 0
+#  else
+        false
+#  endif
+        ,
+        "The required formatter specialization has not been provided.");
+  else if constexpr (!__is_semiregular)
+    static_assert(
+#  if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+        sizeof(_Tp) == 0
+#  else
+        false
+#  endif
+        ,
+        "The required formatter specialization is not semiregular.");
+
+  else if constexpr (!__has_parse_function)
+    static_assert(
+#  if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+        sizeof(_Tp) == 0
+#  else
+        false
+#  endif
+        ,
+        "The required formatter specialization does not have a parse function taking the proper arguments.");
+  else if constexpr (!__correct_parse_function_return_type)
+    static_assert(
+#  if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+        sizeof(_Tp) == 0
+#  else
+        false
+#  endif
----------------
ldionne wrote:

```suggestion
        sizeof(_Tp) == 0
```

IMO the added complexity of using `sizeof(_Tp) == 0` isn't worth carrying a compiler-dependent diff, even if it goes away soon.

https://github.com/llvm/llvm-project/pull/127234


More information about the libcxx-commits mailing list