aboutsummaryrefslogtreecommitdiff
path: root/llvm/unittests/Support/ErrorTest.cpp
diff options
context:
space:
mode:
authorDuncan P. N. Exon Smith <dexonsmith@apple.com>2021-10-20 12:03:31 -0700
committerDuncan P. N. Exon Smith <dexonsmith@apple.com>2021-10-22 11:47:03 -0700
commit27181cad0d4e48eac822aaccf52b612f29eeff73 (patch)
treed6357111f3c1fa656617659f2f4a326d3653c492 /llvm/unittests/Support/ErrorTest.cpp
parent4d692daa3a6ab876171063eb22be21b94df829e7 (diff)
downloadllvm-27181cad0d4e48eac822aaccf52b612f29eeff73.zip
llvm-27181cad0d4e48eac822aaccf52b612f29eeff73.tar.gz
llvm-27181cad0d4e48eac822aaccf52b612f29eeff73.tar.bz2
Support: Add Expected<T>::moveInto() to avoid extra names
Expected<T>::moveInto() takes as an out parameter any `OtherT&` that's assignable from `T&&`. It moves any stored value before returning takeError(). Since moveInto() consumes both the Error and the value, it's only anticipated that we'd use call it on temporaries/rvalues, with naming the Expected first likely to be an anti-pattern of sorts (either you want to deal with both at the same time, or you don't). As such, starting it out as `&&`-qualified... but it'd probably be fine to drop that if there's a good use case for lvalues that appears. There are two common patterns that moveInto() cleans up: ``` // If the variable is new: Expected<std::unique_ptr<int>> ExpectedP = makePointer(); if (!ExpectedP) return ExpectedP.takeError(); std::unique_ptr<int> P = std::move(*ExpectedP); // If the target variable already exists: if (Expected<T> ExpectedP = makePointer()) P = std::move(*ExpectedP); else return ExpectedP.takeError(); ``` moveInto() takes less typing and avoids needing to name (or leak into the scope) an extra variable. ``` // If the variable is new: std::unique_ptr<int> P; if (Error E = makePointer().moveInto(P)) return E; // If the target variable already exists: if (Error E = makePointer().moveInto(P)) return E; ``` It also seems useful for unit tests, to log errors (but continue) when there's an unexpected failure. E.g.: ``` // Crash on error, or undefined in non-asserts builds. std::unique_ptr<MemoryBuffer> MB = cantFail(makeMemoryBuffer()); // Avoid crashing on error without moveInto() :(. Expected<std::unique_ptr<MemoryBuffer>> ExpectedMB = makeMemoryBuffer(); ASSERT_THAT_ERROR(ExpectedMB.takeError(), Succeeded()); std::unique_ptr<MemoryBuffer> MB = std::move(ExpectedMB); // Avoid crashing on error with moveInto() :). std::unique_ptr<MemoryBuffer> MB; ASSERT_THAT_ERROR(makeMemoryBuffer().moveInto(MB), Succeeded()); ``` Differential Revision: https://reviews.llvm.org/D112278
Diffstat (limited to 'llvm/unittests/Support/ErrorTest.cpp')
-rw-r--r--llvm/unittests/Support/ErrorTest.cpp67
1 files changed, 67 insertions, 0 deletions
diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp
index 205f523..4d557cc 100644
--- a/llvm/unittests/Support/ErrorTest.cpp
+++ b/llvm/unittests/Support/ErrorTest.cpp
@@ -1032,4 +1032,71 @@ TEST(Error, SubtypeStringErrorTest) {
"Error 2.");
}
+static Error createAnyError() {
+ return errorCodeToError(test_error_code::unspecified);
+}
+
+struct MoveOnlyBox {
+ Optional<int> 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<T>.
+ auto make = [](int I) -> Expected<MoveOnlyBox> { return MoveOnlyBox(I); };
+ auto makeFailure = []() -> Expected<MoveOnlyBox> { 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<MoveOnlyBox> 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