Index: include/llvm/Support/Error.h =================================================================== --- include/llvm/Support/Error.h +++ include/llvm/Support/Error.h @@ -302,6 +302,14 @@ return Tmp; } + friend raw_ostream &operator<<(raw_ostream &OS, const Error &E) { + if (auto P = E.getPtr()) + P->log(OS); + else + OS << "success"; + return OS; + } + ErrorInfoBase *Payload = nullptr; }; Index: include/llvm/Support/FormatVariadic.h =================================================================== --- include/llvm/Support/FormatVariadic.h +++ include/llvm/Support/FormatVariadic.h @@ -237,6 +237,8 @@ // for type T containing a method whose signature is: // void format(const T &Obj, raw_ostream &Stream, StringRef Options) // Then this method is invoked as described in Step 1. +// 3. If an appropriate operator<< for raw_ostream exists, it will be used. +// For this to work, (raw_ostream& << const T&) must return raw_ostream&. // // If a match cannot be found through either of the above methods, a compiler // error is generated. @@ -258,13 +260,6 @@ 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: include/llvm/Support/FormatVariadicDetails.h =================================================================== --- include/llvm/Support/FormatVariadicDetails.h +++ include/llvm/Support/FormatVariadicDetails.h @@ -38,6 +38,17 @@ } }; +template +class stream_operator_format_adapter : public format_adapter { + T Item; + +public: + explicit stream_operator_format_adapter(T &&Item) + : Item(std::forward(Item)) {} + + void format(llvm::raw_ostream &S, StringRef Options) override { S << Item; } +}; + template class missing_format_adapter; // Test if format_provider is defined on T and contains a member function @@ -59,6 +70,23 @@ (sizeof(test>(nullptr)) == 1); }; +// Test if raw_ostream& << T -> raw_ostream& is findable via ADL. +template class has_StreamOperator { +public: + using ConstRefT = const typename std::decay::type &; + + template + static char test(typename std::enable_if< + std::is_same() + << std::declval()), + llvm::raw_ostream &>::value, + int *>::type); + + template static double test(...); + + static bool const value = (sizeof(test(nullptr)) == 1); +}; + // Simple template that decides whether a type T should use the member-function // based format() invocation. template @@ -77,15 +105,24 @@ bool, !uses_format_member::value && has_FormatProvider::value> { }; +// Simple template that decides whether a type T should use the operator<< +// based format() invocation. This takes last priority. +template +struct uses_stream_operator + : public std::integral_constant::value && + !uses_format_provider::value && + has_StreamOperator::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 struct uses_missing_provider - : public std::integral_constant::value && - !uses_format_provider::value> {}; + : public std::integral_constant::value && + !uses_format_provider::value && + !uses_stream_operator::value> { +}; template typename std::enable_if::value, T>::type @@ -100,6 +137,13 @@ return provider_format_adapter(std::forward(Item)); } +template +typename std::enable_if::value, + stream_operator_format_adapter>::type +build_format_adapter(T &&Item) { + return stream_operator_format_adapter(std::forward(Item)); +} + template typename std::enable_if::value, missing_format_adapter>::type Index: unittests/Support/ErrorTest.cpp =================================================================== --- unittests/Support/ErrorTest.cpp +++ unittests/Support/ErrorTest.cpp @@ -716,6 +716,25 @@ 0); } +TEST(Error, Stream) { + { + Error OK = Error::success(); + std::string Buf; + llvm::raw_string_ostream S(Buf); + S << OK; + EXPECT_EQ("success", S.str()); + consumeError(std::move(OK)); + } + { + Error E1 = make_error(0); + std::string Buf; + llvm::raw_string_ostream S(Buf); + S << E1; + EXPECT_EQ("CustomError { 0}", S.str()); + consumeError(std::move(E1)); + } +} + TEST(Error, ErrorMatchers) { EXPECT_THAT_ERROR(Error::success(), Succeeded()); EXPECT_NONFATAL_FAILURE( Index: unittests/Support/FormatVariadicTest.cpp =================================================================== --- unittests/Support/FormatVariadicTest.cpp +++ unittests/Support/FormatVariadicTest.cpp @@ -671,3 +671,12 @@ EXPECT_EQ(0, R.Copied); EXPECT_EQ(0, R.Moved); } + +namespace adl { +struct X {}; +raw_ostream &operator<<(raw_ostream &OS, const X &) { return OS << "X"; } +} // namespace adl +TEST(FormatVariadicTest, FormatStreamable) { + adl::X X; + EXPECT_EQ("X", formatv("{0}", X).str()); +}