diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h --- a/llvm/include/llvm/ADT/STLExtras.h +++ b/llvm/include/llvm/ADT/STLExtras.h @@ -1353,6 +1353,58 @@ 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 --- a/llvm/unittests/ADT/STLExtrasTest.cpp +++ b/llvm/unittests/ADT/STLExtrasTest.cpp @@ -139,57 +139,94 @@ CanCopy(CanCopy &&) = default; }; -template -struct Range : CanMove, CanCopy { - explicit Range(int &C, int &M, int &D) : C(C), M(M), D(D) {} - Range(const Range &R) : CanCopy(R), C(R.C), M(R.M), D(R.D) { ++C; } - Range(Range &&R) : CanMove(std::move(R)), C(R.C), M(R.M), D(R.D) { - ++M; - } - ~Range() { ++D; } +template +class Counted : CanMove, CanCopy { + unsigned &Moves, &Copies, &Destructs; - int &C; - int &M; - int &D; +public: + explicit Counted(unsigned &Moves, unsigned &Copies, unsigned &Destructs) + : Moves(Moves), Copies(Copies), Destructs(Destructs) {} + Counted(Counted &&O) + : CanMove(std::move(O)), Moves(O.Moves), Copies(O.Copies), + Destructs(O.Destructs) { + ++Moves; + } + Counted(const Counted &O) + : CanCopy(O), Moves(O.Moves), Copies(O.Copies), + Destructs(O.Destructs) { + ++Copies; + } + virtual ~Counted() { ++Destructs; } +}; +template +struct Range : Counted { + using Counted::Counted; int *begin() { return nullptr; } int *end() { return nullptr; } }; -TEST(STLExtrasTest, EnumerateLifetimeSemantics) { - // Test that when enumerating lvalues and rvalues, there are no surprise - // copies or moves. - - // With an rvalue, it should not be destroyed until the end of the scope. - int Copies = 0; - int Moves = 0; - int Destructors = 0; +TEST(STLExtrasTest, EnumerateLifetimeSemanticsPRValue) { + unsigned Moves{}, Copies{}, Destructs{}; { - auto E1 = enumerate(Range(Copies, Moves, Destructors)); + auto E = enumerate(Range(Moves, Copies, Destructs)); + (void)E; // Doesn't compile. rvalue ranges must be moveable. // auto E2 = enumerate(Range(Copies, Moves, Destructors)); - EXPECT_EQ(0, Copies); - EXPECT_EQ(1, Moves); - EXPECT_EQ(1, Destructors); + // FIXME: Can a C++14 compiler elide this temporary entirely? As of C++20 + // this will be required (i.e. no temporary may exist in this case.) + EXPECT_EQ(1u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(1u, Destructs); } - EXPECT_EQ(0, Copies); - EXPECT_EQ(1, Moves); - EXPECT_EQ(2, Destructors); + EXPECT_EQ(1u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(2u, Destructs); +} - Copies = Moves = Destructors = 0; +TEST(STLExtrasTest, EnumerateLifetimeSemanticsRValue) { + // With an rvalue, it should not be destroyed until the end of the scope. + unsigned Moves{}, Copies{}, Destructs{}; + { + Range R(Moves, Copies, Destructs); + { + auto E = enumerate(std::move(R)); + (void)E; + // Doesn't compile. rvalue ranges must be moveable. + // auto E2 = enumerate(Range(Copies, Moves, Destructors)); + EXPECT_EQ(1u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(0u, Destructs); + } + EXPECT_EQ(1u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(1u, Destructs); + } + EXPECT_EQ(1u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(2u, Destructs); +} + +TEST(STLExtrasTest, EnumerateLifetimeSemanticsLValue) { // With an lvalue, it should not be destroyed even after the end of the scope. // lvalue ranges need be neither copyable nor moveable. - Range R(Copies, Moves, Destructors); + unsigned Moves{}, Copies{}, Destructs{}; { - auto Enumerator = enumerate(R); - (void)Enumerator; - EXPECT_EQ(0, Copies); - EXPECT_EQ(0, Moves); - EXPECT_EQ(0, Destructors); + Range R(Moves, Copies, Destructs); + { + auto E = enumerate(R); + (void)E; + EXPECT_EQ(0u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(0u, Destructs); + } + EXPECT_EQ(0u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(0u, Destructs); } - EXPECT_EQ(0, Copies); - EXPECT_EQ(0, Moves); - EXPECT_EQ(0, Destructors); + EXPECT_EQ(0u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(1u, Destructs); } TEST(STLExtrasTest, ApplyTuple) { @@ -747,4 +784,112 @@ (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) { + unsigned Moves{}, Copies{}, Destructs{}; + { + auto V = makeVisitor(Functor(Moves, Copies, Destructs)); + (void)V; + // FIXME: Can a C++14 compiler elide this temporary entirely? As of C++20 + // this will be required (i.e. no temporary may exist in this case.) + EXPECT_EQ(1u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(1u, Destructs); + } + EXPECT_EQ(1u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(2u, Destructs); +} + +TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsRValue) { + unsigned Moves{}, Copies{}, Destructs{}; + { + Functor F(Moves, Copies, Destructs); + { + auto V = makeVisitor(std::move(F)); + (void)V; + EXPECT_EQ(1u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(0u, Destructs); + } + EXPECT_EQ(1u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(1u, Destructs); + } + EXPECT_EQ(1u, Moves); + EXPECT_EQ(0u, Copies); + EXPECT_EQ(2u, Destructs); +} + +TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsLValue) { + unsigned Moves{}, Copies{}, Destructs{}; + { + Functor F(Moves, Copies, Destructs); + { + auto V = makeVisitor(F); + (void)V; + EXPECT_EQ(0u, Moves); + EXPECT_EQ(1u, Copies); + EXPECT_EQ(0u, Destructs); + } + EXPECT_EQ(0u, Moves); + EXPECT_EQ(1u, Copies); + EXPECT_EQ(1u, Destructs); + } + EXPECT_EQ(0u, Moves); + EXPECT_EQ(1u, Copies); + EXPECT_EQ(2u, Destructs); +} + } // namespace