[llvm] r296384 - [Support][Error] Add a 'cantFail' utility function for known-safe calls to

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 27 13:09:48 PST 2017


Author: lhames
Date: Mon Feb 27 15:09:47 2017
New Revision: 296384

URL: http://llvm.org/viewvc/llvm-project?rev=296384&view=rev
Log:
[Support][Error] Add a 'cantFail' utility function for known-safe calls to
fallible functions.

Some fallible functions (those returning Error or Expected<T>) may only fail
for a subset of their inputs. For example, a "safe" square root function will
succeed for all finite positive inputs:

  Expected<double> safeSqrt(double d) {
    if (d < 0 && !isnan(d) && !isinf(d))
      return make_error<...>("Cannot sqrt -ve values, nans or infs");
    return sqrt(d);
  }

At a safe callsite for such a function, checking the error return value is
redundant:

  if (auto ValOrErr = safeSqrt(42.0)) {
    // use *ValOrErr.
  } else
    llvm_unreachable("safeSqrt should always succeed for +ve values");

The cantFail function wraps this check and extracts the contained value,
simplifying control flow:

  double Result = cantFail(safeSqrt(42.0));

This function should be used with care: it is a programmatic error to wrap a
call with cantFail if it can in fact fail. For debug builds this will
result in llvm_unreachable being called. For release builds the behavior is
undefined.

Use of this function is likely to be rare in library code, but more common
for tool and unit-test code where inputs and mock functions may be known to be
safe.

Modified:
    llvm/trunk/docs/ProgrammersManual.rst
    llvm/trunk/include/llvm/Support/Error.h
    llvm/trunk/unittests/Support/ErrorTest.cpp

Modified: llvm/trunk/docs/ProgrammersManual.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/ProgrammersManual.rst?rev=296384&r1=296383&r2=296384&view=diff
==============================================================================
--- llvm/trunk/docs/ProgrammersManual.rst (original)
+++ llvm/trunk/docs/ProgrammersManual.rst Mon Feb 27 15:09:47 2017
@@ -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:`Ex
 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 <err_cantfail>` functions can be used to remove the error type,
+simplifying control flow.
+
 StringError
 """""""""""
 
@@ -765,6 +771,43 @@ mapping can also be supplied from ``Erro
 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<T>, unwrapping the
+T value from the Expected<T> argument:
+
+.. code-block:: c++
+
+  Error mayFail(int X);
+  Expected<int> 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
 """""""""""""""""""""
 

Modified: llvm/trunk/include/llvm/Support/Error.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Error.h?rev=296384&r1=296383&r2=296384&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/Error.h (original)
+++ llvm/trunk/include/llvm/Support/Error.h Mon Feb 27 15:09:47 2017
@@ -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<int> foo(bool DoFallibleOperation);
+///
+///   int X = cantFail(foo(false));
+///   @endcode
+template <typename T>
+T cantFail(Expected<T> 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

Modified: llvm/trunk/unittests/Support/ErrorTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/ErrorTest.cpp?rev=296384&r1=296383&r2=296384&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/ErrorTest.cpp (original)
+++ llvm/trunk/unittests/Support/ErrorTest.cpp Mon Feb 27 15:09:47 2017
@@ -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<int>(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<StringError>("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<int>(make_error<StringError>("foo", IEC)));
+        (void)X;
+      },
+      "Failure value returned from cantFail wrapped call")
+    << "cantFail(Expected<int>) did not cause an abort for failure value";
+}
+#endif
+
+
 // Test Checked Expected<T> in success mode.
 TEST(Error, CheckedExpectedInSuccessMode) {
   Expected<int> A = 7;




More information about the llvm-commits mailing list