diff --git a/include/v8-maybe.h b/include/v8-maybe.h index 0532a51005..8d3aeabe02 100644 --- a/include/v8-maybe.h +++ b/include/v8-maybe.h @@ -5,6 +5,9 @@ #ifndef INCLUDE_V8_MAYBE_H_ #define INCLUDE_V8_MAYBE_H_ +#include +#include + #include "v8-internal.h" // NOLINT(build/include_directory) #include "v8config.h" // NOLINT(build/include_directory) @@ -57,11 +60,20 @@ class Maybe { * Converts this Maybe<> to a value of type T. If this Maybe<> is * nothing (empty), V8 will crash the process. */ - V8_INLINE T FromJust() const { + V8_INLINE T FromJust() const& { if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing(); return value_; } + /** + * Converts this Maybe<> to a value of type T. If this Maybe<> is + * nothing (empty), V8 will crash the process. + */ + V8_INLINE T FromJust() && { + if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing(); + return std::move(value_); + } + /** * Converts this Maybe<> to a value of type T, using a default value if this * Maybe<> is nothing (empty). @@ -82,6 +94,7 @@ class Maybe { private: Maybe() : has_value_(false) {} explicit Maybe(const T& t) : has_value_(true), value_(t) {} + explicit Maybe(T&& t) : has_value_(true), value_(std::move(t)) {} bool has_value_; T value_; @@ -90,6 +103,8 @@ class Maybe { friend Maybe Nothing(); template friend Maybe Just(const U& u); + template >*> + friend Maybe Just(U&& u); }; template @@ -102,6 +117,14 @@ inline Maybe Just(const T& t) { return Maybe(t); } +// Don't use forwarding references here but instead use two overloads. +// Forwarding references only work when type deduction takes place, which is not +// the case for callsites such as Just(t). +template >* = nullptr> +inline Maybe Just(T&& t) { + return Maybe(std::move(t)); +} + // A template specialization of Maybe for the case of T = void. template <> class Maybe { diff --git a/test/unittests/BUILD.gn b/test/unittests/BUILD.gn index 017a133da7..ea0a8a71ea 100644 --- a/test/unittests/BUILD.gn +++ b/test/unittests/BUILD.gn @@ -216,6 +216,7 @@ v8_source_set("unittests_sources") { "api/isolate-unittest.cc", "api/remote-object-unittest.cc", "api/resource-constraints-unittest.cc", + "api/v8-maybe-unittest.cc", "api/v8-object-unittest.cc", "base/address-region-unittest.cc", "base/atomic-utils-unittest.cc", diff --git a/test/unittests/api/v8-maybe-unittest.cc b/test/unittests/api/v8-maybe-unittest.cc new file mode 100644 index 0000000000..315c049608 --- /dev/null +++ b/test/unittests/api/v8-maybe-unittest.cc @@ -0,0 +1,42 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "include/v8-maybe.h" + +#include "src/base/compiler-specific.h" +#include "src/base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace v8 { +namespace internal { + +namespace { +struct Movable { + Movable() = default; + + Movable(const Movable&) = delete; + Movable& operator=(const Movable&) = delete; + + Movable(Movable&&) V8_NOEXCEPT = default; + Movable& operator=(Movable&&) V8_NOEXCEPT = default; +}; +} // namespace + +TEST(MaybeTest, AllowMovableTypes) { + Maybe m1 = Just(Movable{}); + EXPECT_TRUE(m1.IsJust()); + + Maybe m2 = Just({}); + EXPECT_TRUE(m2.IsJust()); + + Maybe m3 = Nothing(); + EXPECT_TRUE(m3.IsNothing()); + + Maybe m4 = Just(Movable{}); + Movable mm = std::move(m4).FromJust(); + USE(mm); +} + +} // namespace internal +} // namespace v8