Skip to content

Commit 44df116

Browse files
authored
[Error] Add non-consuming toString (#95375)
There are some places that want to convert an Error to string, but still retain the original Error object, for example to emit a non-fatal warning. This currently isn't possible, because the entire Error infra is move-based. And what people end up doing in this case is to move the Error... twice. This patch introduces a toStringWithoutConsuming() function to accommodate this use case. This also requires some infrastructure that allows visiting Errors without consuming them.
1 parent 4bccd25 commit 44df116

File tree

5 files changed

+50
-4
lines changed

5 files changed

+50
-4
lines changed

llvm/include/llvm/Support/Error.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ class [[nodiscard]] Error {
166166
// handleErrors needs to be able to set the Checked flag.
167167
template <typename... HandlerTs>
168168
friend Error handleErrors(Error E, HandlerTs &&... Handlers);
169+
// visitErrors needs direct access to the payload.
170+
template <typename HandlerT>
171+
friend void visitErrors(const Error &E, HandlerT H);
169172

170173
// Expected<T> needs to be able to steal the payload when constructed from an
171174
// error.
@@ -369,6 +372,10 @@ class ErrorList final : public ErrorInfo<ErrorList> {
369372
// ErrorList.
370373
template <typename... HandlerTs>
371374
friend Error handleErrors(Error E, HandlerTs &&... Handlers);
375+
// visitErrors needs to be able to iterate the payload list of an
376+
// ErrorList.
377+
template <typename HandlerT>
378+
friend void visitErrors(const Error &E, HandlerT H);
372379

373380
// joinErrors is implemented in terms of join.
374381
friend Error joinErrors(Error, Error);
@@ -977,6 +984,23 @@ inline void handleAllErrors(Error E) {
977984
cantFail(std::move(E));
978985
}
979986

987+
/// Visit all the ErrorInfo(s) contained in E by passing them to the respective
988+
/// handler, without consuming the error.
989+
template <typename HandlerT> void visitErrors(const Error &E, HandlerT H) {
990+
const ErrorInfoBase *Payload = E.getPtr();
991+
if (!Payload)
992+
return;
993+
994+
if (Payload->isA<ErrorList>()) {
995+
const ErrorList &List = static_cast<const ErrorList &>(*Payload);
996+
for (const auto &P : List.Payloads)
997+
H(*P);
998+
return;
999+
}
1000+
1001+
return H(*Payload);
1002+
}
1003+
9801004
/// Handle any errors (if present) in an Expected<T>, then try a recovery path.
9811005
///
9821006
/// If the incoming value is a success value it is returned unmodified. If it
@@ -1031,6 +1055,10 @@ void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner = {});
10311055
/// is used to separate error messages.
10321056
std::string toString(Error E);
10331057

1058+
/// Like toString(), but does not consume the error. This can be used to print
1059+
/// a warning while retaining the original error object.
1060+
std::string toStringWithoutConsuming(const Error &E);
1061+
10341062
/// Consume a Error without doing anything. This method should be used
10351063
/// only where an error can be considered a reasonable and expected return
10361064
/// value.

llvm/lib/Support/Error.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ std::string toString(Error E) {
8282
return join(Errors.begin(), Errors.end(), "\n");
8383
}
8484

85+
std::string toStringWithoutConsuming(const Error &E) {
86+
SmallVector<std::string, 2> Errors;
87+
visitErrors(E, [&Errors](const ErrorInfoBase &EI) {
88+
Errors.push_back(EI.message());
89+
});
90+
return join(Errors.begin(), Errors.end(), "\n");
91+
}
92+
8593
std::error_code ErrorList::convertToErrorCode() const {
8694
return std::error_code(static_cast<int>(ErrorErrorCode::MultipleErrors),
8795
getErrorErrorCat());

llvm/tools/dsymutil/DwarfLinkerForBinary.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj,
147147
if (!ObjectEntry) {
148148
auto Err = ObjectEntry.takeError();
149149
reportWarning(Twine(Obj.getObjectFilename()) + ": " +
150-
toString(std::move(Err)),
150+
toStringWithoutConsuming(Err),
151151
Obj.getObjectFilename());
152152
return errorToErrorCode(std::move(Err));
153153
}
@@ -156,7 +156,7 @@ DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj,
156156
if (!Object) {
157157
auto Err = Object.takeError();
158158
reportWarning(Twine(Obj.getObjectFilename()) + ": " +
159-
toString(std::move(Err)),
159+
toStringWithoutConsuming(Err),
160160
Obj.getObjectFilename());
161161
return errorToErrorCode(std::move(Err));
162162
}

llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,13 +1494,13 @@ Error DumpOutputStyle::dumpModuleSymsForPdb() {
14941494
if (auto EC = Visitor.visitSymbolStreamFiltered(ModS.getSymbolArray(),
14951495
Filter)) {
14961496
P.formatLine("Error while processing symbol records. {0}",
1497-
toString(std::move(EC)));
1497+
toStringWithoutConsuming(EC));
14981498
return EC;
14991499
}
15001500
} else if (auto EC = Visitor.visitSymbolStream(ModS.getSymbolArray(),
15011501
SS.Offset)) {
15021502
P.formatLine("Error while processing symbol records. {0}",
1503-
toString(std::move(EC)));
1503+
toStringWithoutConsuming(EC));
15041504
return EC;
15051505
}
15061506
return Error::success();

llvm/unittests/Support/ErrorTest.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,15 +740,25 @@ TEST(Error, ErrorCodeConversions) {
740740
TEST(Error, ErrorMessage) {
741741
EXPECT_EQ(toString(Error::success()), "");
742742

743+
Error E0 = Error::success();
744+
EXPECT_EQ(toStringWithoutConsuming(E0), "");
745+
EXPECT_EQ(toString(std::move(E0)), "");
746+
743747
Error E1 = make_error<CustomError>(0);
748+
EXPECT_EQ(toStringWithoutConsuming(E1), "CustomError {0}");
744749
EXPECT_EQ(toString(std::move(E1)), "CustomError {0}");
745750

746751
Error E2 = make_error<CustomError>(0);
752+
visitErrors(E2, [](const ErrorInfoBase &EI) {
753+
EXPECT_EQ(EI.message(), "CustomError {0}");
754+
});
747755
handleAllErrors(std::move(E2), [](const CustomError &CE) {
748756
EXPECT_EQ(CE.message(), "CustomError {0}");
749757
});
750758

751759
Error E3 = joinErrors(make_error<CustomError>(0), make_error<CustomError>(1));
760+
EXPECT_EQ(toStringWithoutConsuming(E3), "CustomError {0}\n"
761+
"CustomError {1}");
752762
EXPECT_EQ(toString(std::move(E3)), "CustomError {0}\n"
753763
"CustomError {1}");
754764
}

0 commit comments

Comments
 (0)