https://github.com/aengelke updated https://github.com/llvm/llvm-project/pull/207516
>From ef7186efff12c9c51bcf1a14855940e5497e6d8b Mon Sep 17 00:00:00 2001 From: Alexis Engelke <[email protected]> Date: Sat, 4 Jul 2026 15:22:04 +0000 Subject: [PATCH 1/3] [spr] initial version Created using spr 1.3.8-wip --- .../DataFormatters/FormatterBytecode.cpp | 16 +- .../llvm/DebugInfo/CodeView/Formatters.h | 2 +- .../llvm/DebugInfo/PDB/Native/FormatUtil.h | 2 +- llvm/include/llvm/Support/FormatAdapters.h | 25 ++- llvm/include/llvm/Support/FormatCommon.h | 8 +- llvm/include/llvm/Support/FormatProviders.h | 22 +-- llvm/include/llvm/Support/FormatVariadic.h | 20 +-- .../llvm/Support/FormatVariadicDetails.h | 158 +++++------------- llvm/lib/Support/FormatVariadic.cpp | 2 - llvm/tools/llvm-xray/xray-stacks.cpp | 2 +- llvm/unittests/ADT/TwineTest.cpp | 2 +- llvm/unittests/Support/FormatVariadicTest.cpp | 32 ++-- mlir/include/mlir/TableGen/Format.h | 24 ++- mlir/lib/TableGen/Format.cpp | 12 +- mlir/tools/mlir-tblgen/RewriterGen.cpp | 8 +- 15 files changed, 120 insertions(+), 215 deletions(-) diff --git a/lldb/source/DataFormatters/FormatterBytecode.cpp b/lldb/source/DataFormatters/FormatterBytecode.cpp index 1936524d37dfc..786f9198a53d0 100644 --- a/lldb/source/DataFormatters/FormatterBytecode.cpp +++ b/lldb/source/DataFormatters/FormatterBytecode.cpp @@ -107,26 +107,26 @@ static llvm::Error FormatImpl(DataStack &data) { } using namespace llvm::support::detail; auto arg = data[data.size() - num_args + r.Index]; - auto format = [&](format_adapter &&adapter) { + auto format = [&](FormatFunctorRef &&adapter) { llvm::FmtAlign Align(adapter, r.Where, r.Width, r.Pad); Align.format(os, r.Options); }; if (auto s = std::get_if<std::string>(&arg)) - format(build_format_adapter(s->c_str())); + format(FormatFunctor<const char *>(s->c_str())); else if (auto u = std::get_if<uint64_t>(&arg)) - format(build_format_adapter(u)); + format(FormatFunctor<uint64_t *&>(u)); else if (auto i = std::get_if<int64_t>(&arg)) - format(build_format_adapter(i)); + format(FormatFunctor<int64_t *&>(i)); else if (auto valobj = std::get_if<ValueObjectSP>(&arg)) { if (!valobj->get()) - format(build_format_adapter("null object")); + format(FormatFunctor<const char *>("null object")); else - format(build_format_adapter(valobj->get()->GetValueAsCString())); + format(FormatFunctor<const char *>(valobj->get()->GetValueAsCString())); } else if (auto type = std::get_if<CompilerType>(&arg)) - format(build_format_adapter(type->GetDisplayTypeName())); + format(FormatFunctor<llvm::StringRef>(type->GetDisplayTypeName())); else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&arg)) - format(build_format_adapter(toString(*sel))); + format(FormatFunctor<std::string>(toString(*sel))); } data.Push(s); return llvm::Error::success(); diff --git a/llvm/include/llvm/DebugInfo/CodeView/Formatters.h b/llvm/include/llvm/DebugInfo/CodeView/Formatters.h index bf0340be901e7..dd802d1b144b0 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/Formatters.h +++ b/llvm/include/llvm/DebugInfo/CodeView/Formatters.h @@ -34,7 +34,7 @@ class LLVM_ABI GuidAdapter final : public FormatAdapter<ArrayRef<uint8_t>> { explicit GuidAdapter(ArrayRef<uint8_t> Guid); explicit GuidAdapter(StringRef Guid); - void format(raw_ostream &Stream, StringRef Style) override; + void format(raw_ostream &Stream, StringRef Style); }; } // end namespace detail diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/FormatUtil.h b/llvm/include/llvm/DebugInfo/PDB/Native/FormatUtil.h index 7f70d4157c358..97fb3a12f1501 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/FormatUtil.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/FormatUtil.h @@ -72,7 +72,7 @@ struct EndianAdapter final explicit EndianAdapter(EndianType &&Item) : FormatAdapter<EndianType>(std::move(Item)) {} - void format(llvm::raw_ostream &Stream, StringRef Style) override { + void format(llvm::raw_ostream &Stream, StringRef Style) { format_provider<T>::format(static_cast<T>(this->Item), Stream, Style); } }; diff --git a/llvm/include/llvm/Support/FormatAdapters.h b/llvm/include/llvm/Support/FormatAdapters.h index 91e9c41d8a395..5a22c0bbde8f4 100644 --- a/llvm/include/llvm/Support/FormatAdapters.h +++ b/llvm/include/llvm/Support/FormatAdapters.h @@ -16,8 +16,7 @@ #include "llvm/Support/raw_ostream.h" namespace llvm { -template <typename T> -class FormatAdapter : public support::detail::format_adapter { +template <typename T> class FormatAdapter { protected: explicit FormatAdapter(T &&Item) : Item(std::forward<T>(Item)) {} @@ -36,8 +35,8 @@ template <typename T> class AlignAdapter final : public FormatAdapter<T> { : FormatAdapter<T>(std::forward<T>(Item)), Where(Where), Amount(Amount), Fill(Fill) {} - void format(llvm::raw_ostream &Stream, StringRef Style) override { - auto Adapter = detail::build_format_adapter(std::forward<T>(this->Item)); + void format(llvm::raw_ostream &Stream, StringRef Style) { + auto Adapter = detail::FormatFunctor<T>(std::forward<T>(this->Item)); FmtAlign(Adapter, Where, Amount, Fill).format(Stream, Style); } }; @@ -50,10 +49,10 @@ template <typename T> class PadAdapter final : public FormatAdapter<T> { PadAdapter(T &&Item, size_t Left, size_t Right) : FormatAdapter<T>(std::forward<T>(Item)), Left(Left), Right(Right) {} - void format(llvm::raw_ostream &Stream, StringRef Style) override { - auto Adapter = detail::build_format_adapter(std::forward<T>(this->Item)); + void format(llvm::raw_ostream &Stream, StringRef Style) { + auto Adapter = detail::FormatFunctor<T>(std::forward<T>(this->Item)); Stream.indent(Left); - Adapter.format(Stream, Style); + Adapter(Stream, Style); Stream.indent(Right); } }; @@ -65,10 +64,10 @@ template <typename T> class RepeatAdapter final : public FormatAdapter<T> { RepeatAdapter(T &&Item, size_t Count) : FormatAdapter<T>(std::forward<T>(Item)), Count(Count) {} - void format(llvm::raw_ostream &Stream, StringRef Style) override { - auto Adapter = detail::build_format_adapter(std::forward<T>(this->Item)); + void format(llvm::raw_ostream &Stream, StringRef Style) { + auto Adapter = detail::FormatFunctor<T>(std::forward<T>(this->Item)); for (size_t I = 0; I < Count; ++I) { - Adapter.format(Stream, Style); + Adapter(Stream, Style); } } }; @@ -77,10 +76,8 @@ class ErrorAdapter : public FormatAdapter<Error> { public: ErrorAdapter(Error &&Item) : FormatAdapter(std::move(Item)) {} ErrorAdapter(ErrorAdapter &&) = default; - ~ErrorAdapter() override { consumeError(std::move(Item)); } - void format(llvm::raw_ostream &Stream, StringRef Style) override { - Stream << Item; - } + ~ErrorAdapter() { consumeError(std::move(Item)); } + void format(llvm::raw_ostream &Stream, StringRef Style) { Stream << Item; } }; } // namespace detail } // namespace support diff --git a/llvm/include/llvm/Support/FormatCommon.h b/llvm/include/llvm/Support/FormatCommon.h index eaf291b864c5e..112cf1fd68478 100644 --- a/llvm/include/llvm/Support/FormatCommon.h +++ b/llvm/include/llvm/Support/FormatCommon.h @@ -19,12 +19,12 @@ enum class AlignStyle { Left, Center, Right }; /// Helper class to format to a \p Width wide field, with alignment \p Where /// within that field. struct FmtAlign { - support::detail::format_adapter &Adapter; + support::detail::FormatFunctorRef Adapter; AlignStyle Where; unsigned Width; char Fill; - FmtAlign(support::detail::format_adapter &Adapter, AlignStyle Where, + FmtAlign(support::detail::FormatFunctorRef Adapter, AlignStyle Where, unsigned Width, char Fill = ' ') : Adapter(Adapter), Where(Where), Width(Width), Fill(Fill) {} @@ -35,13 +35,13 @@ struct FmtAlign { // TODO: Make the format method return the number of bytes written, that // way we can also skip the intermediate stream for left-aligned output. if (Width == 0) { - Adapter.format(S, Options); + Adapter(S, Options); return; } SmallString<64> Item; raw_svector_ostream Stream(Item); - Adapter.format(Stream, Options); + Adapter(Stream, Options); if (Width <= Item.size()) { S << Item; return; diff --git a/llvm/include/llvm/Support/FormatProviders.h b/llvm/include/llvm/Support/FormatProviders.h index 78eeec76cf79b..38a203482d594 100644 --- a/llvm/include/llvm/Support/FormatProviders.h +++ b/llvm/include/llvm/Support/FormatProviders.h @@ -325,18 +325,6 @@ struct format_provider< } }; -namespace support { -namespace detail { -template <typename IterT> -using IterValue = typename std::iterator_traits<IterT>::value_type; - -template <typename IterT> -struct range_item_has_provider - : public std::bool_constant< - !support::detail::uses_missing_provider<IterValue<IterT>>::value> {}; -} // namespace detail -} // namespace support - /// Implementation of format_provider<T> for ranges. /// /// This will print an arbitrary range as a delimited sequence of items. @@ -399,8 +387,6 @@ template <typename IterT> class format_provider<llvm::iterator_range<IterT>> { } public: - static_assert(support::detail::range_item_has_provider<IterT>::value, - "Range value_type does not have a format provider!"); static void format(const llvm::iterator_range<IterT> &V, llvm::raw_ostream &Stream, StringRef Style) { StringRef Sep; @@ -409,14 +395,14 @@ template <typename IterT> class format_provider<llvm::iterator_range<IterT>> { auto Begin = V.begin(); auto End = V.end(); if (Begin != End) { - auto Adapter = support::detail::build_format_adapter(*Begin); - Adapter.format(Stream, ArgStyle); + auto Adapter = support::detail::FormatFunctor<decltype(*Begin)>(*Begin); + Adapter(Stream, ArgStyle); ++Begin; } while (Begin != End) { Stream << Sep; - auto Adapter = support::detail::build_format_adapter(*Begin); - Adapter.format(Stream, ArgStyle); + auto Adapter = support::detail::FormatFunctor<decltype(*Begin)>(*Begin); + Adapter(Stream, ArgStyle); ++Begin; } } diff --git a/llvm/include/llvm/Support/FormatVariadic.h b/llvm/include/llvm/Support/FormatVariadic.h index 145fbb47c91b4..322e6e9d8105a 100644 --- a/llvm/include/llvm/Support/FormatVariadic.h +++ b/llvm/include/llvm/Support/FormatVariadic.h @@ -65,11 +65,11 @@ struct ReplacementItem { class formatv_object_base { protected: StringRef Fmt; - ArrayRef<support::detail::format_adapter *> Adapters; + ArrayRef<support::detail::FormatFunctorRef> Adapters; bool Validate; formatv_object_base(StringRef Fmt, - ArrayRef<support::detail::format_adapter *> Adapters, + ArrayRef<support::detail::FormatFunctorRef> Adapters, bool Validate) : Fmt(Fmt), Adapters(Adapters), Validate(Validate) {} @@ -89,9 +89,9 @@ class formatv_object_base { continue; } - auto *W = Adapters[R.Index]; + auto &W = Adapters[R.Index]; - FmtAlign Align(*W, R.Where, R.Width, R.Pad); + FmtAlign Align(W, R.Where, R.Width, R.Pad); Align.format(S, R.Options); } } @@ -122,21 +122,19 @@ template <typename Tuple> class formatv_object : public formatv_object_base { // of the parameters, we have to own the storage for the parameters here, and // have the base class store type-erased pointers into this tuple. Tuple Parameters; - std::array<support::detail::format_adapter *, std::tuple_size<Tuple>::value> + std::array<support::detail::FormatFunctorRef, std::tuple_size<Tuple>::value> ParameterPointers; // The parameters are stored in a std::tuple, which does not provide runtime // indexing capabilities. In order to enable runtime indexing, we use this // structure to put the parameters into a std::array. Since the parameters // are not all the same type, we use some type-erasure by wrapping the - // parameters in a template class that derives from a non-template superclass. - // Essentially, we are converting a std::tuple<Derived<Ts...>> to a - // std::array<Base*>. + // parameters in a template class and refer to them using function_refs. struct create_adapters { template <typename... Ts> - std::array<support::detail::format_adapter *, std::tuple_size<Tuple>::value> + std::array<support::detail::FormatFunctorRef, std::tuple_size<Tuple>::value> operator()(Ts &...Items) { - return {{&Items...}}; + return {{Items...}}; } }; @@ -248,7 +246,7 @@ template <typename Tuple> class formatv_object : public formatv_object_base { template <typename... Ts> inline auto formatv(bool Validate, const char *Fmt, Ts &&...Vals) { auto Params = std::make_tuple( - support::detail::build_format_adapter(std::forward<Ts>(Vals))...); + support::detail::FormatFunctor<Ts>(std::forward<Ts>(Vals))...); return formatv_object<decltype(Params)>(Fmt, std::move(Params), Validate); } diff --git a/llvm/include/llvm/Support/FormatVariadicDetails.h b/llvm/include/llvm/Support/FormatVariadicDetails.h index c0b245e297a58..9fab9b84a3757 100644 --- a/llvm/include/llvm/Support/FormatVariadicDetails.h +++ b/llvm/include/llvm/Support/FormatVariadicDetails.h @@ -10,6 +10,7 @@ #define LLVM_SUPPORT_FORMATVARIADICDETAILS_H #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" @@ -22,134 +23,57 @@ class Error; namespace support { namespace detail { -class LLVM_ABI format_adapter { - virtual void anchor(); -protected: - virtual ~format_adapter() = default; +using FormatFunctorRef = function_ref<void(llvm::raw_ostream &, StringRef)>; -public: - virtual void format(raw_ostream &S, StringRef Options) = 0; -}; - -template <typename T> class provider_format_adapter : public format_adapter { - T Item; - -public: - explicit provider_format_adapter(T &&Item) : Item(std::forward<T>(Item)) {} - - void format(llvm::raw_ostream &S, StringRef Options) override { - format_provider<std::decay_t<T>>::format(Item, S, Options); - } -}; +template <typename T> class FormatFunctor { + // If the caller passed an Error by value, then stream_operator_format_adapter + // would be responsible for consuming it. + // Make the caller opt into this by calling fmt_consume(). + static_assert( + !std::is_same_v<llvm::Error, std::remove_cv_t<T>>, + "llvm::Error-by-value must be wrapped in fmt_consume() for formatv"); -template <typename T> -class stream_operator_format_adapter : public format_adapter { T Item; -public: - explicit stream_operator_format_adapter(T &&Item) - : Item(std::forward<T>(Item)) {} - - void format(llvm::raw_ostream &S, StringRef) override { S << Item; } -}; - -template <typename T> class missing_format_adapter; - -// Test if format_provider<T> is defined on T and contains a member function -// with the signature: -// static void format(const T&, raw_stream &, StringRef); -// -template <class T> class has_FormatProvider { -public: - using Decayed = std::decay_t<T>; - using Signature_format = void (*)(const Decayed &, llvm::raw_ostream &, + using DecayedT = std::decay_t<T>; + using Signature_format = void (*)(const DecayedT &, llvm::raw_ostream &, StringRef); - template <typename U> using check = SameType<Signature_format, &U::format>; - - static constexpr bool value = - llvm::is_detected<check, llvm::format_provider<Decayed>>::value; -}; - -// Test if raw_ostream& << T -> raw_ostream& is findable via ADL. -template <class T> class has_StreamOperator { -public: - using ConstRefT = const std::decay_t<T> &; - template <typename U> - static auto test(int) - -> std::is_same<decltype(std::declval<llvm::raw_ostream &>() - << std::declval<U>()), - llvm::raw_ostream &>; - - template <typename U> static auto test(...) -> std::false_type; + using MemberFormatCheck = decltype(std::declval<U>().format( + std::declval<llvm::raw_ostream &>(), std::declval<llvm::StringRef>())); + template <typename U> + using StaticFormatCheck = SameType<Signature_format, &U::format>; + template <typename U> + using StreamCheck = std::is_same<decltype(std::declval<llvm::raw_ostream &>() + << std::declval<U>()), + llvm::raw_ostream &>; - static constexpr bool value = decltype(test<ConstRefT>(0))::value; +public: + static constexpr bool HasMemberProvider = + llvm::is_detected<MemberFormatCheck, DecayedT>::value; + static constexpr bool HasFormatProvider = + llvm::is_detected<StaticFormatCheck, + llvm::format_provider<DecayedT>>::value; + static constexpr bool HasStreamProvider = + llvm::is_detected<StreamCheck, DecayedT>::value; + + static_assert(HasMemberProvider || HasFormatProvider || HasStreamProvider, + "type has no format provider"); + + explicit FormatFunctor(T &&Item) : Item(std::forward<T>(Item)) {} + + void operator()(llvm::raw_ostream &S, StringRef Options) { + if constexpr (HasMemberProvider) + Item.format(S, Options); + else if constexpr (HasFormatProvider) + format_provider<DecayedT>::format(Item, S, Options); + else if constexpr (HasStreamProvider) + S << Item; + } }; -// Simple template that decides whether a type T should use the member-function -// based format() invocation. -template <typename T> -struct uses_format_member - : public std::is_base_of<format_adapter, std::remove_reference_t<T>> {}; - -// Simple template that decides whether a type T should use the format_provider -// based format() invocation. The member function takes priority, so this test -// will only be true if there is not ALSO a format member. -template <typename T> -struct uses_format_provider - : public std::bool_constant<!uses_format_member<T>::value && - has_FormatProvider<T>::value> {}; - -// Simple template that decides whether a type T should use the operator<< -// based format() invocation. This takes last priority. -template <typename T> -struct uses_stream_operator - : public std::bool_constant<!uses_format_member<T>::value && - !uses_format_provider<T>::value && - has_StreamOperator<T>::value> {}; - -// Simple template that decides whether a type T has neither a member-function -// nor format_provider based implementation that it can use. Mostly used so -// that the compiler spits out a nice diagnostic when a type with no format -// implementation can be located. -template <typename T> -struct uses_missing_provider - : public std::bool_constant<!uses_format_member<T>::value && - !uses_format_provider<T>::value && - !uses_stream_operator<T>::value> {}; - -template <typename T> -std::enable_if_t<uses_format_member<T>::value, T> -build_format_adapter(T &&Item) { - return std::forward<T>(Item); -} - -template <typename T> -std::enable_if_t<uses_format_provider<T>::value, provider_format_adapter<T>> -build_format_adapter(T &&Item) { - return provider_format_adapter<T>(std::forward<T>(Item)); -} - -template <typename T> -std::enable_if_t<uses_stream_operator<T>::value, - stream_operator_format_adapter<T>> -build_format_adapter(T &&Item) { - // If the caller passed an Error by value, then stream_operator_format_adapter - // would be responsible for consuming it. - // Make the caller opt into this by calling fmt_consume(). - static_assert( - !std::is_same_v<llvm::Error, std::remove_cv_t<T>>, - "llvm::Error-by-value must be wrapped in fmt_consume() for formatv"); - return stream_operator_format_adapter<T>(std::forward<T>(Item)); -} - -template <typename T> -std::enable_if_t<uses_missing_provider<T>::value, missing_format_adapter<T>> -build_format_adapter(T &&) { - return missing_format_adapter<T>(); -} } // namespace detail } // namespace support } // namespace llvm diff --git a/llvm/lib/Support/FormatVariadic.cpp b/llvm/lib/Support/FormatVariadic.cpp index f3e8d0a7fe6f3..a853bd357aac3 100644 --- a/llvm/lib/Support/FormatVariadic.cpp +++ b/llvm/lib/Support/FormatVariadic.cpp @@ -222,5 +222,3 @@ formatv_object_base::parseFormatString(StringRef Fmt, size_t NumArgs, #endif // ENABLE_VALIDATION return Replacements; } - -void support::detail::format_adapter::anchor() {} diff --git a/llvm/tools/llvm-xray/xray-stacks.cpp b/llvm/tools/llvm-xray/xray-stacks.cpp index 8101cad28ca1c..28e567cf81c87 100644 --- a/llvm/tools/llvm-xray/xray-stacks.cpp +++ b/llvm/tools/llvm-xray/xray-stacks.cpp @@ -115,7 +115,7 @@ struct format_xray_record : public FormatAdapter<XRayRecord> { explicit format_xray_record(XRayRecord record, const FuncIdConversionHelper &conv) : FormatAdapter<XRayRecord>(std::move(record)), Converter(&conv) {} - void format(raw_ostream &Stream, StringRef Style) override { + void format(raw_ostream &Stream, StringRef Style) { Stream << formatv( "{FuncId: \"{0}\", ThreadId: \"{1}\", RecordType: \"{2}\"}", Converter->SymbolOrNumber(Item.FuncId), Item.TId, diff --git a/llvm/unittests/ADT/TwineTest.cpp b/llvm/unittests/ADT/TwineTest.cpp index d45805f4db7a3..fc1cc3a0e0db0 100644 --- a/llvm/unittests/ADT/TwineTest.cpp +++ b/llvm/unittests/ADT/TwineTest.cpp @@ -119,7 +119,7 @@ TEST(TwineTest, LazyEvaluation) { explicit formatter(int &Count) : FormatAdapter(0), Count(Count) {} int &Count; - void format(raw_ostream &OS, StringRef Style) override { ++Count; } + void format(raw_ostream &OS, StringRef Style) { ++Count; } }; int Count = 0; diff --git a/llvm/unittests/Support/FormatVariadicTest.cpp b/llvm/unittests/Support/FormatVariadicTest.cpp index ea549e7553b0c..afe7c6f6ee6f7 100644 --- a/llvm/unittests/Support/FormatVariadicTest.cpp +++ b/llvm/unittests/Support/FormatVariadicTest.cpp @@ -18,22 +18,28 @@ using namespace llvm; namespace { struct Format : public FormatAdapter<int> { Format(int N) : FormatAdapter<int>(std::move(N)) {} - void format(raw_ostream &OS, StringRef Opt) override { OS << "Format"; } + void format(raw_ostream &OS, StringRef Opt) { OS << "Format"; } }; -using support::detail::uses_format_member; -using support::detail::uses_missing_provider; +using support::detail::FormatFunctor; -static_assert(uses_format_member<Format>::value, ""); -static_assert(uses_format_member<Format &>::value, ""); -static_assert(uses_format_member<Format &&>::value, ""); -static_assert(uses_format_member<const Format>::value, ""); -static_assert(uses_format_member<const Format &>::value, ""); -static_assert(uses_format_member<const volatile Format>::value, ""); -static_assert(uses_format_member<const volatile Format &>::value, ""); +static_assert(FormatFunctor<Format>::HasMemberProvider, ""); +static_assert(FormatFunctor<Format &>::HasMemberProvider, ""); +static_assert(FormatFunctor<Format &&>::HasMemberProvider, ""); +static_assert(FormatFunctor<const Format>::HasMemberProvider, ""); +static_assert(FormatFunctor<const Format &>::HasMemberProvider, ""); +static_assert(FormatFunctor<const volatile Format>::HasMemberProvider, ""); +static_assert(FormatFunctor<const volatile Format &>::HasMemberProvider, ""); -struct NoFormat {}; -static_assert(uses_missing_provider<NoFormat>::value, ""); +struct StreamFormat {}; +// Silence warning about never-called function. +[[maybe_unused]] raw_ostream &operator<<(raw_ostream &, const StreamFormat &); +static_assert(!FormatFunctor<StreamFormat>::HasMemberProvider, ""); +static_assert(!FormatFunctor<StreamFormat>::HasFormatProvider, ""); +static_assert(FormatFunctor<StreamFormat>::HasStreamProvider, ""); +static_assert(!FormatFunctor<int>::HasMemberProvider, ""); +static_assert(FormatFunctor<int>::HasFormatProvider, ""); +static_assert(FormatFunctor<int>::HasStreamProvider, ""); } // Helper to parse format string with no validation. @@ -780,7 +786,7 @@ TEST(FormatVariadicTest, Adapter) { class Negative : public FormatAdapter<int> { public: explicit Negative(int N) : FormatAdapter<int>(std::move(N)) {} - void format(raw_ostream &S, StringRef Options) override { S << -Item; } + void format(raw_ostream &S, StringRef Options) { S << -Item; } }; EXPECT_EQ("-7", formatv("{0}", Negative(7)).str()); diff --git a/mlir/include/mlir/TableGen/Format.h b/mlir/include/mlir/TableGen/Format.h index e9f151e248340..a3c3df487ab83 100644 --- a/mlir/include/mlir/TableGen/Format.h +++ b/mlir/include/mlir/TableGen/Format.h @@ -127,15 +127,15 @@ class FmtObjectBase { // std::vector<Base*>. struct CreateAdapters { template <typename... Ts> - std::vector<llvm::support::detail::format_adapter *> + std::vector<llvm::support::detail::FormatFunctorRef> operator()(Ts &...items) { - return std::vector<llvm::support::detail::format_adapter *>{&items...}; + return std::vector<llvm::support::detail::FormatFunctorRef>{items...}; } }; StringRef fmt; const FmtContext *context; - std::vector<llvm::support::detail::format_adapter *> adapters; + std::vector<llvm::support::detail::FormatFunctorRef> adapters; std::vector<FmtReplacement> replacements; public: @@ -200,8 +200,7 @@ class FmtObject : public FmtObjectBase { class FmtStrVecObject : public FmtObjectBase { public: - using StrFormatAdapter = decltype(llvm::support::detail::build_format_adapter( - std::declval<std::string>())); + using StrFormatAdapter = llvm::support::detail::FormatFunctor<std::string>; FmtStrVecObject(StringRef fmt, const FmtContext *ctx, ArrayRef<std::string> params); @@ -254,19 +253,18 @@ class FmtStrVecObject : public FmtObjectBase { /// in C++ code generation. template <typename... Ts> inline auto tgfmt(StringRef fmt, const FmtContext *ctx, Ts &&...vals) - -> FmtObject< - decltype(std::make_tuple(llvm::support::detail::build_format_adapter( - std::forward<Ts>(vals))...))> { + -> FmtObject<decltype(std::make_tuple( + llvm::support::detail::FormatFunctor<Ts>(std::forward<Ts>(vals))...))> { using ParamTuple = decltype(std::make_tuple( - llvm::support::detail::build_format_adapter(std::forward<Ts>(vals))...)); + llvm::support::detail::FormatFunctor<Ts>(std::forward<Ts>(vals))...)); return FmtObject<ParamTuple>( fmt, ctx, - std::make_tuple(llvm::support::detail::build_format_adapter( - std::forward<Ts>(vals))...)); + std::make_tuple( + llvm::support::detail::FormatFunctor<Ts>(std::forward<Ts>(vals))...)); } -inline FmtStrVecObject tgfmt(StringRef fmt, const FmtContext *ctx, - ArrayRef<std::string> params) { +inline FmtStrVecObject tgfmtv(StringRef fmt, const FmtContext *ctx, + ArrayRef<std::string> params) { return FmtStrVecObject(fmt, ctx, params); } diff --git a/mlir/lib/TableGen/Format.cpp b/mlir/lib/TableGen/Format.cpp index 65f4ad56dd158..2153de2af4797 100644 --- a/mlir/lib/TableGen/Format.cpp +++ b/mlir/lib/TableGen/Format.cpp @@ -183,8 +183,7 @@ void FmtObjectBase::format(raw_ostream &s) const { range = range.drop_front(repl.index); if (repl.end != FmtReplacement::kUnset) range = range.drop_back(adapters.size() - repl.end); - llvm::interleaveComma(range, s, - [&](auto &x) { x->format(s, /*Options=*/""); }); + llvm::interleaveComma(range, s, [&](auto &x) { x(s, /*Options=*/""); }); continue; } @@ -194,7 +193,7 @@ void FmtObjectBase::format(raw_ostream &s) const { s << repl.spec << kMarkerForNoSubst; continue; } - adapters[repl.index]->format(s, /*Options=*/""); + adapters[repl.index](s, /*Options=*/""); } } @@ -203,17 +202,16 @@ FmtStrVecObject::FmtStrVecObject(StringRef fmt, const FmtContext *ctx, : FmtObjectBase(fmt, ctx, params.size()) { parameters.reserve(params.size()); for (std::string p : params) - parameters.push_back( - llvm::support::detail::build_format_adapter(std::move(p))); + parameters.emplace_back(std::move(p)); adapters.reserve(parameters.size()); for (auto &p : parameters) - adapters.push_back(&p); + adapters.push_back(p); } FmtStrVecObject::FmtStrVecObject(FmtStrVecObject &&that) : FmtObjectBase(std::move(that)), parameters(std::move(that.parameters)) { adapters.reserve(parameters.size()); for (auto &p : parameters) - adapters.push_back(&p); + adapters.push_back(p); } diff --git a/mlir/tools/mlir-tblgen/RewriterGen.cpp b/mlir/tools/mlir-tblgen/RewriterGen.cpp index e3043708a46d1..5bc2ac48b4726 100644 --- a/mlir/tools/mlir-tblgen/RewriterGen.cpp +++ b/mlir/tools/mlir-tblgen/RewriterGen.cpp @@ -524,8 +524,8 @@ void PatternEmitter::emitNativeCodeMatch(DagNode tree, StringRef opName, "passing the defining Operation"); auto nativeCodeCall = std::string( - tgfmt(fmt, &fmtCtx.addSubst("_loc", locToUse).withSelf(opName.str()), - static_cast<ArrayRef<std::string>>(capture))); + tgfmtv(fmt, &fmtCtx.addSubst("_loc", locToUse).withSelf(opName.str()), + static_cast<ArrayRef<std::string>>(capture))); emitMatchCheck(opName, formatv("!::mlir::failed({0})", nativeCodeCall), formatv("\"{0} return ::mlir::failure\"", nativeCodeCall)); @@ -1490,8 +1490,8 @@ std::string PatternEmitter::handleReplaceWithNativeCodeCall(DagNode tree, << " replacement: " << attrs[i] << "\n"); } - std::string symbol = tgfmt(fmt, &fmtCtx.addSubst("_loc", locToUse), - static_cast<ArrayRef<std::string>>(attrs)); + std::string symbol = tgfmtv(fmt, &fmtCtx.addSubst("_loc", locToUse), + static_cast<ArrayRef<std::string>>(attrs)); // In general, NativeCodeCall without naming binding don't need this. To // ensure void helper function has been correctly labeled, i.e., use >From 6da8c7d2e2302b4f2e46f3269c35bb5c7e604263 Mon Sep 17 00:00:00 2001 From: Alexis Engelke <[email protected]> Date: Sat, 4 Jul 2026 15:48:05 +0000 Subject: [PATCH 2/3] add template deduction guide Created using spr 1.3.8-wip --- lldb/source/DataFormatters/FormatterBytecode.cpp | 14 +++++++------- llvm/include/llvm/Support/FormatAdapters.h | 6 +++--- llvm/include/llvm/Support/FormatProviders.h | 4 ++-- llvm/include/llvm/Support/FormatVariadic.h | 2 +- llvm/include/llvm/Support/FormatVariadicDetails.h | 2 ++ mlir/include/mlir/TableGen/Format.h | 6 +++--- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lldb/source/DataFormatters/FormatterBytecode.cpp b/lldb/source/DataFormatters/FormatterBytecode.cpp index 786f9198a53d0..2d79641f516f1 100644 --- a/lldb/source/DataFormatters/FormatterBytecode.cpp +++ b/lldb/source/DataFormatters/FormatterBytecode.cpp @@ -113,20 +113,20 @@ static llvm::Error FormatImpl(DataStack &data) { }; if (auto s = std::get_if<std::string>(&arg)) - format(FormatFunctor<const char *>(s->c_str())); + format(FormatFunctor(s->c_str())); else if (auto u = std::get_if<uint64_t>(&arg)) - format(FormatFunctor<uint64_t *&>(u)); + format(FormatFunctor(u)); else if (auto i = std::get_if<int64_t>(&arg)) - format(FormatFunctor<int64_t *&>(i)); + format(FormatFunctor(i)); else if (auto valobj = std::get_if<ValueObjectSP>(&arg)) { if (!valobj->get()) - format(FormatFunctor<const char *>("null object")); + format(FormatFunctor("null object")); else - format(FormatFunctor<const char *>(valobj->get()->GetValueAsCString())); + format(FormatFunctor(valobj->get()->GetValueAsCString())); } else if (auto type = std::get_if<CompilerType>(&arg)) - format(FormatFunctor<llvm::StringRef>(type->GetDisplayTypeName())); + format(FormatFunctor(type->GetDisplayTypeName())); else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&arg)) - format(FormatFunctor<std::string>(toString(*sel))); + format(FormatFunctor(toString(*sel))); } data.Push(s); return llvm::Error::success(); diff --git a/llvm/include/llvm/Support/FormatAdapters.h b/llvm/include/llvm/Support/FormatAdapters.h index 5a22c0bbde8f4..57a9fc03ea51f 100644 --- a/llvm/include/llvm/Support/FormatAdapters.h +++ b/llvm/include/llvm/Support/FormatAdapters.h @@ -36,7 +36,7 @@ template <typename T> class AlignAdapter final : public FormatAdapter<T> { Fill(Fill) {} void format(llvm::raw_ostream &Stream, StringRef Style) { - auto Adapter = detail::FormatFunctor<T>(std::forward<T>(this->Item)); + auto Adapter = detail::FormatFunctor(std::forward<T>(this->Item)); FmtAlign(Adapter, Where, Amount, Fill).format(Stream, Style); } }; @@ -50,7 +50,7 @@ template <typename T> class PadAdapter final : public FormatAdapter<T> { : FormatAdapter<T>(std::forward<T>(Item)), Left(Left), Right(Right) {} void format(llvm::raw_ostream &Stream, StringRef Style) { - auto Adapter = detail::FormatFunctor<T>(std::forward<T>(this->Item)); + auto Adapter = detail::FormatFunctor(std::forward<T>(this->Item)); Stream.indent(Left); Adapter(Stream, Style); Stream.indent(Right); @@ -65,7 +65,7 @@ template <typename T> class RepeatAdapter final : public FormatAdapter<T> { : FormatAdapter<T>(std::forward<T>(Item)), Count(Count) {} void format(llvm::raw_ostream &Stream, StringRef Style) { - auto Adapter = detail::FormatFunctor<T>(std::forward<T>(this->Item)); + auto Adapter = detail::FormatFunctor(std::forward<T>(this->Item)); for (size_t I = 0; I < Count; ++I) { Adapter(Stream, Style); } diff --git a/llvm/include/llvm/Support/FormatProviders.h b/llvm/include/llvm/Support/FormatProviders.h index 38a203482d594..95176333e91e0 100644 --- a/llvm/include/llvm/Support/FormatProviders.h +++ b/llvm/include/llvm/Support/FormatProviders.h @@ -395,13 +395,13 @@ template <typename IterT> class format_provider<llvm::iterator_range<IterT>> { auto Begin = V.begin(); auto End = V.end(); if (Begin != End) { - auto Adapter = support::detail::FormatFunctor<decltype(*Begin)>(*Begin); + auto Adapter = support::detail::FormatFunctor(*Begin); Adapter(Stream, ArgStyle); ++Begin; } while (Begin != End) { Stream << Sep; - auto Adapter = support::detail::FormatFunctor<decltype(*Begin)>(*Begin); + auto Adapter = support::detail::FormatFunctor(*Begin); Adapter(Stream, ArgStyle); ++Begin; } diff --git a/llvm/include/llvm/Support/FormatVariadic.h b/llvm/include/llvm/Support/FormatVariadic.h index 322e6e9d8105a..4c92d45c2f5b9 100644 --- a/llvm/include/llvm/Support/FormatVariadic.h +++ b/llvm/include/llvm/Support/FormatVariadic.h @@ -246,7 +246,7 @@ template <typename Tuple> class formatv_object : public formatv_object_base { template <typename... Ts> inline auto formatv(bool Validate, const char *Fmt, Ts &&...Vals) { auto Params = std::make_tuple( - support::detail::FormatFunctor<Ts>(std::forward<Ts>(Vals))...); + support::detail::FormatFunctor(std::forward<Ts>(Vals))...); return formatv_object<decltype(Params)>(Fmt, std::move(Params), Validate); } diff --git a/llvm/include/llvm/Support/FormatVariadicDetails.h b/llvm/include/llvm/Support/FormatVariadicDetails.h index 9fab9b84a3757..ba919b8896e56 100644 --- a/llvm/include/llvm/Support/FormatVariadicDetails.h +++ b/llvm/include/llvm/Support/FormatVariadicDetails.h @@ -74,6 +74,8 @@ template <typename T> class FormatFunctor { } }; +template <typename T> FormatFunctor(T &&) -> FormatFunctor<T>; + } // namespace detail } // namespace support } // namespace llvm diff --git a/mlir/include/mlir/TableGen/Format.h b/mlir/include/mlir/TableGen/Format.h index a3c3df487ab83..d0f8be28e493b 100644 --- a/mlir/include/mlir/TableGen/Format.h +++ b/mlir/include/mlir/TableGen/Format.h @@ -254,13 +254,13 @@ class FmtStrVecObject : public FmtObjectBase { template <typename... Ts> inline auto tgfmt(StringRef fmt, const FmtContext *ctx, Ts &&...vals) -> FmtObject<decltype(std::make_tuple( - llvm::support::detail::FormatFunctor<Ts>(std::forward<Ts>(vals))...))> { + llvm::support::detail::FormatFunctor(std::forward<Ts>(vals))...))> { using ParamTuple = decltype(std::make_tuple( - llvm::support::detail::FormatFunctor<Ts>(std::forward<Ts>(vals))...)); + llvm::support::detail::FormatFunctor(std::forward<Ts>(vals))...)); return FmtObject<ParamTuple>( fmt, ctx, std::make_tuple( - llvm::support::detail::FormatFunctor<Ts>(std::forward<Ts>(vals))...)); + llvm::support::detail::FormatFunctor(std::forward<Ts>(vals))...)); } inline FmtStrVecObject tgfmtv(StringRef fmt, const FmtContext *ctx, >From 00e30973cb237f7d4179ee81dc321af6f05922de Mon Sep 17 00:00:00 2001 From: Alexis Engelke <[email protected]> Date: Sat, 4 Jul 2026 17:57:15 +0000 Subject: [PATCH 3/3] improve comments Created using spr 1.3.8-wip --- llvm/include/llvm/Support/FormatVariadicDetails.h | 5 ++--- mlir/include/mlir/TableGen/Format.h | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/Support/FormatVariadicDetails.h b/llvm/include/llvm/Support/FormatVariadicDetails.h index ba919b8896e56..62126dc90c0f7 100644 --- a/llvm/include/llvm/Support/FormatVariadicDetails.h +++ b/llvm/include/llvm/Support/FormatVariadicDetails.h @@ -27,9 +27,8 @@ namespace detail { using FormatFunctorRef = function_ref<void(llvm::raw_ostream &, StringRef)>; template <typename T> class FormatFunctor { - // If the caller passed an Error by value, then stream_operator_format_adapter - // would be responsible for consuming it. - // Make the caller opt into this by calling fmt_consume(). + // If the caller passed an Error by value, then we would be responsible for + // consuming it. Make the caller opt into this by calling fmt_consume(). static_assert( !std::is_same_v<llvm::Error, std::remove_cv_t<T>>, "llvm::Error-by-value must be wrapped in fmt_consume() for formatv"); diff --git a/mlir/include/mlir/TableGen/Format.h b/mlir/include/mlir/TableGen/Format.h index d0f8be28e493b..daadf8e636feb 100644 --- a/mlir/include/mlir/TableGen/Format.h +++ b/mlir/include/mlir/TableGen/Format.h @@ -263,6 +263,7 @@ inline auto tgfmt(StringRef fmt, const FmtContext *ctx, Ts &&...vals) llvm::support::detail::FormatFunctor(std::forward<Ts>(vals))...)); } +/// Like tgfmt, but take replacement parameters as an array of strings instead. inline FmtStrVecObject tgfmtv(StringRef fmt, const FmtContext *ctx, ArrayRef<std::string> params) { return FmtStrVecObject(fmt, ctx, params); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
