Bug 1935816 - Make MOZ_TRY() macro return its success value instead of discarding it. r=glandium
This macro allows to write `mozilla::Result`-based code
similar to the rust try macro by using
gcc Statement Expressions [0],
which is a non-standard extension to gcc and clang.
This macro is used as an rvalue, while still returning early for error cases:
```
// Result<SuccessValue, E> Func();
Result<T, E> Bar() {
SuccessValue val = MOZ_TRY(Func());
// ...
}
```
This macro can only be used if `SuccessValue` is movable.
[0]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
Differential Revision: https://phabricator.services.mozilla.com/D235415
This commit is contained in:
committed by
jjaschke@mozilla.com
parent
9fa68a2225
commit
0a20c29367
45
mfbt/Try.h
45
mfbt/Try.h
@@ -10,32 +10,37 @@
|
||||
#include "mozilla/Result.h"
|
||||
|
||||
/**
|
||||
* MOZ_TRY(expr) is the C++ equivalent of Rust's `try!(expr);`. First, it
|
||||
* evaluates expr, which must produce a Result value. On success, it
|
||||
* discards the result altogether. On error, it immediately returns an error
|
||||
* Result from the enclosing function.
|
||||
* MOZ_TRY(expr) is the C++ equivalent of Rust's `target = try!(expr);`, using
|
||||
* gcc's statement expressions [0]. First, it evaluates expr, which must produce
|
||||
* a Result value. On success, the result's success value is 'returned' as
|
||||
* rvalue. On error, immediately returns the error result. This pattern allows
|
||||
* to directly assign the success value:
|
||||
*
|
||||
* ```
|
||||
* SuccessValue val = MOZ_TRY(Func());
|
||||
* ```
|
||||
*
|
||||
* Where `Func()` returns a `Result<SuccessValue, E>` and is called in a
|
||||
* function that returns `Result<T, E>`.
|
||||
*
|
||||
* [0]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
|
||||
*/
|
||||
#define MOZ_TRY(expr) \
|
||||
do { \
|
||||
auto mozTryTempResult_ = ::mozilla::ToResult(expr); \
|
||||
if (MOZ_UNLIKELY(mozTryTempResult_.isErr())) { \
|
||||
return mozTryTempResult_.propagateErr(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MOZ_TRY(expr) \
|
||||
__extension__({ \
|
||||
auto mozTryVarTempResult = ::mozilla::ToResult(expr); \
|
||||
if (MOZ_UNLIKELY(mozTryVarTempResult.isErr())) { \
|
||||
return mozTryVarTempResult.propagateErr(); \
|
||||
} \
|
||||
mozTryVarTempResult.unwrap(); \
|
||||
})
|
||||
/**
|
||||
* MOZ_TRY_VAR(target, expr) is the C++ equivalent of Rust's `target =
|
||||
* try!(expr);`. First, it evaluates expr, which must produce a Result value. On
|
||||
* success, the result's success value is assigned to target. On error,
|
||||
* immediately returns the error result. |target| must be an lvalue.
|
||||
*
|
||||
* This macro is obsolete and its usages should be replaced with `MOZ_TRY`.
|
||||
*/
|
||||
#define MOZ_TRY_VAR(target, expr) \
|
||||
do { \
|
||||
auto mozTryVarTempResult_ = (expr); \
|
||||
if (MOZ_UNLIKELY(mozTryVarTempResult_.isErr())) { \
|
||||
return mozTryVarTempResult_.propagateErr(); \
|
||||
} \
|
||||
(target) = mozTryVarTempResult_.unwrap(); \
|
||||
} while (0)
|
||||
#define MOZ_TRY_VAR(target, expr) (target) = MOZ_TRY(expr);
|
||||
|
||||
#endif // mozilla_Try_h
|
||||
|
||||
@@ -152,6 +152,12 @@ static Result<int, Failed> Task3(bool pass1, bool pass2, int value) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
static Result<int, Failed> Task4(bool pass1, bool pass2, int value) {
|
||||
auto x = MOZ_TRY(Task2(pass1, value));
|
||||
auto y = MOZ_TRY(Task2(pass2, value));
|
||||
return x + y;
|
||||
}
|
||||
|
||||
static void BasicTests() {
|
||||
MOZ_STATIC_AND_RELEASE_ASSERT(Task1(true).isOk());
|
||||
MOZ_STATIC_AND_RELEASE_ASSERT(!Task1(true).isErr());
|
||||
@@ -168,9 +174,9 @@ static void BasicTests() {
|
||||
Task1UnusedZeroEnumErr(false).unwrapErr());
|
||||
|
||||
// MOZ_TRY works.
|
||||
MOZ_STATIC_AND_RELEASE_ASSERT(Task2(true, 3).isOk());
|
||||
MOZ_STATIC_AND_RELEASE_ASSERT(Task2(true, 3).unwrap() == 3);
|
||||
MOZ_STATIC_AND_RELEASE_ASSERT(Task2(true, 3).unwrapOr(6) == 3);
|
||||
MOZ_RELEASE_ASSERT(Task2(true, 3).isOk());
|
||||
MOZ_RELEASE_ASSERT(Task2(true, 3).unwrap() == 3);
|
||||
MOZ_RELEASE_ASSERT(Task2(true, 3).unwrapOr(6) == 3);
|
||||
MOZ_RELEASE_ASSERT(Task2(false, 3).isErr());
|
||||
MOZ_RELEASE_ASSERT(Task2(false, 3).unwrapOr(6) == 6);
|
||||
|
||||
@@ -178,9 +184,14 @@ static void BasicTests() {
|
||||
MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).unwrap() == 3);
|
||||
MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).unwrapOr(6) ==
|
||||
3);
|
||||
MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).isErr());
|
||||
MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).unwrapOr(6) ==
|
||||
6);
|
||||
MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).isErr());
|
||||
MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).unwrapOr(6) == 6);
|
||||
|
||||
MOZ_RELEASE_ASSERT(Task4(true, true, 3).isOk());
|
||||
MOZ_RELEASE_ASSERT(Task4(true, true, 3).unwrap() == 6);
|
||||
MOZ_RELEASE_ASSERT(Task4(true, false, 3).isErr());
|
||||
MOZ_RELEASE_ASSERT(Task4(false, true, 3).isErr());
|
||||
MOZ_RELEASE_ASSERT(Task4(false, true, 3).unwrapOr(6) == 6);
|
||||
|
||||
// MOZ_TRY_VAR works.
|
||||
MOZ_RELEASE_ASSERT(Task3(true, true, 3).isOk());
|
||||
|
||||
Reference in New Issue
Block a user