[Support] Switch to RAII helper for error-as-out-parameter idiom.

As discussed on the llvm-commits thread for r264467.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@264479 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Lang Hames 2016-03-25 23:54:32 +00:00
parent 9a0b1af1ce
commit bbb35fb68d
3 changed files with 56 additions and 13 deletions

View File

@ -143,13 +143,6 @@ public:
/// constructor, but should be preferred for readability where possible.
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.
Error(const Error &Other) = delete;
@ -552,6 +545,36 @@ inline void consumeError(Error Err) {
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.
///
/// This class parallels ErrorOr, but replaces error_code with Error. Since

View File

@ -246,7 +246,7 @@ static Error parseSegmentLoadCommand(
Expected<std::unique_ptr<MachOObjectFile>>
MachOObjectFile::create(MemoryBufferRef Object, bool IsLittleEndian,
bool Is64Bits) {
Error Err = Error::errorForOutParameter();
Error Err;
std::unique_ptr<MachOObjectFile> Obj(
new MachOObjectFile(std::move(Object), IsLittleEndian,
Is64Bits, Err));
@ -262,7 +262,7 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
DataInCodeLoadCmd(nullptr), LinkOptHintsLoadCmd(nullptr),
DyldInfoLoadCmd(nullptr), UuidLoadCmd(nullptr),
HasPageZeroSegment(false) {
ErrorAsOutParameter ErrAsOutParam(Err);
if (is64Bit())
parseHeader(this, Header64, Err);
else

View File

@ -105,12 +105,32 @@ TEST(Error, UncheckedSuccess) {
}
#endif
// Test that errors to be used as out parameters are implicitly checked (
// and thus destruct quietly).
TEST(Error, ErrorAsOutParameter) {
Error E = Error::errorForOutParameter();
// ErrorAsOutParameter tester.
void errAsOutParamHelper(Error &Err) {
ErrorAsOutParameter ErrAsOutParam(Err);
// 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
// to make sure that we don't accidentally treat checked errors as handled).
// Test runs in debug mode only.