Use variant as payload for Result<T, E>

Use std::variant<std::monostate, T, std::unique_ptr<E>> as the payload
for Result allows T to be non-default-constructible and also moves some
bookkeeping from Result into std::variant.

Bug: dawn:2344
Change-Id: I3f7d1aa4b7776de0174052c4379e5abb1ef107c3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/169361
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/common/Result.h b/src/dawn/common/Result.h
index 7bcccfc..8000a0f 100644
--- a/src/dawn/common/Result.h
+++ b/src/dawn/common/Result.h
@@ -33,6 +33,7 @@
 #include <memory>
 #include <type_traits>
 #include <utility>
+#include <variant>
 
 #include "dawn/common/Assert.h"
 #include "dawn/common/Compiler.h"
@@ -224,7 +225,7 @@
 template <typename T, typename E>
 class [[nodiscard]] Result {
   public:
-    Result(T&& success);
+    Result(T success);
     Result(std::unique_ptr<E> error);
 
     Result(Result<T, E>&& other);
@@ -235,19 +236,11 @@
     bool IsError() const;
     bool IsSuccess() const;
 
-    T&& AcquireSuccess();
+    T AcquireSuccess();
     std::unique_ptr<E> AcquireError();
 
   private:
-    enum PayloadType {
-        Success = 0,
-        Error = 1,
-        Acquired = 2,
-    };
-    PayloadType mType;
-
-    std::unique_ptr<E> mError;
-    T mSuccess;
+    std::variant<std::monostate, T, std::unique_ptr<E>> mPayload;
 };
 
 // Implementation of Result<void, E>
@@ -478,52 +471,52 @@
 
 // Implementation of Result<T, E>
 template <typename T, typename E>
-Result<T, E>::Result(T&& success) : mType(Success), mSuccess(std::move(success)) {}
+Result<T, E>::Result(T success) : mPayload(std::move(success)) {}
 
 template <typename T, typename E>
-Result<T, E>::Result(std::unique_ptr<E> error) : mType(Error), mError(std::move(error)) {}
+Result<T, E>::Result(std::unique_ptr<E> error) : mPayload(std::move(error)) {}
 
 template <typename T, typename E>
 Result<T, E>::~Result() {
-    DAWN_ASSERT(mType == Acquired);
+    DAWN_ASSERT(std::holds_alternative<std::monostate>(mPayload));
 }
 
 template <typename T, typename E>
-Result<T, E>::Result(Result<T, E>&& other)
-    : mType(other.mType), mError(std::move(other.mError)), mSuccess(std::move(other.mSuccess)) {
-    other.mType = Acquired;
+Result<T, E>::Result(Result<T, E>&& other) {
+    *this = std::move(other);
 }
+
 template <typename T, typename E>
 Result<T, E>& Result<T, E>::operator=(Result<T, E>&& other) {
-    mType = other.mType;
-    mError = std::move(other.mError);
-    mSuccess = std::move(other.mSuccess);
-    other.mType = Acquired;
+    DAWN_ASSERT(std::holds_alternative<std::monostate>(mPayload));
+    std::swap(mPayload, other.mPayload);
     return *this;
 }
 
 template <typename T, typename E>
 bool Result<T, E>::IsError() const {
-    return mType == Error;
+    return std::holds_alternative<std::unique_ptr<E>>(mPayload);
 }
 
 template <typename T, typename E>
 bool Result<T, E>::IsSuccess() const {
-    return mType == Success;
+    return std::holds_alternative<T>(mPayload);
 }
 
 template <typename T, typename E>
-T&& Result<T, E>::AcquireSuccess() {
-    DAWN_ASSERT(mType == Success);
-    mType = Acquired;
-    return std::move(mSuccess);
+T Result<T, E>::AcquireSuccess() {
+    DAWN_ASSERT(IsSuccess());
+    auto payload = std::move(mPayload);
+    mPayload = {};
+    return std::move(std::get<T>(payload));
 }
 
 template <typename T, typename E>
 std::unique_ptr<E> Result<T, E>::AcquireError() {
-    DAWN_ASSERT(mType == Error);
-    mType = Acquired;
-    return std::move(mError);
+    DAWN_ASSERT(IsError());
+    auto payload = std::move(mPayload);
+    mPayload = {};
+    return std::move(std::get<std::unique_ptr<E>>(payload));
 }
 
 }  // namespace dawn
diff --git a/src/dawn/tests/unittests/ResultTests.cpp b/src/dawn/tests/unittests/ResultTests.cpp
index 8b5064d..44274e1 100644
--- a/src/dawn/tests/unittests/ResultTests.cpp
+++ b/src/dawn/tests/unittests/ResultTests.cpp
@@ -398,5 +398,58 @@
     TestSuccess(&result, {1.0f});
 }
 
+class NonDefaultConstructible {
+  public:
+    explicit NonDefaultConstructible(float v) : v(v) {}
+    bool operator==(const NonDefaultConstructible& other) const { return v == other.v; }
+    float v;
+};
+
+// Test constructing an error Result<T, E> for non-default-constructible T
+TEST(ResultNonDefaultConstructible, ConstructingError) {
+    Result<NonDefaultConstructible, int> result(std::make_unique<int>(placeholderError));
+    TestError(&result, placeholderError);
+}
+
+// Test moving an error Result<T, E> for non-default-constructible T
+TEST(ResultNonDefaultConstructible, MovingError) {
+    Result<NonDefaultConstructible, int> result(std::make_unique<int>(placeholderError));
+    Result<NonDefaultConstructible, int> movedResult(std::move(result));
+    TestError(&movedResult, placeholderError);
+}
+
+// Test returning an error Result<T, E> for non-default-constructible T
+TEST(ResultNonDefaultConstructible, ReturningError) {
+    auto CreateError = []() -> Result<NonDefaultConstructible, int> {
+        return {std::make_unique<int>(placeholderError)};
+    };
+
+    Result<NonDefaultConstructible, int> result = CreateError();
+    TestError(&result, placeholderError);
+}
+
+// Test constructing a success Result<T, E> for non-default-constructible T
+TEST(ResultNonDefaultConstructible, ConstructingSuccess) {
+    Result<NonDefaultConstructible, int> result(NonDefaultConstructible(1.0f));
+    TestSuccess(&result, NonDefaultConstructible(1.0f));
+}
+
+// Test moving a success Result<T, E> for non-default-constructible T
+TEST(ResultNonDefaultConstructible, MovingSuccess) {
+    Result<NonDefaultConstructible, int> result(NonDefaultConstructible(1.0f));
+    Result<NonDefaultConstructible, int> movedResult(std::move(result));
+    TestSuccess(&movedResult, NonDefaultConstructible(1.0f));
+}
+
+// Test returning a success Result<T, E> for non-default-constructible T
+TEST(ResultNonDefaultConstructible, ReturningSuccess) {
+    auto CreateSuccess = []() -> Result<NonDefaultConstructible, int> {
+        return {NonDefaultConstructible(1.0f)};
+    };
+
+    Result<NonDefaultConstructible, int> result = CreateSuccess();
+    TestSuccess(&result, NonDefaultConstructible(1.0f));
+}
+
 }  // anonymous namespace
 }  // namespace dawn