Allow ResultOrError to downcast from backend to frontend types

More concretely this makes Result<T*, E*> able to be move-constructed
from Result<ChildClassOfT*, E*> for free.

BUG=dawn:19

Change-Id: Iea2b8997079ac3bfcf270d6b73a79cf5cac2c06f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/11860
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/common/Result.h b/src/common/Result.h
index cd63a7a..3e33052 100644
--- a/src/common/Result.h
+++ b/src/common/Result.h
@@ -20,6 +20,7 @@
 
 #include <cstddef>
 #include <cstdint>
+#include <type_traits>
 #include <utility>
 
 // Result<T, E> is the following sum type (Haskell notation):
@@ -117,8 +118,11 @@
     Result(T* success);
     Result(E* error);
 
-    Result(Result<T*, E*>&& other);
-    Result<T*, E*>& operator=(Result<T*, E>&& other);
+    // Support returning a Result<T*, E*> from a Result<TChild*, E*>
+    template <typename TChild>
+    Result(Result<TChild*, E*>&& other);
+    template <typename TChild>
+    Result<T*, E*>& operator=(Result<TChild*, E>&& other);
 
     ~Result();
 
@@ -129,6 +133,9 @@
     E* AcquireError();
 
   private:
+    template <typename T2, typename E2>
+    friend class Result;
+
     intptr_t mPayload = detail::kEmptyPayload;
 };
 
@@ -265,13 +272,17 @@
 }
 
 template <typename T, typename E>
-Result<T*, E*>::Result(Result<T*, E*>&& other) : mPayload(other.mPayload) {
+template <typename TChild>
+Result<T*, E*>::Result(Result<TChild*, E*>&& other) : mPayload(other.mPayload) {
     other.mPayload = detail::kEmptyPayload;
+    static_assert(std::is_same<T, TChild>::value || std::is_base_of<T, TChild>::value, "");
 }
 
 template <typename T, typename E>
-Result<T*, E*>& Result<T*, E*>::operator=(Result<T*, E>&& other) {
+template <typename TChild>
+Result<T*, E*>& Result<T*, E*>::operator=(Result<TChild*, E>&& other) {
     ASSERT(mPayload == detail::kEmptyPayload);
+    static_assert(std::is_same<T, TChild>::value || std::is_base_of<T, TChild>::value, "");
     mPayload = other.mPayload;
     other.mPayload = detail::kEmptyPayload;
     return *this;
diff --git a/src/tests/unittests/ResultTests.cpp b/src/tests/unittests/ResultTests.cpp
index e9e5e36..d991462 100644
--- a/src/tests/unittests/ResultTests.cpp
+++ b/src/tests/unittests/ResultTests.cpp
@@ -139,6 +139,31 @@
     TestSuccess(&result, &dummySuccess);
 }
 
+// Tests converting from a Result<TChild*, E*>
+TEST(ResultBothPointer, ConversionFromChildClass) {
+    struct T {
+        int a;
+    };
+    struct TChild : T {};
+
+    TChild child;
+    T* childAsT = &child;
+    {
+        Result<T*, int*> result(&child);
+        TestSuccess(&result, childAsT);
+    }
+    {
+        Result<TChild*, int*> resultChild(&child);
+        Result<T*, int*> result(std::move(resultChild));
+        TestSuccess(&result, childAsT);
+    }
+    {
+        Result<TChild*, int*> resultChild(&child);
+        Result<T*, int*> result = std::move(resultChild);
+        TestSuccess(&result, childAsT);
+    }
+}
+
 // Result<const T*, E*>
 
 // Test constructing an error Result<const T*, E*>