diff --git a/docs/ProgrammersManual.rst b/docs/ProgrammersManual.rst index f6480d0f8b4..d07f7c58f03 100644 --- a/docs/ProgrammersManual.rst +++ b/docs/ProgrammersManual.rst @@ -564,18 +564,18 @@ the boolean conversion operator): .. code-block:: c++ - if (auto Err = canFail(...)) + if (auto Err = mayFail(...)) return Err; // Failure value - move error to caller. // Safe to continue: Err was checked. -In contrast, the following code will always cause an abort, even if ``canFail`` +In contrast, the following code will always cause an abort, even if ``mayFail`` returns a success value: .. code-block:: c++ - canFail(); - // Program will always abort here, even if canFail() returns Success, since + mayFail(); + // Program will always abort here, even if mayFail() returns Success, since // the value is not checked. Failure values are considered checked once a handler for the error type has @@ -633,6 +633,12 @@ exiting with an error code, the :ref:`ExitOnError ` utility may be a better choice than handleErrors, as it simplifies control flow when calling fallible functions. +In situations where it is known that a particular call to a fallible function +will always succeed (for example, a call to a function that can only fail on a +subset of inputs with an input that is known to be safe) the +:ref:`cantFail ` functions can be used to remove the error type, +simplifying control flow. + StringError """"""""""" @@ -765,6 +771,43 @@ mapping can also be supplied from ``Error`` values to exit codes using the Use ``ExitOnError`` in your tool code where possible as it can greatly improve readability. +.. _err_cantfail: + +Using cantFail to simplify safe callsites +""""""""""""""""""""""""""""""""""""""""" + +Some functions may only fail for a subset of their inputs. For such functions +call-sites using known-safe inputs can assume that the result will be a success +value. + +The cantFail functions encapsulate this by wrapping an assertion that their +argument is a success value and, in the case of Expected, unwrapping the +T value from the Expected argument: + +.. code-block:: c++ + + Error mayFail(int X); + Expected mayFail2(int X); + + void foo() { + cantFail(mayFail(KnownSafeValue)); + int Y = cantFail(mayFail2(KnownSafeValue)); + ... + } + +Like the ExitOnError utility, cantFail simplifies control flow. Their treatment +of error cases is very different however: Where ExitOnError is guaranteed to +terminate the program on an error input, cantFile simply asserts that the result +is success. In debug builds this will result in an assertion failure if an error +is encountered. In release builds the behavior of cantFail for failure values is +undefined. As such, care must be taken in the use of cantFail: clients must be +certain that a cantFail wrapped call really can not fail under any +circumstances. + +Use of the cantFail functions should be rare in library code, but they are +likely to be of more use in tool and unit-test code where inputs and/or +mocked-up classes or functions may be known to be safe. + Fallible constructors """"""""""""""""""""" diff --git a/include/llvm/Support/Error.h b/include/llvm/Support/Error.h index f13c9484b5f..21664d4b715 100644 --- a/include/llvm/Support/Error.h +++ b/include/llvm/Support/Error.h @@ -985,6 +985,45 @@ private: LLVM_ATTRIBUTE_NORETURN void report_fatal_error(Error Err, bool gen_crash_diag = true); +/// Report a fatal error if Err is a failure value. +/// +/// This function can be used to wrap calls to fallible functions ONLY when it +/// is known that the Error will always be a success value. E.g. +/// +/// @code{.cpp} +/// // foo only attempts the fallible operation if DoFallibleOperation is +/// // true. If DoFallibleOperation is false then foo always returns +/// // Error::success(). +/// Error foo(bool DoFallibleOperation); +/// +/// cantFail(foo(false)); +/// @endcode +inline void cantFail(Error Err) { + if (Err) + llvm_unreachable("Failure value returned from cantFail wrapped call"); +} + +/// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and +/// returns the contained value. +/// +/// This function can be used to wrap calls to fallible functions ONLY when it +/// is known that the Error will always be a success value. E.g. +/// +/// @code{.cpp} +/// // foo only attempts the fallible operation if DoFallibleOperation is +/// // true. If DoFallibleOperation is false then foo always returns an int. +/// Expected foo(bool DoFallibleOperation); +/// +/// int X = cantFail(foo(false)); +/// @endcode +template +T cantFail(Expected ValOrErr) { + if (ValOrErr) + return std::move(*ValOrErr); + else + llvm_unreachable("Failure value returned from cantFail wrapped call"); +} + } // end namespace llvm #endif // LLVM_SUPPORT_ERROR_H diff --git a/unittests/Support/ErrorTest.cpp b/unittests/Support/ErrorTest.cpp index 29a173a058b..382346cd231 100644 --- a/unittests/Support/ErrorTest.cpp +++ b/unittests/Support/ErrorTest.cpp @@ -469,6 +469,34 @@ TEST(Error, ExitOnError) { << "exitOnError returned an unexpected error result"; } +// Test that the ExitOnError utility works as expected. +TEST(Error, CantFailSuccess) { + cantFail(Error::success()); + + int X = cantFail(Expected(42)); + EXPECT_EQ(X, 42) << "Expected value modified by cantFail"; +} + +// Test that cantFail results in a crash if you pass it a failure value. +#if LLVM_ENABLE_ABI_BREAKING_CHECKS +TEST(Error, CantFailDeath) { + EXPECT_DEATH( + cantFail(make_error("foo", inconvertibleErrorCode())), + "Failure value returned from cantFail wrapped call") + << "cantFail(Error) did not cause an abort for failure value"; + + EXPECT_DEATH( + { + auto IEC = inconvertibleErrorCode(); + int X = cantFail(Expected(make_error("foo", IEC))); + (void)X; + }, + "Failure value returned from cantFail wrapped call") + << "cantFail(Expected) did not cause an abort for failure value"; +} +#endif + + // Test Checked Expected in success mode. TEST(Error, CheckedExpectedInSuccessMode) { Expected A = 7;