Skip to content

Commit 907bde1

Browse files
committed
[Support] Require llvm::Error passed to formatv() to be wrapped in fmt_consume()
Summary: Someone must be responsible for handling an Error. When formatv takes ownership of an Error, the formatv_object destructor must take care of this. Passing an error by value to formatv() is not considered explicit enough to mark the error as handled (see D49013), so we require callers to use a format adapter to confirm this intent. Reviewers: zturner Subscribers: llvm-commits, lhames Differential Revision: https://reviews.llvm.org/D49170 llvm-svn: 336888
1 parent d3b69c6 commit 907bde1

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

llvm/include/llvm/Support/FormatAdapters.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212

1313
#include "llvm/ADT/SmallString.h"
1414
#include "llvm/ADT/StringRef.h"
15+
#include "llvm/Support/Error.h"
1516
#include "llvm/Support/FormatCommon.h"
1617
#include "llvm/Support/FormatVariadicDetails.h"
1718
#include "llvm/Support/raw_ostream.h"
1819

1920
namespace llvm {
2021
template <typename T> class FormatAdapter : public detail::format_adapter {
2122
protected:
22-
explicit FormatAdapter(T &&Item) : Item(Item) {}
23+
explicit FormatAdapter(T &&Item) : Item(std::forward<T>(Item)) {}
2324

2425
T Item;
2526
};
@@ -71,6 +72,14 @@ template <typename T> class RepeatAdapter final : public FormatAdapter<T> {
7172
}
7273
}
7374
};
75+
76+
class ErrorAdapter : public FormatAdapter<Error> {
77+
public:
78+
ErrorAdapter(Error &&Item) : FormatAdapter(std::move(Item)) {}
79+
ErrorAdapter(ErrorAdapter &&) = default;
80+
~ErrorAdapter() { consumeError(std::move(Item)); }
81+
void format(llvm::raw_ostream &Stream, StringRef Style) { Stream << Item; }
82+
};
7483
}
7584

7685
template <typename T>
@@ -88,6 +97,13 @@ template <typename T>
8897
detail::RepeatAdapter<T> fmt_repeat(T &&Item, size_t Count) {
8998
return detail::RepeatAdapter<T>(std::forward<T>(Item), Count);
9099
}
100+
101+
// llvm::Error values must be consumed before being destroyed.
102+
// Wrapping an error in fmt_consume explicitly indicates that the formatv_object
103+
// should take ownership and consume it.
104+
inline detail::ErrorAdapter fmt_consume(Error &&Item) {
105+
return detail::ErrorAdapter(std::move(Item));
106+
}
91107
}
92108

93109
#endif

llvm/include/llvm/Support/FormatVariadicDetails.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
namespace llvm {
1919
template <typename T, typename Enable = void> struct format_provider {};
20+
class Error;
2021

2122
namespace detail {
2223
class format_adapter {
@@ -141,6 +142,12 @@ template <typename T>
141142
typename std::enable_if<uses_stream_operator<T>::value,
142143
stream_operator_format_adapter<T>>::type
143144
build_format_adapter(T &&Item) {
145+
// If the caller passed an Error by value, then stream_operator_format_adapter
146+
// would be responsible for consuming it.
147+
// Make the caller opt into this by calling fmt_consume().
148+
static_assert(
149+
!std::is_same<llvm::Error, typename std::remove_cv<T>::type>::value,
150+
"llvm::Error-by-value must be wrapped in fmt_consume() for formatv");
144151
return stream_operator_format_adapter<T>(std::forward<T>(Item));
145152
}
146153

llvm/unittests/Support/FormatVariadicTest.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//===----------------------------------------------------------------------===//
99

1010
#include "llvm/Support/FormatVariadic.h"
11+
#include "llvm/Support/Error.h"
1112
#include "llvm/Support/FormatAdapters.h"
1213
#include "gtest/gtest.h"
1314

@@ -680,3 +681,11 @@ TEST(FormatVariadicTest, FormatStreamable) {
680681
adl::X X;
681682
EXPECT_EQ("X", formatv("{0}", X).str());
682683
}
684+
685+
TEST(FormatVariadicTest, FormatError) {
686+
auto E1 = make_error<StringError>("X", inconvertibleErrorCode());
687+
EXPECT_EQ("X", formatv("{0}", E1).str());
688+
EXPECT_TRUE(E1.isA<StringError>()); // not consumed
689+
EXPECT_EQ("X", formatv("{0}", fmt_consume(std::move(E1))).str());
690+
EXPECT_FALSE(E1.isA<StringError>()); // consumed
691+
}

0 commit comments

Comments
 (0)