Add an implementation of Result<const T*, E*>

The existing implementation of Result with tagged pointers was not able
to handle constant pointers for the result. This is required in
follow-up CLs to return internal formats in a ResultOrError.

This CL extracts the tagged pointer logic out of Result<T*, E*> so it
can be shared with Result<const T*, E*>.

Tests are also added to cover Result<const T*, E*>.

BUG=dawn:128

Change-Id: Id19ae8e1153bcfcaf94d95ac314faf2b23af6f91
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9100
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/common/BUILD.gn b/src/common/BUILD.gn
index 71ecbe7..0d05c27 100644
--- a/src/common/BUILD.gn
+++ b/src/common/BUILD.gn
@@ -86,6 +86,7 @@
       "Math.cpp",
       "Math.h",
       "Platform.h",
+      "Result.cpp",
       "Result.h",
       "Serial.h",
       "SerialMap.h",
diff --git a/src/common/Result.cpp b/src/common/Result.cpp
new file mode 100644
index 0000000..a4132cd
--- /dev/null
+++ b/src/common/Result.cpp
@@ -0,0 +1,30 @@
+// Copyright 2019 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "common/Result.h"
+
+// Implementation details of the tagged pointer Results
+namespace detail {
+
+    intptr_t MakePayload(const void* pointer, PayloadType type) {
+        intptr_t payload = reinterpret_cast<intptr_t>(pointer);
+        ASSERT((payload & 3) == 0);
+        return payload | type;
+    }
+
+    PayloadType GetPayloadType(intptr_t payload) {
+        return static_cast<PayloadType>(payload & 3);
+    }
+
+}  // namespace detail
diff --git a/src/common/Result.h b/src/common/Result.h
index b4824fe..cd63a7a 100644
--- a/src/common/Result.h
+++ b/src/common/Result.h
@@ -85,6 +85,27 @@
 
 // Specialization of Result when both the error an success are pointers. It is implemented as a
 // tagged pointer. The tag for Success is 0 so that returning the value is fastest.
+
+namespace detail {
+    // Utility functions to manipulate the tagged pointer. Some of them don't need to be templated
+    // but we really want them inlined so we keep them in the headers
+    enum PayloadType {
+        Success = 0,
+        Error = 1,
+        Empty = 2,
+    };
+
+    intptr_t MakePayload(const void* pointer, PayloadType type);
+    PayloadType GetPayloadType(intptr_t payload);
+
+    template <typename T>
+    static T* GetSuccessFromPayload(intptr_t payload);
+    template <typename E>
+    static E* GetErrorFromPayload(intptr_t payload);
+
+    constexpr static intptr_t kEmptyPayload = Empty;
+}  // namespace detail
+
 template <typename T, typename E>
 class DAWN_NO_DISCARD Result<T*, E*> {
   public:
@@ -108,21 +129,33 @@
     E* AcquireError();
 
   private:
-    enum PayloadType {
-        Success = 0,
-        Error = 1,
-        Empty = 2,
-    };
+    intptr_t mPayload = detail::kEmptyPayload;
+};
 
-    // Utility functions to manipulate the tagged pointer. Some of them don't need to be templated
-    // but we really want them inlined so we keep them in the headers
-    static intptr_t MakePayload(void* pointer, PayloadType type);
-    static PayloadType GetPayloadType(intptr_t payload);
-    static T* GetSuccessFromPayload(intptr_t payload);
-    static E* GetErrorFromPayload(intptr_t payload);
+template <typename T, typename E>
+class DAWN_NO_DISCARD Result<const T*, E*> {
+  public:
+    static_assert(alignof_if_defined_else_default<T, 4> >= 4,
+                  "Result<T*, E*> reserves two bits for tagging pointers");
+    static_assert(alignof_if_defined_else_default<E, 4> >= 4,
+                  "Result<T*, E*> reserves two bits for tagging pointers");
 
-    constexpr static intptr_t kEmptyPayload = Empty;
-    intptr_t mPayload = kEmptyPayload;
+    Result(const T* success);
+    Result(E* error);
+
+    Result(Result<const T*, E*>&& other);
+    Result<const T*, E*>& operator=(Result<const T*, E>&& other);
+
+    ~Result();
+
+    bool IsError() const;
+    bool IsSuccess() const;
+
+    const T* AcquireSuccess();
+    E* AcquireError();
+
+  private:
+    intptr_t mPayload = detail::kEmptyPayload;
 };
 
 // Catchall definition of Result<T, E> implemented as a tagged struct. It could be improved to use
@@ -205,79 +238,124 @@
     return error;
 }
 
+// Implementation details of the tagged pointer Results
+namespace detail {
+
+    template <typename T>
+    T* GetSuccessFromPayload(intptr_t payload) {
+        ASSERT(GetPayloadType(payload) == Success);
+        return reinterpret_cast<T*>(payload);
+    }
+
+    template <typename E>
+    E* GetErrorFromPayload(intptr_t payload) {
+        ASSERT(GetPayloadType(payload) == Error);
+        return reinterpret_cast<E*>(payload ^ 1);
+    }
+
+}  // namespace detail
+
 // Implementation of Result<T*, E*>
 template <typename T, typename E>
-Result<T*, E*>::Result(T* success) : mPayload(MakePayload(success, Success)) {
+Result<T*, E*>::Result(T* success) : mPayload(detail::MakePayload(success, detail::Success)) {
 }
 
 template <typename T, typename E>
-Result<T*, E*>::Result(E* error) : mPayload(MakePayload(error, Error)) {
+Result<T*, E*>::Result(E* error) : mPayload(detail::MakePayload(error, detail::Error)) {
 }
 
 template <typename T, typename E>
 Result<T*, E*>::Result(Result<T*, E*>&& other) : mPayload(other.mPayload) {
-    other.mPayload = kEmptyPayload;
+    other.mPayload = detail::kEmptyPayload;
 }
 
 template <typename T, typename E>
 Result<T*, E*>& Result<T*, E*>::operator=(Result<T*, E>&& other) {
-    ASSERT(mPayload == kEmptyPayload);
+    ASSERT(mPayload == detail::kEmptyPayload);
     mPayload = other.mPayload;
-    other.mPayload = kEmptyPayload;
+    other.mPayload = detail::kEmptyPayload;
     return *this;
 }
 
 template <typename T, typename E>
 Result<T*, E*>::~Result() {
-    ASSERT(mPayload == kEmptyPayload);
+    ASSERT(mPayload == detail::kEmptyPayload);
 }
 
 template <typename T, typename E>
 bool Result<T*, E*>::IsError() const {
-    return GetPayloadType(mPayload) == Error;
+    return detail::GetPayloadType(mPayload) == detail::Error;
 }
 
 template <typename T, typename E>
 bool Result<T*, E*>::IsSuccess() const {
-    return GetPayloadType(mPayload) == Success;
+    return detail::GetPayloadType(mPayload) == detail::Success;
 }
 
 template <typename T, typename E>
 T* Result<T*, E*>::AcquireSuccess() {
-    T* success = GetSuccessFromPayload(mPayload);
-    mPayload = kEmptyPayload;
+    T* success = detail::GetSuccessFromPayload<T>(mPayload);
+    mPayload = detail::kEmptyPayload;
     return success;
 }
 
 template <typename T, typename E>
 E* Result<T*, E*>::AcquireError() {
-    E* error = GetErrorFromPayload(mPayload);
-    mPayload = kEmptyPayload;
+    E* error = detail::GetErrorFromPayload<E>(mPayload);
+    mPayload = detail::kEmptyPayload;
     return error;
 }
 
+// Implementation of Result<const T*, E*>
 template <typename T, typename E>
-intptr_t Result<T*, E*>::MakePayload(void* pointer, PayloadType type) {
-    intptr_t payload = reinterpret_cast<intptr_t>(pointer);
-    ASSERT((payload & 3) == 0);
-    return payload | type;
+Result<const T*, E*>::Result(const T* success)
+    : mPayload(detail::MakePayload(success, detail::Success)) {
 }
 
 template <typename T, typename E>
-typename Result<T*, E*>::PayloadType Result<T*, E*>::GetPayloadType(intptr_t payload) {
-    return static_cast<PayloadType>(payload & 3);
+Result<const T*, E*>::Result(E* error) : mPayload(detail::MakePayload(error, detail::Error)) {
 }
 
 template <typename T, typename E>
-T* Result<T*, E*>::GetSuccessFromPayload(intptr_t payload) {
-    ASSERT(GetPayloadType(payload) == Success);
-    return reinterpret_cast<T*>(payload);
+Result<const T*, E*>::Result(Result<const T*, E*>&& other) : mPayload(other.mPayload) {
+    other.mPayload = detail::kEmptyPayload;
 }
 
 template <typename T, typename E>
-E* Result<T*, E*>::GetErrorFromPayload(intptr_t payload) {
-    ASSERT(GetPayloadType(payload) == Error);
-    return reinterpret_cast<E*>(payload ^ 1);
+Result<const T*, E*>& Result<const T*, E*>::operator=(Result<const T*, E>&& other) {
+    ASSERT(mPayload == detail::kEmptyPayload);
+    mPayload = other.mPayload;
+    other.mPayload = detail::kEmptyPayload;
+    return *this;
+}
+
+template <typename T, typename E>
+Result<const T*, E*>::~Result() {
+    ASSERT(mPayload == detail::kEmptyPayload);
+}
+
+template <typename T, typename E>
+bool Result<const T*, E*>::IsError() const {
+    return detail::GetPayloadType(mPayload) == detail::Error;
+}
+
+template <typename T, typename E>
+bool Result<const T*, E*>::IsSuccess() const {
+    return detail::GetPayloadType(mPayload) == detail::Success;
+}
+
+template <typename T, typename E>
+const T* Result<const T*, E*>::AcquireSuccess() {
+    T* success = detail::GetSuccessFromPayload<T>(mPayload);
+    mPayload = detail::kEmptyPayload;
+    return success;
+}
+
+template <typename T, typename E>
+E* Result<const T*, E*>::AcquireError() {
+    E* error = detail::GetErrorFromPayload<E>(mPayload);
+    mPayload = detail::kEmptyPayload;
+    return error;
 }
 
 // Implementation of Result<T, E>
diff --git a/src/tests/unittests/ResultTests.cpp b/src/tests/unittests/ResultTests.cpp
index 4616141..e9e5e36 100644
--- a/src/tests/unittests/ResultTests.cpp
+++ b/src/tests/unittests/ResultTests.cpp
@@ -38,6 +38,7 @@
 
 static int dummyError = 0xbeef;
 static float dummySuccess = 42.0f;
+static const float dummyConstSuccess = 42.0f;
 
 // Result<void, E*>
 
@@ -138,6 +139,50 @@
     TestSuccess(&result, &dummySuccess);
 }
 
+// Result<const T*, E*>
+
+// Test constructing an error Result<const T*, E*>
+TEST(ResultBothPointerWithConstResult, ConstructingError) {
+    Result<const float*, int*> result(&dummyError);
+    TestError(&result, &dummyError);
+}
+
+// Test moving an error Result<const T*, E*>
+TEST(ResultBothPointerWithConstResult, MovingError) {
+    Result<const float*, int*> result(&dummyError);
+    Result<const float*, int*> movedResult(std::move(result));
+    TestError(&movedResult, &dummyError);
+}
+
+// Test returning an error Result<const T*, E*>
+TEST(ResultBothPointerWithConstResult, ReturningError) {
+    auto CreateError = []() -> Result<const float*, int*> { return {&dummyError}; };
+
+    Result<const float*, int*> result = CreateError();
+    TestError(&result, &dummyError);
+}
+
+// Test constructing a success Result<const T*, E*>
+TEST(ResultBothPointerWithConstResult, ConstructingSuccess) {
+    Result<const float*, int*> result(&dummyConstSuccess);
+    TestSuccess(&result, &dummyConstSuccess);
+}
+
+// Test moving a success Result<const T*, E*>
+TEST(ResultBothPointerWithConstResult, MovingSuccess) {
+    Result<const float*, int*> result(&dummyConstSuccess);
+    Result<const float*, int*> movedResult(std::move(result));
+    TestSuccess(&movedResult, &dummyConstSuccess);
+}
+
+// Test returning a success Result<const T*, E*>
+TEST(ResultBothPointerWithConstResult, ReturningSuccess) {
+    auto CreateSuccess = []() -> Result<const float*, int*> { return {&dummyConstSuccess}; };
+
+    Result<const float*, int*> result = CreateSuccess();
+    TestSuccess(&result, &dummyConstSuccess);
+}
+
 // Result<T, E>
 
 // Test constructing an error Result<T, E>