diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h --- a/llvm/include/llvm/Support/raw_ostream.h +++ b/llvm/include/llvm/Support/raw_ostream.h @@ -303,6 +303,11 @@ //===--------------------------------------------------------------------===// private: + /// Return the current position within the stream, not counting the bytes + /// currently in the buffer. + virtual uint64_t current_pos() const = 0; + +protected: /// The is the piece of the class that is implemented by subclasses. This /// writes the \p Size bytes starting at /// \p Ptr to the underlying stream. @@ -318,11 +323,6 @@ /// \invariant { Size > 0 } virtual void write_impl(const char *Ptr, size_t Size) = 0; - /// Return the current position within the stream, not counting the bytes - /// currently in the buffer. - virtual uint64_t current_pos() const = 0; - -protected: /// Use the provided buffer as the raw_ostream buffer. This is intended for /// use only by subclasses which can arrange for the output to go directly /// into the desired output buffer, instead of being copied on each flush. @@ -410,9 +410,6 @@ uint64_t pos = 0; - /// See raw_ostream::write_impl. - void write_impl(const char *Ptr, size_t Size) override; - void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override; /// Return the current position within the stream, not counting the bytes @@ -427,6 +424,10 @@ void anchor() override; +protected: + /// See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t Size) override; + public: /// Open the specified file for writing. If an error occurs, information /// about the error is put into EC, and the stream should be immediately @@ -496,12 +497,35 @@ void clear_error() { EC = std::error_code(); } }; +/// A raw_ostream which is tied to another stream. When data is written to the +/// main stream, it automatically flushes the tied-to stream, so that output is +/// not interleaved between the two. base_stream must be a raw_ostream or +/// subclass. +template class tied_raw_ostream : public base_stream { + raw_ostream &TiedStream; + +protected: + /// Flushes the tied-to stream, and then writes to the main stream. + void write_impl(const char *Ptr, size_t Size) override { + TiedStream.flush(); + base_stream::write_impl(Ptr, Size); + } + +public: + /// Construct a stream using \p Args and tie the stream \p TiedStream to it. + template + tied_raw_ostream(raw_ostream &TiedStream, Ts &&... Args) + : base_stream(std::forward(Args)...), TiedStream(TiedStream){}; +}; + /// This returns a reference to a raw_fd_ostream for standard output. Use it /// like: outs() << "foo" << "bar"; raw_fd_ostream &outs(); -/// This returns a reference to a raw_fd_ostream for standard error. Use it -/// like: errs() << "foo" << "bar"; +/// errs() - This returns a reference to a raw_ostream for standard error. +/// Use it like: errs() << "foo" << "bar"; +/// The stream is tied to stdout to ensure stdout is flushed before stderr is +/// written, to ensure the error messages are written in their expected place. raw_fd_ostream &errs(); /// This returns a reference to a raw_ostream which simply discards output. @@ -516,13 +540,14 @@ class raw_string_ostream : public raw_ostream { std::string &OS; - /// See raw_ostream::write_impl. - void write_impl(const char *Ptr, size_t Size) override; - /// Return the current position within the stream, not counting the bytes /// currently in the buffer. uint64_t current_pos() const override { return OS.size(); } +protected: + /// See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t Size) override; + public: explicit raw_string_ostream(std::string &O) : OS(O) { SetUnbuffered(); @@ -545,14 +570,15 @@ class raw_svector_ostream : public raw_pwrite_stream { SmallVectorImpl &OS; - /// See raw_ostream::write_impl. - void write_impl(const char *Ptr, size_t Size) override; - void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override; /// Return the current position within the stream. uint64_t current_pos() const override; +protected: + /// See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t Size) override; + public: /// Construct a new raw_svector_ostream. /// @@ -572,14 +598,16 @@ /// A raw_ostream that discards all output. class raw_null_ostream : public raw_pwrite_stream { - /// See raw_ostream::write_impl. - void write_impl(const char *Ptr, size_t size) override; void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override; /// Return the current position within the stream, not counting the bytes /// currently in the buffer. uint64_t current_pos() const override; +protected: + /// See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t size) override; + public: explicit raw_null_ostream() = default; ~raw_null_ostream() override; diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp --- a/llvm/lib/Support/raw_ostream.cpp +++ b/llvm/lib/Support/raw_ostream.cpp @@ -874,7 +874,7 @@ raw_fd_ostream &llvm::errs() { // Set standard error to be unbuffered by default. - static raw_fd_ostream S(STDERR_FILENO, false, true); + static tied_raw_ostream S(outs(), STDERR_FILENO, false, true); return S; } diff --git a/llvm/unittests/Support/raw_ostream_test.cpp b/llvm/unittests/Support/raw_ostream_test.cpp --- a/llvm/unittests/Support/raw_ostream_test.cpp +++ b/llvm/unittests/Support/raw_ostream_test.cpp @@ -356,4 +356,25 @@ { raw_fd_ostream("-", EC, sys::fs::OpenFlags::OF_None); } { raw_fd_ostream("-", EC, sys::fs::OpenFlags::OF_None); } } + +TEST(tied_raw_ostreamTest, flush_tied_to_stream_on_write) { + std::string TiedToBuffer; + raw_string_ostream TiedTo(TiedToBuffer); + TiedTo.SetBuffered(); + TiedTo << "a"; + std::error_code EC; + + std::string Buffer; + tied_raw_ostream TiedStream(TiedTo, Buffer); + // Sanity check that the stream hasn't already been flushed. + EXPECT_EQ("", TiedToBuffer); + + // Empty string doesn't cause a flush of TiedTo. + TiedStream << std::string(); + EXPECT_EQ("", TiedToBuffer); + + // Non-empty strings trigger flush of TiedTo. + TiedStream << "b"; + EXPECT_EQ("a", TiedToBuffer); +} }