[PATCH] D49013: [Support] Allow formatv() to consume llvm::Error by-value without crashing.

Sam McCall via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 6 02:27:07 PDT 2018


sammccall created this revision.
sammccall added reviewers: zturner, ioeric.
Herald added a subscriber: llvm-commits.

Currently the following code crashes if an error occurs:

  llvm::Expected<Foo> MaybeFoo = ...
  if (!MaybeFoo)
    llvm::errs() << formatv("Error: {0}", MaybeFoo.takeError());

I feel conflicted about adding a special case like this, but

- Error has an unusual API that interacts strangely with other libraries.
- it's fairly important, and
- it gets used conditionally on error-handling paths that are rarely tested.

The main alternative I can see is to remove operator<< from Error again.
This requires you to write formatv(..., llvm::toString(MaybeFoo.takeError())),
and provides no way of printing an error by reference.


Repository:
  rL LLVM

https://reviews.llvm.org/D49013

Files:
  include/llvm/Support/FormatVariadicDetails.h
  lib/Support/FormatVariadic.cpp
  unittests/Support/FormatVariadicTest.cpp


Index: unittests/Support/FormatVariadicTest.cpp
===================================================================
--- unittests/Support/FormatVariadicTest.cpp
+++ unittests/Support/FormatVariadicTest.cpp
@@ -8,6 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FormatAdapters.h"
 #include "gtest/gtest.h"
 
@@ -680,3 +681,11 @@
   adl::X X;
   EXPECT_EQ("X", formatv("{0}", X).str());
 }
+
+TEST(FormatVariadicTest, FormatError) {
+  auto E1 = make_error<StringError>("X", inconvertibleErrorCode());
+  EXPECT_EQ("X", formatv("{0}", E1).str());
+  EXPECT_TRUE(E1.isA<StringError>()); // not consumed
+  EXPECT_EQ("X", formatv("{0}", std::move(E1)).str());
+  EXPECT_FALSE(E1.isA<StringError>()); // consumed
+}
Index: lib/Support/FormatVariadic.cpp
===================================================================
--- lib/Support/FormatVariadic.cpp
+++ lib/Support/FormatVariadic.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Error.h"
 
 using namespace llvm;
 
@@ -152,3 +153,5 @@
   }
   return Replacements;
 }
+
+void detail::cleanup(llvm::Error &&E) { consumeError(std::move(E)); }
Index: include/llvm/Support/FormatVariadicDetails.h
===================================================================
--- include/llvm/Support/FormatVariadicDetails.h
+++ include/llvm/Support/FormatVariadicDetails.h
@@ -17,6 +17,7 @@
 
 namespace llvm {
 template <typename T, typename Enable = void> struct format_provider {};
+class Error;
 
 namespace detail {
 class format_adapter {
@@ -38,15 +39,22 @@
   }
 };
 
+// Hack to allow accepting Error by value: formatv("Error: {0}", V.takeError());
+// In this case the error must be marked as consumed.
+template <typename T> void cleanup(T &&) {}
+void cleanup(llvm::Error &&E);
+
 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)) {}
+  stream_operator_format_adapter(stream_operator_format_adapter &&) = default;
 
   void format(llvm::raw_ostream &S, StringRef Options) override { S << Item; }
+  ~stream_operator_format_adapter() { detail::cleanup(std::forward<T>(Item)); }
 };
 
 template <typename T> class missing_format_adapter;


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D49013.154378.patch
Type: text/x-patch
Size: 2499 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20180706/029bee41/attachment.bin>


More information about the llvm-commits mailing list