[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:48 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
+ ,
+ "The required formatter specialization's parse function does not return the required type.");
+
+ else {
+ // During constant evaluation this function is called, but the format
+ // member function has not been evaluated. This means these functions
+ // can't be evaluated at that time.
+ //
+ // Note this else branch should never been taken during constant
+ // eveluation, the static_asserts in one of the branches above should
+ // trigger.
+ constexpr bool __has_format_function = requires(_Formatter& __f, _Tp&& __t, _Context __fc) {
+ { __f.format(__t, __fc) };
+ };
+ constexpr bool __is_format_function_const_qualified = requires(const _Formatter& __cf, _Tp&& __t, _Context __fc) {
+ { __cf.format(__t, __fc) };
+ };
+ constexpr bool __correct_format_function_return_type = requires(_Formatter& __f, _Tp&& __t, _Context __fc) {
+ { __f.format(__t, __fc)->template same_as<typename _Context::iterator> };
+ };
+
+ if constexpr (!__has_format_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 format function taking the proper arguments.");
+ else if constexpr (!__is_format_function_const_qualified)
+ static_assert(
+# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+ sizeof(_Tp) == 0
+# else
+ false
+# endif
+ ,
+ "The required formatter specialization's format function is not const qualified.");
+ else if constexpr (!__correct_format_function_return_type)
+ static_assert(
+# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+ sizeof(_Tp) == 0
+# else
+ false
+# endif
+ ,
+ "The required formatter specialization's format function does not return the required type.");
+
+ else
+ // This should not happen; it makes sure the code is ill-formed.
+ 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 formattable with its context.");
+ }
+}
+
+// The Psuedo constructor is constrained per [format.arg]/4.
----------------
ldionne wrote:
```suggestion
// The pseudo constructor is constrained per [format.arg]/4.
```
https://github.com/llvm/llvm-project/pull/127234
More information about the libcxx-commits
mailing list