Index: llvm/trunk/include/llvm/Support/FormatVariadic.h =================================================================== --- llvm/trunk/include/llvm/Support/FormatVariadic.h +++ llvm/trunk/include/llvm/Support/FormatVariadic.h @@ -230,9 +230,8 @@ // For a given parameter of type T, the following steps are executed in order // until a match is found: // -// 1. If the parameter is of class type, and contains a method -// void format(raw_ostream &Stream, StringRef Options) -// Then this method is invoked to produce the formatted output. The +// 1. If the parameter is of class type, and inherits from format_adapter, +// Then format() is invoked on it to produce the formatted output. The // implementation should write the formatted text into `Stream`. // 2. If there is a suitable template specialization of format_provider<> // for type T containing a method whose signature is: @@ -259,6 +258,13 @@ std::make_tuple(detail::build_format_adapter(std::forward(Vals))...)); } +// Allow a formatv_object to be formatted (no options supported). +template struct format_provider> { + static void format(const formatv_object &V, raw_ostream &OS, StringRef) { + OS << V; + } +}; + } // end namespace llvm #endif // LLVM_SUPPORT_FORMATVARIADIC_H Index: llvm/trunk/include/llvm/Support/FormatVariadicDetails.h =================================================================== --- llvm/trunk/include/llvm/Support/FormatVariadicDetails.h +++ llvm/trunk/include/llvm/Support/FormatVariadicDetails.h @@ -31,7 +31,7 @@ T Item; public: - explicit provider_format_adapter(T &&Item) : Item(Item) {} + explicit provider_format_adapter(T &&Item) : Item(std::forward(Item)) {} void format(llvm::raw_ostream &S, StringRef Options) override { format_provider::type>::format(Item, S, Options); Index: llvm/trunk/unittests/Support/FormatVariadicTest.cpp =================================================================== --- llvm/trunk/unittests/Support/FormatVariadicTest.cpp +++ llvm/trunk/unittests/Support/FormatVariadicTest.cpp @@ -578,3 +578,32 @@ // const Format cvar(1); // EXPECT_EQ("Format", formatv("{0}", cvar).str()); } + +TEST(FormatVariadicTest, FormatFormatvObject) { + EXPECT_EQ("Format", formatv("F{0}t", formatv("o{0}a", "rm")).str()); + EXPECT_EQ("[ ! ]", formatv("[{0,+5}]", formatv("{0,-2}", "!")).str()); +} + +namespace { +struct Recorder { + int Copied = 0, Moved = 0; + Recorder() = default; + Recorder(const Recorder &Copy) : Copied(1 + Copy.Copied), Moved(Copy.Moved) {} + Recorder(const Recorder &&Move) + : Copied(Move.Copied), Moved(1 + Move.Moved) {} +}; +} // namespace +template <> struct llvm::format_provider { + static void format(const Recorder &R, raw_ostream &OS, StringRef style) { + OS << R.Copied << "C " << R.Moved << "M"; + } +}; + +TEST(FormatVariadicTest, CopiesAndMoves) { + Recorder R; + EXPECT_EQ("0C 0M", formatv("{0}", R).str()); + EXPECT_EQ("0C 3M", formatv("{0}", std::move(R)).str()); + EXPECT_EQ("0C 3M", formatv("{0}", Recorder()).str()); + EXPECT_EQ(0, R.Copied); + EXPECT_EQ(0, R.Moved); +}