forked from OSchip/llvm-project
[Support] Switch to RAII helper for error-as-out-parameter idiom.
As discussed on the llvm-commits thread for r264467. llvm-svn: 264479
This commit is contained in:
parent
ed963704f5
commit
d1af8fce0f
|
@ -143,13 +143,6 @@ public:
|
||||||
/// constructor, but should be preferred for readability where possible.
|
/// constructor, but should be preferred for readability where possible.
|
||||||
static Error success() { return Error(); }
|
static Error success() { return Error(); }
|
||||||
|
|
||||||
/// Create a 'pre-checked' success value suitable for use as an out-parameter.
|
|
||||||
static Error errorForOutParameter() {
|
|
||||||
Error Err;
|
|
||||||
(void)!!Err;
|
|
||||||
return Err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errors are not copy-constructable.
|
// Errors are not copy-constructable.
|
||||||
Error(const Error &Other) = delete;
|
Error(const Error &Other) = delete;
|
||||||
|
|
||||||
|
@ -552,6 +545,36 @@ inline void consumeError(Error Err) {
|
||||||
handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {});
|
handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper for Errors used as out-parameters.
|
||||||
|
///
|
||||||
|
/// This helper is for use with the Error-as-out-parameter idiom, where an error
|
||||||
|
/// is passed to a function or method by reference, rather than being returned.
|
||||||
|
/// In such cases it is helpful to set the checked bit on entry to the function
|
||||||
|
/// so that the error can be written to (unchecked Errors abort on assignment)
|
||||||
|
/// and clear the checked bit on exit so that clients cannot accidentally forget
|
||||||
|
/// to check the result. This helper performs these actions automatically using
|
||||||
|
/// RAII:
|
||||||
|
///
|
||||||
|
/// Result foo(Error &Err) {
|
||||||
|
/// ErrorAsOutParameter ErrAsOutParam(Err); // 'Checked' flag set
|
||||||
|
/// // <body of foo>
|
||||||
|
/// // <- 'Checked' flag auto-cleared when ErrAsOutParam is destructed.
|
||||||
|
/// }
|
||||||
|
class ErrorAsOutParameter {
|
||||||
|
public:
|
||||||
|
ErrorAsOutParameter(Error &Err) : Err(Err) {
|
||||||
|
// Raise the checked bit if Err is success.
|
||||||
|
(void)!!Err;
|
||||||
|
}
|
||||||
|
~ErrorAsOutParameter() {
|
||||||
|
// Clear the checked bit.
|
||||||
|
if (!Err)
|
||||||
|
Err = Error::success();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Error &Err;
|
||||||
|
};
|
||||||
|
|
||||||
/// Tagged union holding either a T or a Error.
|
/// Tagged union holding either a T or a Error.
|
||||||
///
|
///
|
||||||
/// This class parallels ErrorOr, but replaces error_code with Error. Since
|
/// This class parallels ErrorOr, but replaces error_code with Error. Since
|
||||||
|
|
|
@ -246,7 +246,7 @@ static Error parseSegmentLoadCommand(
|
||||||
Expected<std::unique_ptr<MachOObjectFile>>
|
Expected<std::unique_ptr<MachOObjectFile>>
|
||||||
MachOObjectFile::create(MemoryBufferRef Object, bool IsLittleEndian,
|
MachOObjectFile::create(MemoryBufferRef Object, bool IsLittleEndian,
|
||||||
bool Is64Bits) {
|
bool Is64Bits) {
|
||||||
Error Err = Error::errorForOutParameter();
|
Error Err;
|
||||||
std::unique_ptr<MachOObjectFile> Obj(
|
std::unique_ptr<MachOObjectFile> Obj(
|
||||||
new MachOObjectFile(std::move(Object), IsLittleEndian,
|
new MachOObjectFile(std::move(Object), IsLittleEndian,
|
||||||
Is64Bits, Err));
|
Is64Bits, Err));
|
||||||
|
@ -262,7 +262,7 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
|
||||||
DataInCodeLoadCmd(nullptr), LinkOptHintsLoadCmd(nullptr),
|
DataInCodeLoadCmd(nullptr), LinkOptHintsLoadCmd(nullptr),
|
||||||
DyldInfoLoadCmd(nullptr), UuidLoadCmd(nullptr),
|
DyldInfoLoadCmd(nullptr), UuidLoadCmd(nullptr),
|
||||||
HasPageZeroSegment(false) {
|
HasPageZeroSegment(false) {
|
||||||
|
ErrorAsOutParameter ErrAsOutParam(Err);
|
||||||
if (is64Bit())
|
if (is64Bit())
|
||||||
parseHeader(this, Header64, Err);
|
parseHeader(this, Header64, Err);
|
||||||
else
|
else
|
||||||
|
|
|
@ -105,12 +105,32 @@ TEST(Error, UncheckedSuccess) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Test that errors to be used as out parameters are implicitly checked (
|
// ErrorAsOutParameter tester.
|
||||||
// and thus destruct quietly).
|
void errAsOutParamHelper(Error &Err) {
|
||||||
TEST(Error, ErrorAsOutParameter) {
|
ErrorAsOutParameter ErrAsOutParam(Err);
|
||||||
Error E = Error::errorForOutParameter();
|
// Verify that checked flag is raised - assignment should not crash.
|
||||||
|
Err = Error::success();
|
||||||
|
// Raise the checked bit manually - caller should still have to test the
|
||||||
|
// error.
|
||||||
|
(void)!!Err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that ErrorAsOutParameter sets the checked flag on construction.
|
||||||
|
TEST(Error, ErrorAsOutParameterChecked) {
|
||||||
|
Error E;
|
||||||
|
errAsOutParamHelper(E);
|
||||||
|
(void)!!E;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that ErrorAsOutParameter clears the checked flag on destruction.
|
||||||
|
#ifndef NDEBUG
|
||||||
|
TEST(Error, ErrorAsOutParameterUnchecked) {
|
||||||
|
EXPECT_DEATH({ Error E; errAsOutParamHelper(E); },
|
||||||
|
"Program aborted due to an unhandled Error:")
|
||||||
|
<< "ErrorAsOutParameter did not clear the checked flag on destruction.";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Check that we abort on unhandled failure cases. (Force conversion to bool
|
// Check that we abort on unhandled failure cases. (Force conversion to bool
|
||||||
// to make sure that we don't accidentally treat checked errors as handled).
|
// to make sure that we don't accidentally treat checked errors as handled).
|
||||||
// Test runs in debug mode only.
|
// Test runs in debug mode only.
|
||||||
|
|
Loading…
Reference in New Issue