diff --git a/llvm/include/llvm/ADT/STLForwardCompat.h b/llvm/include/llvm/ADT/STLForwardCompat.h --- a/llvm/include/llvm/ADT/STLForwardCompat.h +++ b/llvm/include/llvm/ADT/STLForwardCompat.h @@ -17,6 +17,8 @@ #ifndef LLVM_ADT_STLFORWARDCOMPAT_H #define LLVM_ADT_STLFORWARDCOMPAT_H +#include "llvm/ADT/Optional.h" +#include #include namespace llvm { @@ -35,6 +37,50 @@ using remove_cvref_t // NOLINT(readability-identifier-naming) = typename llvm::remove_cvref::type; +//===----------------------------------------------------------------------===// +// Features from C++23 +//===----------------------------------------------------------------------===// + +// TODO: Remove this in favor of std::optional::transform once we switch to +// C++23. +template +auto transformOptional(const std::optional &O, const Function &F) + -> std::optional { + if (O) + return F(O.value()); + return std::nullopt; +} + +// TODO: Remove this in favor of std::optional::transform once we switch to +// C++23. +template +auto transformOptional(std::optional &&O, const Function &F) + -> std::optional { + if (O) + return F(std::move(O).value()); + return std::nullopt; +} + +// TODO: Remove this once the migration from llvm::Optional to std::optional is +// complete. +template +auto transformOptional(const Optional &O, const Function &F) + -> Optional { + if (O) + return F(O.value()); + return std::nullopt; +} + +// TODO: Remove this once the migration from llvm::Optional to std::optional is +// complete. +template +auto transformOptional(Optional &&O, const Function &F) + -> Optional { + if (O) + return F(std::move(O).value()); + return std::nullopt; +} + } // namespace llvm #endif // LLVM_ADT_STLFORWARDCOMPAT_H diff --git a/llvm/unittests/ADT/STLForwardCompatTest.cpp b/llvm/unittests/ADT/STLForwardCompatTest.cpp --- a/llvm/unittests/ADT/STLForwardCompatTest.cpp +++ b/llvm/unittests/ADT/STLForwardCompatTest.cpp @@ -44,4 +44,110 @@ llvm::remove_cvref_t>::value)); } +namespace { + +struct MoveOnly { + static unsigned MoveConstructions; + static unsigned Destructions; + static unsigned MoveAssignments; + int val; + explicit MoveOnly(int val) : val(val) { + } + MoveOnly(MoveOnly&& other) { + val = other.val; + ++MoveConstructions; + } + MoveOnly &operator=(MoveOnly&& other) { + val = other.val; + ++MoveAssignments; + return *this; + } + ~MoveOnly() { + ++Destructions; + } + static void ResetCounts() { + MoveConstructions = 0; + Destructions = 0; + MoveAssignments = 0; + } +}; + +unsigned MoveOnly::MoveConstructions = 0; +unsigned MoveOnly::Destructions = 0; +unsigned MoveOnly::MoveAssignments = 0; + +} // end anonymous namespace + +TEST(TransformOptionalTest, TransformStd) { + std::optional A; + + std::optional B = + llvm::transformOptional(A, [&](int N) { return N + 1; }); + EXPECT_FALSE(B.has_value()); + + A = 3; + std::optional C = + llvm::transformOptional(A, [&](int N) { return N + 1; }); + EXPECT_TRUE(C.has_value()); + EXPECT_EQ(4, C.value()); +} + +TEST(TransformOptionalTest, MoveTransformStd) { + std::optional A; + + MoveOnly::ResetCounts(); + std::optional B = llvm::transformOptional( + std::move(A), [&](const MoveOnly &M) { return M.val + 2; }); + EXPECT_FALSE(B.has_value()); + EXPECT_EQ(0u, MoveOnly::MoveConstructions); + EXPECT_EQ(0u, MoveOnly::MoveAssignments); + EXPECT_EQ(0u, MoveOnly::Destructions); + + A = MoveOnly(5); + MoveOnly::ResetCounts(); + std::optional C = llvm::transformOptional( + std::move(A), [&](const MoveOnly &M) { return M.val + 2; }); + EXPECT_TRUE(C.has_value()); + EXPECT_EQ(7, C.value()); + EXPECT_EQ(0u, MoveOnly::MoveConstructions); + EXPECT_EQ(0u, MoveOnly::MoveAssignments); + EXPECT_EQ(0u, MoveOnly::Destructions); +} + +TEST(TransformOptionalTest, TransformLlvm) { + llvm::Optional A; + + llvm::Optional B = + llvm::transformOptional(A, [&](int N) { return N + 1; }); + EXPECT_FALSE(B.has_value()); + + A = 3; + llvm::Optional C = + llvm::transformOptional(A, [&](int N) { return N + 1; }); + EXPECT_TRUE(C.has_value()); + EXPECT_EQ(4, C.value()); +} + +TEST(TransformOptionalTest, MoveTransformLlvm) { + llvm::Optional A; + + MoveOnly::ResetCounts(); + llvm::Optional B = llvm::transformOptional( + std::move(A), [&](const MoveOnly &M) { return M.val + 2; }); + EXPECT_FALSE(B.has_value()); + EXPECT_EQ(0u, MoveOnly::MoveConstructions); + EXPECT_EQ(0u, MoveOnly::MoveAssignments); + EXPECT_EQ(0u, MoveOnly::Destructions); + + A = MoveOnly(5); + MoveOnly::ResetCounts(); + llvm::Optional C = llvm::transformOptional( + std::move(A), [&](const MoveOnly &M) { return M.val + 2; }); + EXPECT_TRUE(C.has_value()); + EXPECT_EQ(7, C.value()); + EXPECT_EQ(0u, MoveOnly::MoveConstructions); + EXPECT_EQ(0u, MoveOnly::MoveAssignments); + EXPECT_EQ(0u, MoveOnly::Destructions); +} + } // namespace