Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /* A type suitable for returning either a value or an error from a function. */
8 :
9 : #ifndef mozilla_Result_h
10 : #define mozilla_Result_h
11 :
12 : #include "mozilla/Alignment.h"
13 : #include "mozilla/Assertions.h"
14 : #include "mozilla/Attributes.h"
15 : #include "mozilla/Types.h"
16 : #include "mozilla/TypeTraits.h"
17 : #include "mozilla/Variant.h"
18 :
19 : namespace mozilla {
20 :
21 : /**
22 : * Empty struct, indicating success for operations that have no return value.
23 : * For example, if you declare another empty struct `struct OutOfMemory {};`,
24 : * then `Result<Ok, OutOfMemory>` represents either success or OOM.
25 : */
26 : struct Ok {};
27 :
28 : template <typename E> class GenericErrorResult;
29 : template <typename V, typename E> class Result;
30 :
31 : namespace detail {
32 :
33 : enum class PackingStrategy {
34 : Variant,
35 : NullIsOk,
36 : LowBitTagIsError,
37 : PackedVariant,
38 : };
39 :
40 : template <typename V, typename E, PackingStrategy Strategy>
41 : class ResultImplementation;
42 :
43 : template <typename V, typename E>
44 5134 : class ResultImplementation<V, E, PackingStrategy::Variant>
45 : {
46 : mozilla::Variant<V, E> mStorage;
47 :
48 : public:
49 5134 : explicit ResultImplementation(V aValue) : mStorage(aValue) {}
50 0 : explicit ResultImplementation(E aErrorValue) : mStorage(aErrorValue) {}
51 :
52 15402 : bool isOk() const { return mStorage.template is<V>(); }
53 :
54 : // The callers of these functions will assert isOk() has the proper value, so
55 : // these functions (in all ResultImplementation specializations) don't need
56 : // to do so.
57 5134 : V unwrap() const { return mStorage.template as<V>(); }
58 0 : E unwrapErr() const { return mStorage.template as<E>(); }
59 : };
60 :
61 : /**
62 : * mozilla::Variant doesn't like storing a reference. This is a specialization
63 : * to store E as pointer if it's a reference.
64 : */
65 : template <typename V, typename E>
66 : class ResultImplementation<V, E&, PackingStrategy::Variant>
67 : {
68 : mozilla::Variant<V, E*> mStorage;
69 :
70 : public:
71 : explicit ResultImplementation(V aValue) : mStorage(aValue) {}
72 : explicit ResultImplementation(E& aErrorValue) : mStorage(&aErrorValue) {}
73 :
74 : bool isOk() const { return mStorage.template is<V>(); }
75 : V unwrap() const { return mStorage.template as<V>(); }
76 : E& unwrapErr() const { return *mStorage.template as<E*>(); }
77 : };
78 :
79 : /**
80 : * Specialization for when the success type is Ok (or another empty class) and
81 : * the error type is a reference.
82 : */
83 : template <typename V, typename E>
84 : class ResultImplementation<V, E&, PackingStrategy::NullIsOk>
85 : {
86 : E* mErrorValue;
87 :
88 : public:
89 1564 : explicit ResultImplementation(V) : mErrorValue(nullptr) {}
90 0 : explicit ResultImplementation(E& aErrorValue) : mErrorValue(&aErrorValue) {}
91 :
92 3128 : bool isOk() const { return mErrorValue == nullptr; }
93 :
94 : V unwrap() const { return V(); }
95 0 : E& unwrapErr() const { return *mErrorValue; }
96 : };
97 :
98 : /**
99 : * Specialization for when alignment permits using the least significant bit as
100 : * a tag bit.
101 : */
102 : template <typename V, typename E>
103 : class ResultImplementation<V*, E&, PackingStrategy::LowBitTagIsError>
104 : {
105 : uintptr_t mBits;
106 :
107 : public:
108 140739 : explicit ResultImplementation(V* aValue)
109 140739 : : mBits(reinterpret_cast<uintptr_t>(aValue))
110 : {
111 140739 : MOZ_ASSERT((uintptr_t(aValue) % MOZ_ALIGNOF(V)) == 0,
112 : "Result value pointers must not be misaligned");
113 140739 : }
114 0 : explicit ResultImplementation(E& aErrorValue)
115 0 : : mBits(reinterpret_cast<uintptr_t>(&aErrorValue) | 1)
116 : {
117 0 : MOZ_ASSERT((uintptr_t(&aErrorValue) % MOZ_ALIGNOF(E)) == 0,
118 : "Result errors must not be misaligned");
119 0 : }
120 :
121 422217 : bool isOk() const { return (mBits & 1) == 0; }
122 :
123 140739 : V* unwrap() const { return reinterpret_cast<V*>(mBits); }
124 : E& unwrapErr() const { return *reinterpret_cast<E*>(mBits ^ 1); }
125 : };
126 :
127 : // Return true if any of the struct can fit in a word.
128 : template<typename V, typename E>
129 : struct IsPackableVariant
130 : {
131 : struct VEbool {
132 : V v;
133 : E e;
134 : bool ok;
135 : };
136 : struct EVbool {
137 : E e;
138 : V v;
139 : bool ok;
140 : };
141 :
142 : using Impl = typename Conditional<sizeof(VEbool) <= sizeof(EVbool),
143 : VEbool, EVbool>::Type;
144 :
145 : static const bool value = sizeof(Impl) <= sizeof(uintptr_t);
146 : };
147 :
148 : /**
149 : * Specialization for when both type are not using all the bytes, in order to
150 : * use one byte as a tag.
151 : */
152 : template <typename V, typename E>
153 : class ResultImplementation<V, E, PackingStrategy::PackedVariant>
154 : {
155 : using Impl = typename IsPackableVariant<V, E>::Impl;
156 : Impl data;
157 :
158 : public:
159 45459 : explicit ResultImplementation(V aValue)
160 : {
161 1370 : data.v = aValue;
162 45459 : data.ok = true;
163 45459 : }
164 29 : explicit ResultImplementation(E aErrorValue)
165 : {
166 29 : data.e = aErrorValue;
167 29 : data.ok = false;
168 29 : }
169 :
170 93593 : bool isOk() const { return data.ok; }
171 :
172 1370 : V unwrap() const { return data.v; }
173 32 : E unwrapErr() const { return data.e; }
174 : };
175 :
176 : // To use nullptr as a special value, we need the counter part to exclude zero
177 : // from its range of valid representations.
178 : //
179 : // By default assume that zero can be represented.
180 : template<typename T>
181 : struct UnusedZero
182 : {
183 : static const bool value = false;
184 : };
185 :
186 : // References can't be null.
187 : template<typename T>
188 : struct UnusedZero<T&>
189 : {
190 : static const bool value = true;
191 : };
192 :
193 : // A bit of help figuring out which of the above specializations to use.
194 : //
195 : // We begin by safely assuming types don't have a spare bit.
196 : template <typename T> struct HasFreeLSB { static const bool value = false; };
197 :
198 : // The lowest bit of a properly-aligned pointer is always zero if the pointee
199 : // type is greater than byte-aligned. That bit is free to use if it's masked
200 : // out of such pointers before they're dereferenced.
201 : template <typename T> struct HasFreeLSB<T*> {
202 : static const bool value = (MOZ_ALIGNOF(T) & 1) == 0;
203 : };
204 :
205 : // We store references as pointers, so they have a free bit if a pointer would
206 : // have one.
207 : template <typename T> struct HasFreeLSB<T&> {
208 : static const bool value = HasFreeLSB<T*>::value;
209 : };
210 :
211 : // Select one of the previous result implementation based on the properties of
212 : // the V and E types.
213 : template <typename V, typename E>
214 : struct SelectResultImpl
215 : {
216 : static const PackingStrategy value =
217 : (IsEmpty<V>::value && UnusedZero<E>::value)
218 : ? PackingStrategy::NullIsOk
219 : : (detail::HasFreeLSB<V>::value && detail::HasFreeLSB<E>::value)
220 : ? PackingStrategy::LowBitTagIsError
221 : : (IsDefaultConstructible<V>::value && IsDefaultConstructible<E>::value &&
222 : IsPackableVariant<V, E>::value)
223 : ? PackingStrategy::PackedVariant
224 : : PackingStrategy::Variant;
225 :
226 : using Type = detail::ResultImplementation<V, E, value>;
227 : };
228 :
229 : template <typename T>
230 : struct IsResult : FalseType { };
231 :
232 : template <typename V, typename E>
233 : struct IsResult<Result<V, E>> : TrueType { };
234 :
235 : } // namespace detail
236 :
237 : /**
238 : * Result<V, E> represents the outcome of an operation that can either succeed
239 : * or fail. It contains either a success value of type V or an error value of
240 : * type E.
241 : *
242 : * All Result methods are const, so results are basically immutable.
243 : * This is just like Variant<V, E> but with a slightly different API, and the
244 : * following cases are optimized so Result can be stored more efficiently:
245 : *
246 : * - If the success type is Ok (or another empty class) and the error type is a
247 : * reference, Result<V, E&> is guaranteed to be pointer-sized and all zero
248 : * bits on success. Do not change this representation! There is JIT code that
249 : * depends on it.
250 : *
251 : * - If the success type is a pointer type and the error type is a reference
252 : * type, and the least significant bit is unused for both types when stored
253 : * as a pointer (due to alignment rules), Result<V*, E&> is guaranteed to be
254 : * pointer-sized. In this case, we use the lowest bit as tag bit: 0 to
255 : * indicate the Result's bits are a V, 1 to indicate the Result's bits (with
256 : * the 1 masked out) encode an E*.
257 : *
258 : * The purpose of Result is to reduce the screwups caused by using `false` or
259 : * `nullptr` to indicate errors.
260 : * What screwups? See <https://bugzilla.mozilla.org/show_bug.cgi?id=912928> for
261 : * a partial list.
262 : */
263 : template <typename V, typename E>
264 5134 : class MOZ_MUST_USE_TYPE Result final
265 : {
266 : using Impl = typename detail::SelectResultImpl<V, E>::Type;
267 :
268 : Impl mImpl;
269 :
270 : public:
271 : /**
272 : * Create a success result.
273 : */
274 192895 : MOZ_IMPLICIT Result(V aValue) : mImpl(aValue) { MOZ_ASSERT(isOk()); }
275 :
276 : /**
277 : * Create an error result.
278 : */
279 : explicit Result(E aErrorValue) : mImpl(aErrorValue) { MOZ_ASSERT(isErr()); }
280 :
281 : /**
282 : * Implementation detail of MOZ_TRY().
283 : * Create an error result from another error result.
284 : */
285 : template <typename E2>
286 29 : MOZ_IMPLICIT Result(const GenericErrorResult<E2>& aErrorResult)
287 29 : : mImpl(aErrorResult.mErrorValue)
288 : {
289 : static_assert(mozilla::IsConvertible<E2, E>::value,
290 : "E2 must be convertible to E");
291 29 : MOZ_ASSERT(isErr());
292 29 : }
293 :
294 : Result(const Result&) = default;
295 : Result& operator=(const Result&) = default;
296 :
297 : /** True if this Result is a success result. */
298 340139 : bool isOk() const { return mImpl.isOk(); }
299 :
300 : /** True if this Result is an error result. */
301 194200 : bool isErr() const { return !mImpl.isOk(); }
302 :
303 : /** Get the success value from this Result, which must be a success result. */
304 147242 : V unwrap() const {
305 147242 : MOZ_ASSERT(isOk());
306 147242 : return mImpl.unwrap();
307 : }
308 :
309 : /** Get the error value from this Result, which must be an error result. */
310 32 : E unwrapErr() const {
311 32 : MOZ_ASSERT(isErr());
312 32 : return mImpl.unwrapErr();
313 : }
314 :
315 : /**
316 : * Map a function V -> W over this result's success variant. If this result is
317 : * an error, do not invoke the function and return a copy of the error.
318 : *
319 : * Mapping over success values invokes the function to produce a new success
320 : * value:
321 : *
322 : * // Map Result<int, E> to another Result<int, E>
323 : * Result<int, E> res(5);
324 : * Result<int, E> res2 = res.map([](int x) { return x * x; });
325 : * MOZ_ASSERT(res2.unwrap() == 25);
326 : *
327 : * // Map Result<const char*, E> to Result<size_t, E>
328 : * Result<const char*, E> res("hello, map!");
329 : * Result<size_t, E> res2 = res.map(strlen);
330 : * MOZ_ASSERT(res2.unwrap() == 11);
331 : *
332 : * Mapping over an error does not invoke the function and copies the error:
333 : *
334 : * Result<V, int> res(5);
335 : * MOZ_ASSERT(res.isErr());
336 : * Result<W, int> res2 = res.map([](V v) { ... });
337 : * MOZ_ASSERT(res2.isErr());
338 : * MOZ_ASSERT(res2.unwrapErr() == 5);
339 : */
340 : template<typename F>
341 : auto map(F f) const -> Result<decltype(f(*((V*) nullptr))), E> {
342 : using RetResult = Result<decltype(f(*((V*) nullptr))), E>;
343 : return isOk() ? RetResult(f(unwrap())) : RetResult(unwrapErr());
344 : }
345 :
346 : /**
347 : * Given a function V -> Result<W, E>, apply it to this result's success value
348 : * and return its result. If this result is an error value, then return a
349 : * copy.
350 : *
351 : * This is sometimes called "flatMap" or ">>=" in other contexts.
352 : *
353 : * `andThen`ing over success values invokes the function to produce a new
354 : * result:
355 : *
356 : * Result<const char*, Error> res("hello, andThen!");
357 : * Result<HtmlFreeString, Error> res2 = res.andThen([](const char* s) {
358 : * return containsHtmlTag(s)
359 : * ? Result<HtmlFreeString, Error>(Error("Invalid: contains HTML"))
360 : * : Result<HtmlFreeString, Error>(HtmlFreeString(s));
361 : * }
362 : * });
363 : * MOZ_ASSERT(res2.isOk());
364 : * MOZ_ASSERT(res2.unwrap() == HtmlFreeString("hello, andThen!");
365 : *
366 : * `andThen`ing over error results does not invoke the function, and just
367 : * produces a new copy of the error result:
368 : *
369 : * Result<int, const char*> res("some error");
370 : * auto res2 = res.andThen([](int x) { ... });
371 : * MOZ_ASSERT(res2.isErr());
372 : * MOZ_ASSERT(res.unwrapErr() == res2.unwrapErr());
373 : */
374 : template<
375 : typename F,
376 : typename = typename EnableIf<
377 : detail::IsResult<decltype((*((F*) nullptr))(*((V*) nullptr)))>::value
378 : >::Type
379 : >
380 : auto andThen(F f) const -> decltype(f(*((V*) nullptr))) {
381 : return isOk() ? f(unwrap()) : GenericErrorResult<E>(unwrapErr());
382 : }
383 : };
384 :
385 : /**
386 : * A type that auto-converts to an error Result. This is like a Result without
387 : * a success type. It's the best return type for functions that always return
388 : * an error--functions designed to build and populate error objects. It's also
389 : * useful in error-handling macros; see MOZ_TRY for an example.
390 : */
391 : template <typename E>
392 : class MOZ_MUST_USE_TYPE GenericErrorResult
393 : {
394 : E mErrorValue;
395 :
396 : template<typename V, typename E2> friend class Result;
397 :
398 : public:
399 29 : explicit GenericErrorResult(E aErrorValue) : mErrorValue(aErrorValue) {}
400 : };
401 :
402 : template <typename E>
403 : inline GenericErrorResult<E>
404 29 : Err(E&& aErrorValue)
405 : {
406 29 : return GenericErrorResult<E>(aErrorValue);
407 : }
408 :
409 : } // namespace mozilla
410 :
411 : /**
412 : * MOZ_TRY(expr) is the C++ equivalent of Rust's `try!(expr);`. First, it
413 : * evaluates expr, which must produce a Result value. On success, it
414 : * discards the result altogether. On error, it immediately returns an error
415 : * Result from the enclosing function.
416 : */
417 : #define MOZ_TRY(expr) \
418 : do { \
419 : auto mozTryTempResult_ = (expr); \
420 : if (mozTryTempResult_.isErr()) { \
421 : return ::mozilla::Err(mozTryTempResult_.unwrapErr()); \
422 : } \
423 : } while (0)
424 :
425 : /**
426 : * MOZ_TRY_VAR(target, expr) is the C++ equivalent of Rust's `target = try!(expr);`.
427 : * First, it evaluates expr, which must produce a Result value.
428 : * On success, the result's success value is assigned to target.
429 : * On error, immediately returns the error result.
430 : * |target| must evaluate to a reference without any side effects.
431 : */
432 : #define MOZ_TRY_VAR(target, expr) \
433 : do { \
434 : auto mozTryVarTempResult_ = (expr); \
435 : if (mozTryVarTempResult_.isErr()) { \
436 : return ::mozilla::Err( \
437 : mozTryVarTempResult_.unwrapErr()); \
438 : } \
439 : (target) = mozTryVarTempResult_.unwrap(); \
440 : } while (0)
441 :
442 : #endif // mozilla_Result_h
|