diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h index 430da0f42348b..372907be8a11d 100644 --- a/llvm/include/llvm/ADT/STLExtras.h +++ b/llvm/include/llvm/ADT/STLExtras.h @@ -1299,6 +1299,58 @@ using is_one_of = disjunction...>; template using are_base_of = conjunction...>; +namespace detail { +template struct Visitor; + +template +struct Visitor : remove_cvref_t, Visitor { + explicit constexpr Visitor(HeadT &&Head, TailTs &&...Tail) + : remove_cvref_t(std::forward(Head)), + Visitor(std::forward(Tail)...) {} + using remove_cvref_t::operator(); + using Visitor::operator(); +}; + +template struct Visitor : remove_cvref_t { + explicit constexpr Visitor(HeadT &&Head) + : remove_cvref_t(std::forward(Head)) {} + using remove_cvref_t::operator(); +}; +} // namespace detail + +/// Returns an opaquely-typed Callable object whose operator() overload set is +/// the sum of the operator() overload sets of each CallableT in CallableTs. +/// +/// The type of the returned object derives from each CallableT in CallableTs. +/// The returned object is constructed by invoking the appropriate copy or move +/// constructor of each CallableT, as selected by overload resolution on the +/// corresponding argument to makeVisitor. +/// +/// Example: +/// +/// \code +/// auto visitor = makeVisitor([](auto) { return "unhandled type"; }, +/// [](int i) { return "int"; }, +/// [](std::string s) { return "str"; }); +/// auto a = visitor(42); // `a` is now "int". +/// auto b = visitor("foo"); // `b` is now "str". +/// auto c = visitor(3.14f); // `c` is now "unhandled type". +/// \endcode +/// +/// Example of making a visitor with a lambda which captures a move-only type: +/// +/// \code +/// std::unique_ptr FH = /* ... */; +/// auto visitor = makeVisitor( +/// [FH{std::move(FH)}](Foo F) { return FH->handle(F); }, +/// [](int i) { return i; }, +/// [](std::string s) { return atoi(s); }); +/// \endcode +template +constexpr decltype(auto) makeVisitor(CallableTs &&...Callables) { + return detail::Visitor(std::forward(Callables)...); +} + //===----------------------------------------------------------------------===// // Extra additions for arrays //===----------------------------------------------------------------------===// diff --git a/llvm/unittests/ADT/STLExtrasTest.cpp b/llvm/unittests/ADT/STLExtrasTest.cpp index 512c594d86322..eb87670700f7d 100644 --- a/llvm/unittests/ADT/STLExtrasTest.cpp +++ b/llvm/unittests/ADT/STLExtrasTest.cpp @@ -764,4 +764,151 @@ TEST(STLExtras, Unique) { EXPECT_EQ(3, V[3]); } +TEST(STLExtrasTest, TypesAreDistinct) { + EXPECT_TRUE((llvm::TypesAreDistinct<>::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_FALSE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_FALSE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_FALSE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); +} + +TEST(STLExtrasTest, FirstIndexOfType) { + EXPECT_EQ((llvm::FirstIndexOfType::value), 0u); + EXPECT_EQ((llvm::FirstIndexOfType::value), 0u); + EXPECT_EQ((llvm::FirstIndexOfType::value), 1u); + EXPECT_EQ((llvm::FirstIndexOfType::value), + 2u); +} + +TEST(STLExtrasTest, TypeAtIndex) { + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); +} + +TEST(STLExtrasTest, MakeVisitorOneCallable) { + auto IdentityLambda = [](auto X) { return X; }; + auto IdentityVisitor = makeVisitor(IdentityLambda); + EXPECT_EQ(IdentityLambda(1), IdentityVisitor(1)); + EXPECT_EQ(IdentityLambda(2.0f), IdentityVisitor(2.0f)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); +} + +TEST(STLExtrasTest, MakeVisitorTwoCallables) { + auto Visitor = + makeVisitor([](int) { return "int"; }, [](std::string) { return "str"; }); + EXPECT_EQ(Visitor(42), "int"); + EXPECT_EQ(Visitor("foo"), "str"); +} + +TEST(STLExtrasTest, MakeVisitorCallableMultipleOperands) { + auto Second = makeVisitor([](int I, float F) { return F; }, + [](float F, int I) { return I; }); + EXPECT_EQ(Second(1.f, 1), 1); + EXPECT_EQ(Second(1, 1.f), 1.f); +} + +TEST(STLExtrasTest, MakeVisitorDefaultCase) { + { + auto Visitor = makeVisitor([](int I) { return I + 100; }, + [](float F) { return F * 2; }, + [](auto) { return "unhandled type"; }); + EXPECT_EQ(Visitor(24), 124); + EXPECT_EQ(Visitor(2.f), 4.f); + EXPECT_EQ(Visitor(2.), "unhandled type"); + EXPECT_EQ(Visitor(Visitor), "unhandled type"); + } + { + auto Visitor = makeVisitor([](auto) { return "unhandled type"; }, + [](int I) { return I + 100; }, + [](float F) { return F * 2; }); + EXPECT_EQ(Visitor(24), 124); + EXPECT_EQ(Visitor(2.f), 4.f); + EXPECT_EQ(Visitor(2.), "unhandled type"); + EXPECT_EQ(Visitor(Visitor), "unhandled type"); + } +} + +template +struct Functor : Counted { + using Counted::Counted; + void operator()() {} +}; + +TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsPRValue) { + int Copies = 0; + int Moves = 0; + int Destructors = 0; + { + auto V = makeVisitor(Functor(Copies, Moves, Destructors)); + (void)V; + EXPECT_EQ(0, Copies); + EXPECT_EQ(1, Moves); + EXPECT_EQ(1, Destructors); + } + EXPECT_EQ(0, Copies); + EXPECT_EQ(1, Moves); + EXPECT_EQ(2, Destructors); +} + +TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsRValue) { + int Copies = 0; + int Moves = 0; + int Destructors = 0; + { + Functor F(Copies, Moves, Destructors); + { + auto V = makeVisitor(std::move(F)); + (void)V; + EXPECT_EQ(0, Copies); + EXPECT_EQ(1, Moves); + EXPECT_EQ(0, Destructors); + } + EXPECT_EQ(0, Copies); + EXPECT_EQ(1, Moves); + EXPECT_EQ(1, Destructors); + } + EXPECT_EQ(0, Copies); + EXPECT_EQ(1, Moves); + EXPECT_EQ(2, Destructors); +} + +TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsLValue) { + int Copies = 0; + int Moves = 0; + int Destructors = 0; + { + Functor F(Copies, Moves, Destructors); + { + auto V = makeVisitor(F); + (void)V; + EXPECT_EQ(1, Copies); + EXPECT_EQ(0, Moves); + EXPECT_EQ(0, Destructors); + } + EXPECT_EQ(1, Copies); + EXPECT_EQ(0, Moves); + EXPECT_EQ(1, Destructors); + } + EXPECT_EQ(1, Copies); + EXPECT_EQ(0, Moves); + EXPECT_EQ(2, Destructors); +} + } // namespace