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