Bug 1601556 - Make Result<V, E> a literal type if V and E are literal types and PackingStrategy is not Variant. r=emilio

Also make CompactPair<A, B> a literal type if A and B are literal types,
and add MaybeStorageBase that ought to be used as a basis of MaybeStorage
in a follow-up patch.

Differential Revision: https://phabricator.services.mozilla.com/D55930
This commit is contained in:
Simon Giesecke 2021-03-23 12:35:19 +00:00
Родитель f74737684e
Коммит ae75c3faa8
9 изменённых файлов: 293 добавлений и 175 удалений

Просмотреть файл

@ -43,22 +43,22 @@ struct CompactPairHelper<A, B, AsMember, AsMember> {
protected:
template <typename... AArgs, std::size_t... AIndexes, typename... BArgs,
std::size_t... BIndexes>
CompactPairHelper(std::tuple<AArgs...>& aATuple,
std::tuple<BArgs...>& aBTuple,
std::index_sequence<AIndexes...>,
std::index_sequence<BIndexes...>)
constexpr CompactPairHelper(std::tuple<AArgs...>& aATuple,
std::tuple<BArgs...>& aBTuple,
std::index_sequence<AIndexes...>,
std::index_sequence<BIndexes...>)
: mFirstA(std::forward<AArgs>(std::get<AIndexes>(aATuple))...),
mSecondB(std::forward<BArgs>(std::get<BIndexes>(aBTuple))...) {}
public:
template <typename AArg, typename BArg>
CompactPairHelper(AArg&& aA, BArg&& aB)
constexpr CompactPairHelper(AArg&& aA, BArg&& aB)
: mFirstA(std::forward<AArg>(aA)), mSecondB(std::forward<BArg>(aB)) {}
A& first() { return mFirstA; }
const A& first() const { return mFirstA; }
B& second() { return mSecondB; }
const B& second() const { return mSecondB; }
constexpr A& first() { return mFirstA; }
constexpr const A& first() const { return mFirstA; }
constexpr B& second() { return mSecondB; }
constexpr const B& second() const { return mSecondB; }
void swap(CompactPairHelper& aOther) {
std::swap(mFirstA, aOther.mFirstA);
@ -75,22 +75,22 @@ struct CompactPairHelper<A, B, AsMember, AsBase> : private B {
protected:
template <typename... AArgs, std::size_t... AIndexes, typename... BArgs,
std::size_t... BIndexes>
CompactPairHelper(std::tuple<AArgs...>& aATuple,
std::tuple<BArgs...>& aBTuple,
std::index_sequence<AIndexes...>,
std::index_sequence<BIndexes...>)
constexpr CompactPairHelper(std::tuple<AArgs...>& aATuple,
std::tuple<BArgs...>& aBTuple,
std::index_sequence<AIndexes...>,
std::index_sequence<BIndexes...>)
: B(std::forward<BArgs>(std::get<BIndexes>(aBTuple))...),
mFirstA(std::forward<AArgs>(std::get<AIndexes>(aATuple))...) {}
public:
template <typename AArg, typename BArg>
CompactPairHelper(AArg&& aA, BArg&& aB)
constexpr CompactPairHelper(AArg&& aA, BArg&& aB)
: B(std::forward<BArg>(aB)), mFirstA(std::forward<AArg>(aA)) {}
A& first() { return mFirstA; }
const A& first() const { return mFirstA; }
B& second() { return *this; }
const B& second() const { return *this; }
constexpr A& first() { return mFirstA; }
constexpr const A& first() const { return mFirstA; }
constexpr B& second() { return *this; }
constexpr const B& second() const { return *this; }
void swap(CompactPairHelper& aOther) {
std::swap(mFirstA, aOther.mFirstA);
@ -106,22 +106,22 @@ struct CompactPairHelper<A, B, AsBase, AsMember> : private A {
protected:
template <typename... AArgs, std::size_t... AIndexes, typename... BArgs,
std::size_t... BIndexes>
CompactPairHelper(std::tuple<AArgs...>& aATuple,
std::tuple<BArgs...>& aBTuple,
std::index_sequence<AIndexes...>,
std::index_sequence<BIndexes...>)
constexpr CompactPairHelper(std::tuple<AArgs...>& aATuple,
std::tuple<BArgs...>& aBTuple,
std::index_sequence<AIndexes...>,
std::index_sequence<BIndexes...>)
: A(std::forward<AArgs>(std::get<AIndexes>(aATuple))...),
mSecondB(std::forward<BArgs>(std::get<BIndexes>(aBTuple))...) {}
public:
template <typename AArg, typename BArg>
CompactPairHelper(AArg&& aA, BArg&& aB)
constexpr CompactPairHelper(AArg&& aA, BArg&& aB)
: A(std::forward<AArg>(aA)), mSecondB(std::forward<BArg>(aB)) {}
A& first() { return *this; }
const A& first() const { return *this; }
B& second() { return mSecondB; }
const B& second() const { return mSecondB; }
constexpr A& first() { return *this; }
constexpr const A& first() const { return *this; }
constexpr B& second() { return mSecondB; }
constexpr const B& second() const { return mSecondB; }
void swap(CompactPairHelper& aOther) {
std::swap(static_cast<A&>(*this), static_cast<A&>(aOther));
@ -137,22 +137,22 @@ struct CompactPairHelper<A, B, AsBase, AsBase> : private A, private B {
protected:
template <typename... AArgs, std::size_t... AIndexes, typename... BArgs,
std::size_t... BIndexes>
CompactPairHelper(std::tuple<AArgs...>& aATuple,
std::tuple<BArgs...>& aBTuple,
std::index_sequence<AIndexes...>,
std::index_sequence<BIndexes...>)
constexpr CompactPairHelper(std::tuple<AArgs...>& aATuple,
std::tuple<BArgs...>& aBTuple,
std::index_sequence<AIndexes...>,
std::index_sequence<BIndexes...>)
: A(std::forward<AArgs>(std::get<AIndexes>(aATuple))...),
B(std::forward<BArgs>(std::get<BIndexes>(aBTuple))...) {}
public:
template <typename AArg, typename BArg>
CompactPairHelper(AArg&& aA, BArg&& aB)
constexpr CompactPairHelper(AArg&& aA, BArg&& aB)
: A(std::forward<AArg>(aA)), B(std::forward<BArg>(aB)) {}
A& first() { return static_cast<A&>(*this); }
const A& first() const { return static_cast<A&>(*this); }
B& second() { return static_cast<B&>(*this); }
const B& second() const { return static_cast<B&>(*this); }
constexpr A& first() { return static_cast<A&>(*this); }
constexpr const A& first() const { return static_cast<A&>(*this); }
constexpr B& second() { return static_cast<B&>(*this); }
constexpr const B& second() const { return static_cast<B&>(*this); }
void swap(CompactPairHelper& aOther) {
std::swap(static_cast<A&>(*this), static_cast<A&>(aOther));
@ -185,8 +185,8 @@ struct CompactPair : private detail::CompactPairHelper<A, B> {
using Base::Base;
template <typename... AArgs, typename... BArgs>
CompactPair(std::piecewise_construct_t, std::tuple<AArgs...> aFirst,
std::tuple<BArgs...> aSecond)
constexpr CompactPair(std::piecewise_construct_t, std::tuple<AArgs...> aFirst,
std::tuple<BArgs...> aSecond)
: Base(aFirst, aSecond, std::index_sequence_for<AArgs...>(),
std::index_sequence_for<BArgs...>()) {}

Просмотреть файл

@ -17,6 +17,7 @@
#include "mozilla/Alignment.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/MaybeStorageBase.h"
#include "mozilla/MemoryChecking.h"
#include "mozilla/OperatorNewExtensions.h"
#include "mozilla/Poison.h"
@ -103,12 +104,6 @@ struct MaybePoisoner {
}
};
template <typename T>
constexpr bool IsTriviallyDestructibleAndCopyable =
std::is_trivially_destructible_v<T> &&
(std::is_trivially_copy_constructible_v<T> ||
!std::is_copy_constructible_v<T>);
template <typename T,
bool TriviallyDestructibleAndCopyable =
IsTriviallyDestructibleAndCopyable<T>,

94
mfbt/MaybeStorageBase.h Normal file
Просмотреть файл

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Internal storage class used e.g. by Maybe and Result. This file doesn't
* contain any public declarations. */
#ifndef mfbt_MaybeStorageBase_h
#define mfbt_MaybeStorageBase_h
#include <type_traits>
#include <utility>
namespace mozilla::detail {
template <typename T>
constexpr bool IsTriviallyDestructibleAndCopyable =
std::is_trivially_destructible_v<T> &&
(std::is_trivially_copy_constructible_v<T> ||
!std::is_copy_constructible_v<T>);
template <typename T, bool TriviallyDestructibleAndCopyable =
IsTriviallyDestructibleAndCopyable<T>>
struct MaybeStorageBase;
struct Dummy {};
template <typename T>
struct MaybeStorageBase<T, false> {
protected:
using NonConstT = std::remove_const_t<T>;
union Union {
Union() {}
explicit Union(const T& aVal) : val{aVal} {}
template <typename U,
typename = std::enable_if_t<std::is_move_constructible_v<U>>>
explicit Union(U&& aVal) : val{std::forward<U>(aVal)} {}
template <typename... Args>
explicit Union(std::in_place_t, Args&&... aArgs)
: val{std::forward<Args>(aArgs)...} {}
~Union() {}
NonConstT val;
} mStorage;
public:
MaybeStorageBase() = default;
explicit MaybeStorageBase(const T& aVal) : mStorage{aVal} {}
explicit MaybeStorageBase(T&& aVal) : mStorage{std::move(aVal)} {}
template <typename... Args>
explicit MaybeStorageBase(std::in_place_t, Args&&... aArgs)
: mStorage{std::in_place, std::forward<Args>(aArgs)...} {}
const T* addr() const { return &mStorage.val; }
T* addr() { return &mStorage.val; }
};
template <typename T>
struct MaybeStorageBase<T, true> {
protected:
using NonConstT = std::remove_const_t<T>;
union Union {
constexpr Union() : dummy() {}
constexpr explicit Union(const T& aVal) : val{aVal} {}
constexpr explicit Union(T&& aVal) : val{std::move(aVal)} {}
template <typename... Args>
constexpr explicit Union(std::in_place_t, Args&&... aArgs)
: val{std::forward<Args>(aArgs)...} {}
NonConstT val;
Dummy dummy;
} mStorage;
public:
constexpr MaybeStorageBase() = default;
constexpr explicit MaybeStorageBase(const T& aVal) : mStorage{aVal} {}
constexpr explicit MaybeStorageBase(T&& aVal) : mStorage{std::move(aVal)} {}
template <typename... Args>
constexpr explicit MaybeStorageBase(std::in_place_t, Args&&... aArgs)
: mStorage{std::in_place, std::forward<Args>(aArgs)...} {}
constexpr const T* addr() const { return &mStorage.val; }
constexpr T* addr() { return &mStorage.val; }
};
} // namespace mozilla::detail
#endif

Просмотреть файл

@ -16,6 +16,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/CompactPair.h"
#include "mozilla/MaybeStorageBase.h"
namespace mozilla {
@ -46,28 +47,25 @@ struct UnusedZero;
template <typename V, typename E, PackingStrategy Strategy>
class ResultImplementation;
template <typename V>
struct EmptyWrapper : V {
constexpr EmptyWrapper() = default;
explicit constexpr EmptyWrapper(const V&) {}
explicit constexpr EmptyWrapper(std::in_place_t) {}
constexpr V* addr() { return this; }
constexpr const V* addr() const { return this; }
};
// The purpose of AlignedStorageOrEmpty is to make an empty class look like
// std::aligned_storage_t for the purposes of the PackingStrategy::NullIsOk
// specializations of ResultImplementation below. We can't use
// std::aligned_storage_t itself with an empty class, since it would no longer
// be empty.
template <typename V, bool IsEmpty = std::is_empty_v<V>>
struct AlignedStorageOrEmpty;
template <typename V>
struct AlignedStorageOrEmpty<V, true> : V {
constexpr V* addr() { return this; }
constexpr const V* addr() const { return this; }
};
template <typename V>
struct AlignedStorageOrEmpty<V, false> {
V* addr() { return reinterpret_cast<V*>(&mData); }
const V* addr() const { return reinterpret_cast<const V*>(&mData); }
private:
std::aligned_storage_t<sizeof(V), alignof(V)> mData;
};
using AlignedStorageOrEmpty =
std::conditional_t<std::is_empty_v<V>, EmptyWrapper<V>,
MaybeStorageBase<V>>;
template <typename V, typename E>
class ResultImplementationNullIsOkBase {
@ -85,35 +83,24 @@ class ResultImplementationNullIsOkBase {
CompactPair<AlignedStorageOrEmpty<V>, ErrorStorageType> mValue;
public:
explicit ResultImplementationNullIsOkBase(const V& aSuccessValue)
: mValue(std::piecewise_construct, std::tuple<>(),
std::tuple(kNullValue)) {
if constexpr (!std::is_empty_v<V>) {
new (mValue.first().addr()) V(aSuccessValue);
}
}
explicit ResultImplementationNullIsOkBase(V&& aSuccessValue)
: mValue(std::piecewise_construct, std::tuple<>(),
std::tuple(kNullValue)) {
if constexpr (!std::is_empty_v<V>) {
new (mValue.first().addr()) V(std::move(aSuccessValue));
}
}
explicit constexpr ResultImplementationNullIsOkBase(const V& aSuccessValue)
: mValue(aSuccessValue, kNullValue) {}
explicit constexpr ResultImplementationNullIsOkBase(V&& aSuccessValue)
: mValue(std::move(aSuccessValue), kNullValue) {}
template <typename... Args>
explicit ResultImplementationNullIsOkBase(std::in_place_t, Args&&... aArgs)
: mValue(std::piecewise_construct, std::tuple<>(),
std::tuple(kNullValue)) {
if constexpr (!std::is_empty_v<V>) {
new (mValue.first().addr()) V(std::forward<Args>(aArgs)...);
}
}
explicit ResultImplementationNullIsOkBase(E aErrorValue)
explicit constexpr ResultImplementationNullIsOkBase(std::in_place_t,
Args&&... aArgs)
: mValue(std::piecewise_construct,
std::tuple(std::in_place, std::forward<Args>(aArgs)...),
std::tuple(kNullValue)) {}
explicit constexpr ResultImplementationNullIsOkBase(E aErrorValue)
: mValue(std::piecewise_construct, std::tuple<>(),
std::tuple(UnusedZero<E>::Store(std::move(aErrorValue)))) {
MOZ_ASSERT(mValue.second() != kNullValue);
}
ResultImplementationNullIsOkBase(ResultImplementationNullIsOkBase&& aOther)
constexpr ResultImplementationNullIsOkBase(
ResultImplementationNullIsOkBase&& aOther)
: mValue(std::piecewise_construct, std::tuple<>(),
std::tuple(aOther.mValue.second())) {
if constexpr (!std::is_empty_v<V>) {
@ -138,15 +125,15 @@ class ResultImplementationNullIsOkBase {
return *this;
}
bool isOk() const { return mValue.second() == kNullValue; }
constexpr bool isOk() const { return mValue.second() == kNullValue; }
const V& inspect() const { return *mValue.first().addr(); }
V unwrap() { return std::move(*mValue.first().addr()); }
constexpr const V& inspect() const { return *mValue.first().addr(); }
constexpr V unwrap() { return std::move(*mValue.first().addr()); }
decltype(auto) inspectErr() const {
constexpr decltype(auto) inspectErr() const {
return UnusedZero<E>::Inspect(mValue.second());
}
E unwrapErr() { return UnusedZero<E>::Unwrap(mValue.second()); }
constexpr E unwrapErr() { return UnusedZero<E>::Unwrap(mValue.second()); }
};
template <typename V, typename E,
@ -226,42 +213,40 @@ class ResultImplementation<V, E, PackingStrategy::LowBitTagIsError> {
#endif
public:
explicit ResultImplementation(V aValue) {
explicit constexpr ResultImplementation(V aValue) : mBits(0) {
if constexpr (!std::is_empty_v<V>) {
std::memcpy(&mBits, &aValue, sizeof(V));
MOZ_ASSERT((mBits & 1) == 0);
} else {
(void)aValue;
mBits = 0;
}
}
explicit ResultImplementation(E aErrorValue) {
explicit constexpr ResultImplementation(E aErrorValue) : mBits(1) {
if constexpr (!std::is_empty_v<E>) {
std::memcpy(&mBits, &aErrorValue, sizeof(E));
MOZ_ASSERT((mBits & 1) == 0);
mBits |= 1;
} else {
(void)aErrorValue;
mBits = 1;
}
}
bool isOk() const { return (mBits & 1) == 0; }
constexpr bool isOk() const { return (mBits & 1) == 0; }
V inspect() const {
constexpr V inspect() const {
V res;
std::memcpy(&res, &mBits, sizeof(V));
return res;
}
V unwrap() { return inspect(); }
constexpr V unwrap() { return inspect(); }
E inspectErr() const {
constexpr E inspectErr() const {
const auto bits = mBits ^ 1;
E res;
std::memcpy(&res, &bits, sizeof(E));
return res;
}
E unwrapErr() { return inspectErr(); }
constexpr E unwrapErr() { return inspectErr(); }
};
// Return true if any of the struct can fit in a word.
@ -294,22 +279,22 @@ class ResultImplementation<V, E, PackingStrategy::PackedVariant> {
Impl data;
public:
explicit ResultImplementation(V aValue) {
explicit constexpr ResultImplementation(V aValue) {
data.v = std::move(aValue);
data.ok = true;
}
explicit ResultImplementation(E aErrorValue) {
explicit constexpr ResultImplementation(E aErrorValue) {
data.e = std::move(aErrorValue);
data.ok = false;
}
bool isOk() const { return data.ok; }
constexpr bool isOk() const { return data.ok; }
const V& inspect() const { return data.v; }
V unwrap() { return std::move(data.v); }
constexpr const V& inspect() const { return data.v; }
constexpr V unwrap() { return std::move(data.v); }
const E& inspectErr() const { return data.e; }
E unwrapErr() { return std::move(data.e); }
constexpr const E& inspectErr() const { return data.e; }
constexpr E unwrapErr() { return std::move(data.e); }
};
// To use nullptr as a special value, we need the counter part to exclude zero
@ -398,7 +383,7 @@ struct IsResult<Result<V, E>> : std::true_type {};
} // namespace detail
template <typename V, typename E>
auto ToResult(Result<V, E>&& aValue)
constexpr auto ToResult(Result<V, E>&& aValue)
-> decltype(std::forward<Result<V, E>>(aValue)) {
return std::forward<Result<V, E>>(aValue);
}
@ -461,22 +446,24 @@ class MOZ_MUST_USE_TYPE Result final {
using err_type = E;
/** Create a success result. */
MOZ_IMPLICIT Result(V&& aValue) : mImpl(std::forward<V>(aValue)) {
MOZ_IMPLICIT constexpr Result(V&& aValue) : mImpl(std::forward<V>(aValue)) {
MOZ_ASSERT(isOk());
}
/** Create a success result. */
MOZ_IMPLICIT Result(const V& aValue) : mImpl(aValue) { MOZ_ASSERT(isOk()); }
MOZ_IMPLICIT constexpr Result(const V& aValue) : mImpl(aValue) {
MOZ_ASSERT(isOk());
}
/** Create a success result in-place. */
template <typename... Args>
explicit Result(std::in_place_t, Args&&... aArgs)
explicit constexpr Result(std::in_place_t, Args&&... aArgs)
: mImpl(std::in_place, std::forward<Args>(aArgs)...) {
MOZ_ASSERT(isOk());
}
/** Create an error result. */
explicit Result(E aErrorValue) : mImpl(std::move(aErrorValue)) {
explicit constexpr Result(E aErrorValue) : mImpl(std::move(aErrorValue)) {
MOZ_ASSERT(isErr());
}
@ -485,7 +472,7 @@ class MOZ_MUST_USE_TYPE Result final {
* different but convertible error type. */
template <typename E2,
typename = std::enable_if_t<std::is_convertible_v<E2, E>>>
MOZ_IMPLICIT Result(Result<V, E2>&& aOther)
MOZ_IMPLICIT constexpr Result(Result<V, E2>&& aOther)
: mImpl(aOther.isOk() ? Impl{aOther.unwrap()}
: Impl{aOther.unwrapErr()}) {}
@ -494,7 +481,7 @@ class MOZ_MUST_USE_TYPE Result final {
* Create an error result from another error result.
*/
template <typename E2>
MOZ_IMPLICIT Result(GenericErrorResult<E2>&& aErrorResult)
MOZ_IMPLICIT constexpr Result(GenericErrorResult<E2>&& aErrorResult)
: mImpl(std::move(aErrorResult.mErrorValue)) {
static_assert(std::is_convertible_v<E2, E>, "E2 must be convertible to E");
MOZ_ASSERT(isErr());
@ -505,7 +492,7 @@ class MOZ_MUST_USE_TYPE Result final {
* Create an error result from another error result.
*/
template <typename E2>
MOZ_IMPLICIT Result(const GenericErrorResult<E2>& aErrorResult)
MOZ_IMPLICIT constexpr Result(const GenericErrorResult<E2>& aErrorResult)
: mImpl(aErrorResult.mErrorValue) {
static_assert(std::is_convertible_v<E2, E>, "E2 must be convertible to E");
MOZ_ASSERT(isErr());
@ -517,14 +504,14 @@ class MOZ_MUST_USE_TYPE Result final {
Result& operator=(Result&&) = default;
/** True if this Result is a success result. */
bool isOk() const { return mImpl.isOk(); }
constexpr bool isOk() const { return mImpl.isOk(); }
/** True if this Result is an error result. */
bool isErr() const { return !mImpl.isOk(); }
constexpr bool isErr() const { return !mImpl.isOk(); }
/** Take the success value from this Result, which must be a success result.
*/
V unwrap() {
constexpr V unwrap() {
MOZ_ASSERT(isOk());
return mImpl.unwrap();
}
@ -533,18 +520,18 @@ class MOZ_MUST_USE_TYPE Result final {
* Take the success value from this Result, which must be a success result.
* If it is an error result, then return the aValue.
*/
V unwrapOr(V aValue) {
constexpr V unwrapOr(V aValue) {
return MOZ_LIKELY(isOk()) ? mImpl.unwrap() : std::move(aValue);
}
/** Take the error value from this Result, which must be an error result. */
E unwrapErr() {
constexpr E unwrapErr() {
MOZ_ASSERT(isErr());
return mImpl.unwrapErr();
}
/** See the success value from this Result, which must be a success result. */
decltype(auto) inspect() const {
constexpr decltype(auto) inspect() const {
static_assert(!std::is_reference_v<
std::invoke_result_t<decltype(&Impl::inspect), Impl>> ||
std::is_const_v<std::remove_reference_t<
@ -554,7 +541,7 @@ class MOZ_MUST_USE_TYPE Result final {
}
/** See the error value from this Result, which must be an error result. */
decltype(auto) inspectErr() const {
constexpr decltype(auto) inspectErr() const {
static_assert(
!std::is_reference_v<
std::invoke_result_t<decltype(&Impl::inspectErr), Impl>> ||
@ -574,7 +561,7 @@ class MOZ_MUST_USE_TYPE Result final {
* if (res.isErr()) { return res.propagateErr(); }
* }
*/
GenericErrorResult<E> propagateErr() {
constexpr GenericErrorResult<E> propagateErr() {
MOZ_ASSERT(isErr());
return GenericErrorResult<E>{mImpl.unwrapErr()};
}
@ -608,7 +595,7 @@ class MOZ_MUST_USE_TYPE Result final {
* MOZ_ASSERT(res2.unwrapErr() == 5);
*/
template <typename F>
auto map(F f) -> Result<std::result_of_t<F(V)>, E> {
constexpr auto map(F f) -> Result<std::result_of_t<F(V)>, E> {
using RetResult = Result<std::result_of_t<F(V)>, E>;
return MOZ_LIKELY(isOk()) ? RetResult(f(unwrap())) : RetResult(unwrapErr());
}
@ -641,7 +628,7 @@ class MOZ_MUST_USE_TYPE Result final {
* MOZ_ASSERT(res2.unwrap() == 5);
*/
template <typename F>
auto mapErr(F f) -> Result<V, std::result_of_t<F(E)>> {
constexpr auto mapErr(F f) {
using RetResult = Result<V, std::result_of_t<F(E)>>;
return MOZ_UNLIKELY(isErr()) ? RetResult(f(unwrapErr()))
: RetResult(unwrap());
@ -737,7 +724,7 @@ class MOZ_MUST_USE_TYPE Result final {
*/
template <typename F, typename = std::enable_if_t<detail::IsResult<
std::invoke_result_t<F, V&&>>::value>>
auto andThen(F f) -> std::invoke_result_t<F, V&&> {
constexpr auto andThen(F f) -> std::invoke_result_t<F, V&&> {
return MOZ_LIKELY(isOk()) ? f(unwrap()) : propagateErr();
}
};
@ -756,15 +743,15 @@ class MOZ_MUST_USE_TYPE GenericErrorResult {
friend class Result;
public:
explicit GenericErrorResult(const E& aErrorValue)
explicit constexpr GenericErrorResult(const E& aErrorValue)
: mErrorValue(aErrorValue) {}
explicit GenericErrorResult(E&& aErrorValue)
explicit constexpr GenericErrorResult(E&& aErrorValue)
: mErrorValue(std::move(aErrorValue)) {}
};
template <typename E>
inline auto Err(E&& aErrorValue) {
inline constexpr auto Err(E&& aErrorValue) {
return GenericErrorResult<std::decay_t<E>>(std::forward<E>(aErrorValue));
}

Просмотреть файл

@ -9,6 +9,7 @@
#ifndef mozilla_ResultVariant_h
#define mozilla_ResultVariant_h
#include "mozilla/MaybeStorageBase.h"
#include "mozilla/Result.h"
#include "mozilla/Variant.h"

Просмотреть файл

@ -67,6 +67,7 @@ EXPORTS.mozilla = [
"MathAlgorithms.h",
"Maybe.h",
"MaybeOneOf.h",
"MaybeStorageBase.h",
"MemoryChecking.h",
"MemoryReporting.h",
"NonDereferenceable.h",

Просмотреть файл

@ -14,6 +14,10 @@ using mozilla::Ok;
using mozilla::Result;
using mozilla::UniquePtr;
#define MOZ_STATIC_AND_RELEASE_ASSERT(expr) \
static_assert(expr); \
MOZ_RELEASE_ASSERT(expr)
enum struct TestUnusedZeroEnum : int16_t { Ok = 0, NotOk = 1 };
namespace mozilla::detail {
@ -94,23 +98,28 @@ static_assert(sizeof(Foo32) >= sizeof(uintptr_t) ||
sizeof(Result<Foo16, Foo32>) <= sizeof(uintptr_t),
"Result with small types should be pointer-sized");
static GenericErrorResult<Failed> Fail() {
static Failed failed;
return Err(failed);
}
static_assert(std::is_literal_type_v<Result<int*, Failed>>);
static_assert(std::is_literal_type_v<Result<Ok, Failed>>);
static_assert(std::is_literal_type_v<Result<Ok, Foo8>>);
static_assert(std::is_literal_type_v<Result<Foo8, Foo16>>);
static_assert(!std::is_literal_type_v<Result<Ok, UniquePtr<int>>>);
static GenericErrorResult<TestUnusedZeroEnum> FailTestUnusedZeroEnum() {
static constexpr GenericErrorResult<Failed> Fail() { return Err(Failed{}); }
static constexpr GenericErrorResult<TestUnusedZeroEnum>
FailTestUnusedZeroEnum() {
return Err(TestUnusedZeroEnum::NotOk);
}
static Result<Ok, Failed> Task1(bool pass) {
static constexpr Result<Ok, Failed> Task1(bool pass) {
if (!pass) {
return Fail(); // implicit conversion from GenericErrorResult to Result
}
return Ok();
}
static Result<Ok, TestUnusedZeroEnum> Task1UnusedZeroEnumErr(bool pass) {
static constexpr Result<Ok, TestUnusedZeroEnum> Task1UnusedZeroEnumErr(
bool pass) {
if (!pass) {
return FailTestUnusedZeroEnum(); // implicit conversion from
// GenericErrorResult to Result
@ -118,14 +127,14 @@ static Result<Ok, TestUnusedZeroEnum> Task1UnusedZeroEnumErr(bool pass) {
return Ok();
}
static Result<int, Failed> Task2(bool pass, int value) {
static constexpr Result<int, Failed> Task2(bool pass, int value) {
MOZ_TRY(
Task1(pass)); // converts one type of result to another in the error case
return value; // implicit conversion from T to Result<T, E>
}
static Result<int, TestUnusedZeroEnum> Task2UnusedZeroEnumErr(bool pass,
int value) {
static constexpr Result<int, TestUnusedZeroEnum> Task2UnusedZeroEnumErr(
bool pass, int value) {
MOZ_TRY(Task1UnusedZeroEnumErr(
pass)); // converts one type of result to another in the error case
return value; // implicit conversion from T to Result<T, E>
@ -139,32 +148,34 @@ static Result<int, Failed> Task3(bool pass1, bool pass2, int value) {
}
static void BasicTests() {
MOZ_RELEASE_ASSERT(Task1(true).isOk());
MOZ_RELEASE_ASSERT(!Task1(true).isErr());
MOZ_RELEASE_ASSERT(!Task1(false).isOk());
MOZ_RELEASE_ASSERT(Task1(false).isErr());
MOZ_STATIC_AND_RELEASE_ASSERT(Task1(true).isOk());
MOZ_STATIC_AND_RELEASE_ASSERT(!Task1(true).isErr());
MOZ_STATIC_AND_RELEASE_ASSERT(!Task1(false).isOk());
MOZ_STATIC_AND_RELEASE_ASSERT(Task1(false).isErr());
MOZ_RELEASE_ASSERT(Task1UnusedZeroEnumErr(true).isOk());
MOZ_RELEASE_ASSERT(!Task1UnusedZeroEnumErr(true).isErr());
MOZ_RELEASE_ASSERT(!Task1UnusedZeroEnumErr(false).isOk());
MOZ_RELEASE_ASSERT(Task1UnusedZeroEnumErr(false).isErr());
MOZ_RELEASE_ASSERT(TestUnusedZeroEnum::NotOk ==
Task1UnusedZeroEnumErr(false).inspectErr());
MOZ_RELEASE_ASSERT(TestUnusedZeroEnum::NotOk ==
Task1UnusedZeroEnumErr(false).unwrapErr());
MOZ_STATIC_AND_RELEASE_ASSERT(Task1UnusedZeroEnumErr(true).isOk());
MOZ_STATIC_AND_RELEASE_ASSERT(!Task1UnusedZeroEnumErr(true).isErr());
MOZ_STATIC_AND_RELEASE_ASSERT(!Task1UnusedZeroEnumErr(false).isOk());
MOZ_STATIC_AND_RELEASE_ASSERT(Task1UnusedZeroEnumErr(false).isErr());
MOZ_STATIC_AND_RELEASE_ASSERT(TestUnusedZeroEnum::NotOk ==
Task1UnusedZeroEnumErr(false).inspectErr());
MOZ_STATIC_AND_RELEASE_ASSERT(TestUnusedZeroEnum::NotOk ==
Task1UnusedZeroEnumErr(false).unwrapErr());
// MOZ_TRY works.
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_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(false, 3).isErr());
MOZ_RELEASE_ASSERT(Task2(false, 3).unwrapOr(6) == 6);
MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).isOk());
MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).unwrap() == 3);
MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).unwrapOr(6) == 3);
MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).isErr());
MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).unwrapOr(6) == 6);
MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).isOk());
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_TRY_VAR works.
MOZ_RELEASE_ASSERT(Task3(true, true, 3).isOk());
@ -175,13 +186,13 @@ static void BasicTests() {
// Lvalues should work too.
{
Result<Ok, Failed> res = Task1(true);
MOZ_RELEASE_ASSERT(res.isOk());
MOZ_RELEASE_ASSERT(!res.isErr());
constexpr Result<Ok, Failed> res1 = Task1(true);
MOZ_STATIC_AND_RELEASE_ASSERT(res1.isOk());
MOZ_STATIC_AND_RELEASE_ASSERT(!res1.isErr());
res = Task1(false);
MOZ_RELEASE_ASSERT(!res.isOk());
MOZ_RELEASE_ASSERT(res.isErr());
constexpr Result<Ok, Failed> res2 = Task1(false);
MOZ_STATIC_AND_RELEASE_ASSERT(!res2.isOk());
MOZ_STATIC_AND_RELEASE_ASSERT(res2.isErr());
}
{
@ -210,7 +221,7 @@ static void BasicTests() {
}
struct NonCopyableNonMovable {
explicit NonCopyableNonMovable(uint32_t aValue) : mValue(aValue) {}
explicit constexpr NonCopyableNonMovable(uint32_t aValue) : mValue(aValue) {}
NonCopyableNonMovable(const NonCopyableNonMovable&) = delete;
NonCopyableNonMovable(NonCopyableNonMovable&&) = delete;
@ -226,8 +237,8 @@ static void InPlaceConstructionTests() {
static_assert(mozilla::detail::SelectResultImpl<NonCopyableNonMovable,
Failed>::value ==
mozilla::detail::PackingStrategy::NullIsOk);
const Result<NonCopyableNonMovable, Failed> result{std::in_place, 42};
MOZ_RELEASE_ASSERT(42 == result.inspect().mValue);
constexpr Result<NonCopyableNonMovable, Failed> result{std::in_place, 42u};
MOZ_STATIC_AND_RELEASE_ASSERT(42 == result.inspect().mValue);
}
{
@ -312,6 +323,17 @@ static void MapTest() {
MOZ_RELEASE_ASSERT(strcmp(res2.unwrap(), "hello") == 0);
}
// Mapping over success values (constexpr).
{
constexpr uint64_t kValue = 42u;
constexpr auto res2a = Result<int32_t, Failed>{5}.map([](int32_t x) {
MOZ_RELEASE_ASSERT(x == 5);
return kValue;
});
MOZ_STATIC_AND_RELEASE_ASSERT(res2a.isOk());
MOZ_STATIC_AND_RELEASE_ASSERT(kValue == res2a.inspect());
}
// Mapping over error values.
{
MyError err(1);
@ -407,13 +429,13 @@ static void OrElseTest() {
struct MyError {
int x;
explicit MyError(int y) : x(y) {}
explicit constexpr MyError(int y) : x(y) {}
};
struct MyError2 {
int a;
explicit MyError2(int b) : a(b) {}
explicit constexpr MyError2(int b) : a(b) {}
};
// `orElse`ing over error values, to Result<V, E> (the same error type) error
@ -528,6 +550,14 @@ static void AndThenTest() {
MOZ_RELEASE_ASSERT(r2.unwrap() == 11);
}
// `andThen`ing over success results (constexpr).
{
constexpr Result<int, Failed> r2a = Result<int, Failed>{10}.andThen(
[](int x) { return Result<int, Failed>(x + 1); });
MOZ_STATIC_AND_RELEASE_ASSERT(r2a.isOk());
MOZ_STATIC_AND_RELEASE_ASSERT(r2a.inspect() == 11);
}
// `andThen`ing over error results.
{
Result<int, const char*> r3("error");
@ -547,6 +577,16 @@ static void AndThenTest() {
MOZ_RELEASE_ASSERT(r2.isOk());
MOZ_RELEASE_ASSERT(r2.unwrap() == 11);
}
// `andThen`ing over error results (constexpr).
{
constexpr Result<int, Failed> r4a =
Result<int, Failed>{Failed{}}.andThen([](int x) {
MOZ_RELEASE_ASSERT(false);
return Result<int, Failed>(1);
});
MOZ_STATIC_AND_RELEASE_ASSERT(r4a.isErr());
}
}
using UniqueResult = Result<UniquePtr<int>, const char*>;

Просмотреть файл

@ -188,9 +188,8 @@ class nsAutoRef : public nsAutoRefBase<T> {
// operator->() and disown() are provided by nsAutoRefBase<T>.
// The default nsSimpleRef<T> provides get().
private:
// No copy constructor
explicit nsAutoRef(ThisClass& aRefToSteal);
explicit nsAutoRef(const ThisClass& aRefToSteal) = delete;
};
/**
@ -216,8 +215,8 @@ class nsReturnRef : public nsAutoRefBase<T> {
MOZ_IMPLICIT nsReturnRef(RawRefOnly aRefToRelease)
: BaseClass(aRefToRelease) {}
// Copy construction transfers ownership
nsReturnRef(nsReturnRef<T>& aRefToSteal) : BaseClass(aRefToSteal) {}
// Move construction transfers ownership
nsReturnRef(nsReturnRef<T>&& aRefToSteal) = default;
MOZ_IMPLICIT nsReturnRef(const nsReturningRef<T>& aReturning)
: BaseClass(aReturning) {}
@ -428,8 +427,7 @@ class nsAutoRefBase : public nsSimpleRef<T> {
explicit nsAutoRefBase(RawRefOnly aRefToRelease) : SimpleRef(aRefToRelease) {}
// Constructors that steal ownership
explicit nsAutoRefBase(ThisClass& aRefToSteal)
: SimpleRef(aRefToSteal.disown()) {}
nsAutoRefBase(ThisClass&& aRefToSteal) : SimpleRef(aRefToSteal.disown()) {}
explicit nsAutoRefBase(const nsReturningRef<T>& aReturning)
: SimpleRef(aReturning.mReturnRef.disown()) {}
@ -444,10 +442,9 @@ class nsAutoRefBase : public nsSimpleRef<T> {
explicit LocalSimpleRef(RawRef aRawRef) : SimpleRef(aRawRef) {}
};
private:
public:
ThisClass& operator=(const ThisClass& aSmartRef) = delete;
public:
RawRef operator->() const { return this->get(); }
// Transfer ownership to a raw reference.

Просмотреть файл

@ -241,6 +241,9 @@ class MOZ_NEEDS_NO_VTABLE_TYPE nsTHashtable {
nsTHashtable(nsTHashtable<EntryType>&& aOther);
nsTHashtable<EntryType>& operator=(nsTHashtable<EntryType>&& aOther);
nsTHashtable(const nsTHashtable<EntryType>&) = delete;
nsTHashtable& operator=(const nsTHashtable<EntryType>&) = delete;
/**
* Return the generation number for the table. This increments whenever
* the table data items are moved.