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 @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/STLForwardCompat.h" +#include "MoveOnly.h" #include "gtest/gtest.h" namespace { @@ -44,4 +45,76 @@ llvm::remove_cvref_t>::value)); } +TEST(TransformTest, 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(TransformTest, MoveTransformStd) { + using llvm::MoveOnly; + + 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(TransformTest, 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(TransformTest, MoveTransformLlvm) { + using llvm::MoveOnly; + + 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