xref: /llvm-project/llvm/include/llvm/Support/LogicalResult.h (revision db791b278a414fb6df1acc1799adcf11d8fb9169)
1 //===- LogicalResult.h - Utilities for handling success/failure -*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_SUPPORT_LOGICALRESULT_H
10 #define LLVM_SUPPORT_LOGICALRESULT_H
11 
12 #include <cassert>
13 #include <optional>
14 
15 namespace llvm {
16 /// This class represents an efficient way to signal success or failure. It
17 /// should be preferred over the use of `bool` when appropriate, as it avoids
18 /// all of the ambiguity that arises in interpreting a boolean result. This
19 /// class is marked as NODISCARD to ensure that the result is processed. Users
20 /// may explicitly discard a result by using `(void)`, e.g.
21 /// `(void)functionThatReturnsALogicalResult();`. Given the intended nature of
22 /// this class, it generally shouldn't be used as the result of functions that
23 /// very frequently have the result ignored. This class is intended to be used
24 /// in conjunction with the utility functions below.
25 struct [[nodiscard]] LogicalResult {
26 public:
27   /// If isSuccess is true a `success` result is generated, otherwise a
28   /// 'failure' result is generated.
29   static LogicalResult success(bool IsSuccess = true) {
30     return LogicalResult(IsSuccess);
31   }
32 
33   /// If isFailure is true a `failure` result is generated, otherwise a
34   /// 'success' result is generated.
35   static LogicalResult failure(bool IsFailure = true) {
36     return LogicalResult(!IsFailure);
37   }
38 
39   /// Returns true if the provided LogicalResult corresponds to a success value.
succeededLogicalResult40   constexpr bool succeeded() const { return IsSuccess; }
41 
42   /// Returns true if the provided LogicalResult corresponds to a failure value.
failedLogicalResult43   constexpr bool failed() const { return !IsSuccess; }
44 
45 private:
LogicalResultLogicalResult46   LogicalResult(bool IsSuccess) : IsSuccess(IsSuccess) {}
47 
48   /// Boolean indicating if this is a success result, if false this is a
49   /// failure result.
50   bool IsSuccess;
51 };
52 
53 /// Utility function to generate a LogicalResult. If isSuccess is true a
54 /// `success` result is generated, otherwise a 'failure' result is generated.
55 inline LogicalResult success(bool IsSuccess = true) {
56   return LogicalResult::success(IsSuccess);
57 }
58 
59 /// Utility function to generate a LogicalResult. If isFailure is true a
60 /// `failure` result is generated, otherwise a 'success' result is generated.
61 inline LogicalResult failure(bool IsFailure = true) {
62   return LogicalResult::failure(IsFailure);
63 }
64 
65 /// Utility function that returns true if the provided LogicalResult corresponds
66 /// to a success value.
succeeded(LogicalResult Result)67 inline bool succeeded(LogicalResult Result) { return Result.succeeded(); }
68 
69 /// Utility function that returns true if the provided LogicalResult corresponds
70 /// to a failure value.
failed(LogicalResult Result)71 inline bool failed(LogicalResult Result) { return Result.failed(); }
72 
73 /// This class provides support for representing a failure result, or a valid
74 /// value of type `T`. This allows for integrating with LogicalResult, while
75 /// also providing a value on the success path.
76 template <typename T> class [[nodiscard]] FailureOr : public std::optional<T> {
77 public:
78   /// Allow constructing from a LogicalResult. The result *must* be a failure.
79   /// Success results should use a proper instance of type `T`.
FailureOr(LogicalResult Result)80   FailureOr(LogicalResult Result) {
81     assert(failed(Result) &&
82            "success should be constructed with an instance of 'T'");
83   }
FailureOr()84   FailureOr() : FailureOr(failure()) {}
FailureOr(T && Y)85   FailureOr(T &&Y) : std::optional<T>(std::forward<T>(Y)) {}
FailureOr(const T & Y)86   FailureOr(const T &Y) : std::optional<T>(Y) {}
87   template <typename U,
88             std::enable_if_t<std::is_constructible<T, U>::value> * = nullptr>
FailureOr(const FailureOr<U> & Other)89   FailureOr(const FailureOr<U> &Other)
90       : std::optional<T>(failed(Other) ? std::optional<T>()
91                                        : std::optional<T>(*Other)) {}
92 
LogicalResult()93   operator LogicalResult() const { return success(has_value()); }
94 
95 private:
96   /// Hide the bool conversion as it easily creates confusion.
97   using std::optional<T>::operator bool;
98   using std::optional<T>::has_value;
99 };
100 
101 /// Wrap a value on the success path in a FailureOr of the same value type.
102 template <typename T,
103           typename = std::enable_if_t<!std::is_convertible_v<T, bool>>>
success(T && Y)104 inline auto success(T &&Y) {
105   return FailureOr<std::decay_t<T>>(std::forward<T>(Y));
106 }
107 
108 /// This class represents success/failure for parsing-like operations that find
109 /// it important to chain together failable operations with `||`.  This is an
110 /// extended version of `LogicalResult` that allows for explicit conversion to
111 /// bool.
112 ///
113 /// This class should not be used for general error handling cases - we prefer
114 /// to keep the logic explicit with the `succeeded`/`failed` predicates.
115 /// However, traditional monadic-style parsing logic can sometimes get
116 /// swallowed up in boilerplate without this, so we provide this for narrow
117 /// cases where it is important.
118 ///
119 class [[nodiscard]] ParseResult : public LogicalResult {
120 public:
LogicalResult(Result)121   ParseResult(LogicalResult Result = success()) : LogicalResult(Result) {}
122 
123   /// Failure is true in a boolean context.
124   constexpr explicit operator bool() const { return failed(); }
125 };
126 } // namespace llvm
127 
128 #endif // LLVM_SUPPORT_LOGICALRESULT_H
129