diff --git a/llvm/docs/ProgrammersManual.rst b/llvm/docs/ProgrammersManual.rst --- a/llvm/docs/ProgrammersManual.rst +++ b/llvm/docs/ProgrammersManual.rst @@ -568,6 +568,46 @@ This second form is often more readable for functions that involve multiple ``Expected`` values as it limits the indentation required. +If an ``Expected`` value will be moved into an existing variable then the +``moveInto()`` method avoids the need to name an extra variable. This is +useful to enable ``operator->()`` the ``Expected`` value has pointer-like +semantics. For example: + +.. code-block:: c++ + + Expected> openBuffer(StringRef Path); + Error processBuffer(StringRef Buffer); + + Error processBufferAtPath(StringRef Path) { + // Try to open a buffer. + std::unique_ptr MB; + if (auto Err = openBuffer(Path).moveInto(MB)) + // On error, return the Error value. + return Err; + // On success, use MB. + return processContent(MB->getBuffer()); + } + +This third form works with any type that can be assigned to from ``T&&``. This +can be useful if the ``Expected`` value needs to be stored an already-declared +``Optional``. For example: + +.. code-block:: c++ + + Expected extractClassName(StringRef Definition); + struct ClassData { + StringRef Definition; + Optional LazyName; + ... + Error initialize() { + if (auto Err = extractClassName(Path).moveInto(LazyName)) + // On error, return the Error value. + return Err; + // On success, LazyName has been initialized. + ... + } + }; + All ``Error`` instances, whether success or failure, must be either checked or moved from (via ``std::move`` or a return) before they are destructed. Accidentally discarding an unchecked error will cause a program abort at the diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h --- a/llvm/include/llvm/Support/Error.h +++ b/llvm/include/llvm/Support/Error.h @@ -577,6 +577,16 @@ return const_cast *>(this)->get(); } + /// Returns \a takeError() after moving the held T (if any) into \p V. + template + Error moveInto(OtherT &Value, + std::enable_if_t::value> * = + nullptr) && { + if (*this) + Value = std::move(get()); + return takeError(); + } + /// Check that this Expected is an error of type ErrT. template bool errorIsA() const { return HasError && (*getErrorStorage())->template isA(); diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp --- a/llvm/unittests/Support/ErrorTest.cpp +++ b/llvm/unittests/Support/ErrorTest.cpp @@ -1032,4 +1032,71 @@ "Error 2."); } +static Error createAnyError() { + return errorCodeToError(test_error_code::unspecified); +} + +struct MoveOnlyBox { + Optional Box; + + explicit MoveOnlyBox(int I) : Box(I) {} + MoveOnlyBox() = default; + MoveOnlyBox(MoveOnlyBox &&) = default; + MoveOnlyBox &operator=(MoveOnlyBox &&) = default; + + MoveOnlyBox(const MoveOnlyBox &) = delete; + MoveOnlyBox &operator=(const MoveOnlyBox &) = delete; + + bool operator==(const MoveOnlyBox &RHS) const { + if (bool(Box) != bool(RHS.Box)) + return false; + return Box ? *Box == *RHS.Box : false; + } +}; + +TEST(Error, moveInto) { + // Use MoveOnlyBox as the T in Expected. + auto make = [](int I) -> Expected { return MoveOnlyBox(I); }; + auto makeFailure = []() -> Expected { return createAnyError(); }; + + { + MoveOnlyBox V; + + // Failure with no prior value. + EXPECT_THAT_ERROR(makeFailure().moveInto(V), Failed()); + EXPECT_EQ(None, V.Box); + + // Success with no prior value. + EXPECT_THAT_ERROR(make(5).moveInto(V), Succeeded()); + EXPECT_EQ(5, V.Box); + + // Success with an existing value. + EXPECT_THAT_ERROR(make(7).moveInto(V), Succeeded()); + EXPECT_EQ(7, V.Box); + + // Failure with an existing value. Might be nice to assign a + // default-constructed value in this case, but for now it's being left + // alone. + EXPECT_THAT_ERROR(makeFailure().moveInto(V), Failed()); + EXPECT_EQ(7, V.Box); + } + + // Check that this works with optionals too. + { + // Same cases as above. + Optional MaybeV; + EXPECT_THAT_ERROR(makeFailure().moveInto(MaybeV), Failed()); + EXPECT_EQ(None, MaybeV); + + EXPECT_THAT_ERROR(make(5).moveInto(MaybeV), Succeeded()); + EXPECT_EQ(MoveOnlyBox(5), MaybeV); + + EXPECT_THAT_ERROR(make(7).moveInto(MaybeV), Succeeded()); + EXPECT_EQ(MoveOnlyBox(7), MaybeV); + + EXPECT_THAT_ERROR(makeFailure().moveInto(MaybeV), Failed()); + EXPECT_EQ(MoveOnlyBox(7), MaybeV); + } +} + } // namespace