Index: include/llvm/ADT/STLExtras.h =================================================================== --- include/llvm/ADT/STLExtras.h +++ include/llvm/ADT/STLExtras.h @@ -627,7 +627,7 @@ }; namespace detail { -template class enumerator_impl { +template class enumerator_impl { public: template struct result_pair { result_pair(std::size_t Index, X Value) : Index(Index), Value(Value) {} @@ -637,6 +637,9 @@ }; struct iterator { + using I = decltype(std::begin(std::declval())); + using V = decltype(*std::declval()); + iterator(I Iter, std::size_t Index) : Iter(Iter), Index(Index) {} result_pair operator*() const { @@ -657,24 +660,17 @@ std::size_t Index; }; - enumerator_impl(I Begin, I End) - : Begin(std::move(Begin)), End(std::move(End)) {} + explicit enumerator_impl(R &&Range) : Range(std::forward(Range)) {} - iterator begin() { return iterator(Begin, 0); } - iterator end() { return iterator(End, std::size_t(-1)); } + iterator begin() { return iterator(std::begin(Range), 0); } + iterator end() { return iterator(std::end(Range), std::size_t(-1)); } - iterator begin() const { return iterator(Begin, 0); } - iterator end() const { return iterator(End, std::size_t(-1)); } + iterator begin() const { return iterator(std::begin(Range), 0); } + iterator end() const { return iterator(std::end(Range), std::size_t(-1)); } private: - I Begin; - I End; + R Range; }; - -template -auto make_enumerator(I Begin, I End) -> enumerator_impl { - return enumerator_impl(std::move(Begin), std::move(End)); -} } /// Given an input range, returns a new range whose values are are pair (A,B) @@ -692,10 +688,8 @@ /// Item 2 - C /// Item 3 - D /// -template -auto enumerate(R &&Range) - -> decltype(detail::make_enumerator(std::begin(Range), std::end(Range))) { - return detail::make_enumerator(std::begin(Range), std::end(Range)); +template detail::enumerator_impl enumerate(R &&Range) { + return detail::enumerator_impl(std::forward(Range)); } } // End llvm namespace Index: unittests/ADT/STLExtrasTest.cpp =================================================================== --- unittests/ADT/STLExtrasTest.cpp +++ unittests/ADT/STLExtrasTest.cpp @@ -86,4 +86,97 @@ EXPECT_EQ('c', foo[1]); EXPECT_EQ('d', foo[2]); } + +TEST(STLExtrasTest, EnumerateRValueRef) { + using ResultPair = std::pair; + std::vector Results; + for (auto X : llvm::enumerate(std::vector{1, 2, 3})) { + Results.push_back(std::make_pair(X.Index, X.Value)); + } + + EXPECT_EQ(3, Results.size()); + EXPECT_EQ(ResultPair(0, 1), Results[0]); + EXPECT_EQ(ResultPair(1, 2), Results[1]); + EXPECT_EQ(ResultPair(2, 3), Results[2]); +} + +TEST(STLExtrasTest, EnumerateNested) { + std::vector foo = {'a', 'b', 'c'}; + + using InternalPairType = std::pair; + using ExternalPairType = std::pair; + std::vector Results; + + for (auto X : enumerate(enumerate(foo))) { + Results.push_back( + std::make_pair(X.Index, std::make_pair(X.Value.Index, X.Value.Value))); + } + + EXPECT_EQ(ExternalPairType(0u, InternalPairType(0u, 'a')), Results[0]); + EXPECT_EQ(ExternalPairType(1u, InternalPairType(1u, 'b')), Results[1]); + EXPECT_EQ(ExternalPairType(2u, InternalPairType(2u, 'c')), Results[2]); +} + +template struct CanMove {}; +template <> struct CanMove { + CanMove(CanMove &&) = delete; + + CanMove() = default; + CanMove(const CanMove &) = default; +}; + +template struct CanCopy {}; +template <> struct CanCopy { + CanCopy(const CanCopy &) = delete; + + CanCopy() = default; + 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; } + + int &C; + int &M; + int &D; + + void *begin() { return nullptr; } + void *end() { return nullptr; } +}; + +TEST(STLExtrasTest, EnumerateLifetimeSemantics) { + // With an rvalue, it should not be destroyed until the end of the scope. + int Copies = 0; + int Moves = 0; + int Destructors = 0; + { + auto E1 = enumerate(Range(Copies, Moves, Destructors)); + // 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); + } + EXPECT_EQ(0, Copies); + EXPECT_EQ(1, Moves); + EXPECT_EQ(2, Destructors); + + Copies = Moves = Destructors = 0; + // 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); + { + auto Enumerator = enumerate(R); + EXPECT_EQ(0, Copies); + EXPECT_EQ(0, Moves); + EXPECT_EQ(0, Destructors); + } + EXPECT_EQ(0, Copies); + EXPECT_EQ(0, Moves); + EXPECT_EQ(0, Destructors); +} }