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*>